From 9bc08af88042b14b4f24650a06f9fc8bf6bbc33b Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 1 Feb 2021 19:09:52 +0000 Subject: [PATCH] - Added ability to the PDCP LTE entity to send PDCP Status report. - Added handling of PDCP status report to the PDCP LTE entity. - Added test to the generation and handling of PDCP status report. --- .../srslte/interfaces/pdcp_interface_types.h | 12 ++ lib/include/srslte/upper/pdcp_entity_lte.h | 5 + lib/src/upper/pdcp_entity_lte.cc | 146 ++++++++++++- lib/test/upper/CMakeLists.txt | 4 + lib/test/upper/pdcp_lte_test_discard_sdu.cc | 2 + lib/test/upper/pdcp_lte_test_status_report.cc | 197 ++++++++++++++++++ 6 files changed, 362 insertions(+), 4 deletions(-) create mode 100644 lib/test/upper/pdcp_lte_test_status_report.cc diff --git a/lib/include/srslte/interfaces/pdcp_interface_types.h b/lib/include/srslte/interfaces/pdcp_interface_types.h index 71b594db8..85b558817 100644 --- a/lib/include/srslte/interfaces/pdcp_interface_types.h +++ b/lib/include/srslte/interfaces/pdcp_interface_types.h @@ -42,6 +42,18 @@ enum pdcp_dc_field_t { }; static const char* pdcp_dc_field_text[PDCP_DC_FIELD_N_ITEMS] = {"Control PDU", "Data PDU"}; +enum pdcp_pdu_type_t { + PDCP_PDU_TYPE_STATUS_REPORT = 0, + PDCP_PDU_TYPE_INTERSPERSED_ROHC_FEEDBACK_PACKET, + PDCP_PDU_TYPE_LWA_STATUS_REPORT, + PDCP_PDU_TYPE_LWA_END_MARKER_PACKET, + PDCP_PDU_TYPE_N_ITEMS, +}; +static const char* pdcp_pdu_type_text[PDCP_PDU_TYPE_N_ITEMS] = {"PDCP Report PDU", + "Interspersed ROCH Feedback Packet", + "LWA Status Report", + "LWA End-marker Packet"}; + // Taken from PDCP-Config (TS 38.331 version 15.2.1) enum class pdcp_t_reordering_t { ms0 = 0, diff --git a/lib/include/srslte/upper/pdcp_entity_lte.h b/lib/include/srslte/upper/pdcp_entity_lte.h index 660a7554c..eb45fb448 100644 --- a/lib/include/srslte/upper/pdcp_entity_lte.h +++ b/lib/include/srslte/upper/pdcp_entity_lte.h @@ -64,6 +64,10 @@ public: // Getter for unacknowledged PDUs. Used for handover std::map get_buffered_pdus() override; + // Status report helper(s) + bool send_status_report(); + void handle_status_report_pdu(srslte::unique_byte_buffer_t pdu); + // Internal state getters/setters void get_bearer_state(pdcp_lte_state_t* state) override; void set_bearer_state(const pdcp_lte_state_t& state) override; @@ -89,6 +93,7 @@ private: // TX Queue std::map undelivered_sdus_queue; + void handle_control_pdu(srslte::unique_byte_buffer_t pdu); void handle_srb_pdu(srslte::unique_byte_buffer_t pdu); void handle_um_drb_pdu(srslte::unique_byte_buffer_t pdu); void handle_am_drb_pdu(srslte::unique_byte_buffer_t pdu); diff --git a/lib/src/upper/pdcp_entity_lte.cc b/lib/src/upper/pdcp_entity_lte.cc index 3ce93ebae..2f13a860e 100644 --- a/lib/src/upper/pdcp_entity_lte.cc +++ b/lib/src/upper/pdcp_entity_lte.cc @@ -11,7 +11,9 @@ */ #include "srslte/upper/pdcp_entity_lte.h" +#include "srslte/common/int_helpers.h" #include "srslte/common/security.h" +#include namespace srslte { @@ -181,9 +183,10 @@ void pdcp_entity_lte::write_sdu(unique_byte_buffer_t sdu, int upper_sn) // RLC interface void pdcp_entity_lte::write_pdu(unique_byte_buffer_t pdu) { - // drop control PDUs + // Handle control PDUs if (is_drb() && is_control_pdu(pdu)) { - logger.info("Dropping PDCP control PDU"); + logger.info("Handling PDCP control PDU"); + handle_control_pdu(std::move(pdu)); return; } @@ -223,8 +226,22 @@ void pdcp_entity_lte::write_pdu(unique_byte_buffer_t pdu) } } +// Handle control PDU +void pdcp_entity_lte::handle_control_pdu(unique_byte_buffer_t pdu) +{ + switch ((pdu->msg[0] >> 4) & 0x03) { + case PDCP_PDU_TYPE_STATUS_REPORT: + handle_status_report_pdu(std::move(pdu)); + break; + default: + logger.warning("Unhandled control PDU"); + return; + } + return; +} + /**************************************************************************** - * Rx data/control handler functions + * Rx data handler functions * Ref: 3GPP TS 36.323 v10.1.0 Section 5.1.2 ***************************************************************************/ // SRBs (5.1.2.2) @@ -333,7 +350,7 @@ void pdcp_entity_lte::handle_am_drb_pdu(srslte::unique_byte_buffer_t pdu) sn_diff_last_submit, last_submit_diff_sn, reordering_window); - return; // Discard + return; } if ((int32_t)(st.next_pdcp_rx_sn - sn) > (int32_t)reordering_window) { @@ -368,6 +385,127 @@ void pdcp_entity_lte::handle_am_drb_pdu(srslte::unique_byte_buffer_t pdu) gw->write_pdu(lcid, std::move(pdu)); } +/**************************************************************************** + * Control handler functions (Status Report) + * Ref: 3GPP TS 36.323 v10.1.0 Section 5.1.3 + ***************************************************************************/ + +// Section 5.3.1 transmit operation +bool pdcp_entity_lte::send_status_report() +{ + // Check wether RCL AM is being used. + if (rlc->rb_is_um(lcid)) { + logger.error("Trying to send PDCP Status Report and RLC is not AM"); + return false; + } + + // Get First Missing Segment (FMS) + uint32_t fms = 0; + if (undelivered_sdus_queue.empty()) { + fms = st.next_pdcp_tx_sn; + } else { + fms = undelivered_sdus_queue.begin()->first; + } + + logger.debug("PDCP Status report: FMS=%d", fms); + + // Allocate Status Report PDU + unique_byte_buffer_t pdu = make_byte_buffer(); + + // Set control bit and type of PDU + pdu->msg[0] = ((uint8_t)PDCP_DC_FIELD_CONTROL_PDU << 7) | ((uint8_t)PDCP_PDU_TYPE_STATUS_REPORT << 4); + + // Set FMS + switch (cfg.sn_len) { + case PDCP_SN_LEN_12: + pdu->msg[0] = pdu->msg[0] | (0x0F & (fms >> 8)); + pdu->msg[1] = 0xFF & fms; + pdu->N_bytes = 2; + break; + case PDCP_SN_LEN_18: + pdu->msg[0] = pdu->msg[0] | (0x03 & (fms >> 16)); + pdu->msg[1] = 0xFF & (fms >> 8); + pdu->msg[2] = 0xFF & fms; + pdu->N_bytes = 3; + break; + default: + logger.error("Unsupported SN length for Status Report."); + return false; + } + + // Add bitmap of missing PDUs, if necessary + if (not undelivered_sdus_queue.empty()) { + uint32_t byte_offset = 0; + for (auto it = undelivered_sdus_queue.begin(); it != undelivered_sdus_queue.end(); it++) { + uint32_t offset = it->first - fms; + uint32_t bit_offset = offset % 8; + byte_offset = offset / 8; + pdu->msg[pdu->N_bytes + byte_offset] |= 1 << (7 - bit_offset); + } + pdu->N_bytes += (byte_offset + 1); + } + + // Write PDU to RLC + rlc->write_sdu(lcid, std::move(pdu)); + + return true; +} + +// Section 5.3.2 receive operation +void pdcp_entity_lte::handle_status_report_pdu(unique_byte_buffer_t pdu) +{ + uint32_t fms; + std::vector acked_sns; + uint32_t bitmap_offset; + + // Get FMS + switch (cfg.sn_len) { + case PDCP_SN_LEN_12: { + uint16_t tmp16; + uint8_to_uint16(pdu->msg, &tmp16); + fms = tmp16 & 0x0FFF; + bitmap_offset = 2; + break; + } + case PDCP_SN_LEN_18: { + uint8_to_uint24(pdu->msg, &fms); + fms = fms & 0x3FFF; + bitmap_offset = 3; + break; + } + default: + logger.error("Unsupported SN length for Status Report."); + return; + } + + // Remove all SDUs with SN smaller than FMS + for (auto it = undelivered_sdus_queue.begin(); it != undelivered_sdus_queue.end();) { + if (it->first < fms) { + discard_timers_map.erase(it->first); + it = undelivered_sdus_queue.erase(it); + } else { + ++it; + } + } + + // Get acked SNs from bitmap + for (uint32_t i = 0; (i + bitmap_offset) < pdu->N_bytes; i++) { + std::bitset<8> bset{pdu->msg[bitmap_offset + i]}; + for (uint8_t j = 0; j < 8; j++) { + if (bset[8 - j]) { + uint32_t acked_sn = fms + i * 8 + j; + acked_sns.push_back(acked_sn); + } + } + } + + // Discard ACK'ed SDUs + for (uint32_t sn : acked_sns) { + logger.debug("Status report ACKed SN=%d.", sn); + undelivered_sdus_queue.erase(sn); + discard_timers_map.erase(sn); + } +} /**************************************************************************** * TX PDUs Queue Helper ***************************************************************************/ diff --git a/lib/test/upper/CMakeLists.txt b/lib/test/upper/CMakeLists.txt index cd0ac0034..c16c76f7b 100644 --- a/lib/test/upper/CMakeLists.txt +++ b/lib/test/upper/CMakeLists.txt @@ -77,6 +77,10 @@ add_executable(pdcp_lte_test_discard_sdu pdcp_lte_test_discard_sdu.cc) target_link_libraries(pdcp_lte_test_discard_sdu srslte_upper srslte_common) add_test(pdcp_lte_test_discard_sdu pdcp_lte_test_discard_sdu) +add_executable(pdcp_lte_test_status_report pdcp_lte_test_status_report.cc) +target_link_libraries(pdcp_lte_test_status_report srslte_upper srslte_common) +add_test(pdcp_lte_test_status_report pdcp_lte_test_status_report) + ######################################################################## # Option to run command after build (useful for remote builds) ######################################################################## diff --git a/lib/test/upper/pdcp_lte_test_discard_sdu.cc b/lib/test/upper/pdcp_lte_test_discard_sdu.cc index 0a847514a..57b00b722 100644 --- a/lib/test/upper/pdcp_lte_test_discard_sdu.cc +++ b/lib/test/upper/pdcp_lte_test_discard_sdu.cc @@ -139,6 +139,8 @@ int run_all_tests() int main() { + srslog::init(); + if (run_all_tests() != SRSLTE_SUCCESS) { fprintf(stderr, "pdcp_lte_tests() failed\n"); return SRSLTE_ERROR; diff --git a/lib/test/upper/pdcp_lte_test_status_report.cc b/lib/test/upper/pdcp_lte_test_status_report.cc new file mode 100644 index 000000000..f22c7ade4 --- /dev/null +++ b/lib/test/upper/pdcp_lte_test_status_report.cc @@ -0,0 +1,197 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#include "pdcp_lte_test.h" +#include + +/* + * Test correct transmission of FMS and Bitmap in status report + */ +int test_tx_status_report(const srslte::pdcp_lte_state_t& init_state, srslog::basic_logger& logger) +{ + srslte::pdcp_config_t cfg = {1, + srslte::PDCP_RB_IS_DRB, + srslte::SECURITY_DIRECTION_UPLINK, + srslte::SECURITY_DIRECTION_DOWNLINK, + srslte::PDCP_SN_LEN_12, + srslte::pdcp_t_reordering_t::ms500, + srslte::pdcp_discard_timer_t::ms500}; + + pdcp_lte_test_helper pdcp_hlp(cfg, sec_cfg, logger); + srslte::pdcp_entity_lte* pdcp = &pdcp_hlp.pdcp; + rlc_dummy* rlc = &pdcp_hlp.rlc; + srsue::stack_test_dummy* stack = &pdcp_hlp.stack; + + pdcp_hlp.set_pdcp_initial_state(init_state); + srslte::unique_byte_buffer_t out_pdu = srslte::make_byte_buffer(); + + // Write 256 SDUs and notify imediatly -> FMS 0001 0000 0001 + for (uint32_t i = 0; i < 257; i++) { + srslte::unique_byte_buffer_t sdu = srslte::make_byte_buffer(); + sdu->append_bytes(sdu1, sizeof(sdu1)); + pdcp->write_sdu(std::move(sdu)); + pdcp->notify_delivery({i}); + } + + // Check undelivered SDUs queue size + TESTASSERT(pdcp->nof_discard_timers() == 0); // 0 timers should be running + + // Generate the status report + pdcp->send_status_report(); + rlc->get_last_sdu(out_pdu); + logger.debug(out_pdu->msg, out_pdu->N_bytes, "Status PDU:"); + + // Check status PDU + /* + * | D/C | TYPE | FMS | -> | 0 | 0 | 0001 | + * | FMS | -> | 00000001 | + */ + TESTASSERT(out_pdu->msg[0] == 1); + TESTASSERT(out_pdu->msg[1] == 1); + TESTASSERT(out_pdu->N_bytes == 2); + + // Write another 16 SDUs but don't notify SN=257, SN=258, SN=271 and SN=272 + for (uint32_t i = 257; i < 273; i++) { + srslte::unique_byte_buffer_t sdu = srslte::make_byte_buffer(); + sdu->append_bytes(sdu1, sizeof(sdu1)); + pdcp->write_sdu(std::move(sdu)); + if (i != 257 && i != 258 && i != 271 && i != 272) { + pdcp->notify_delivery({i}); + } + } + + // Check undelivered SDUs queue size + TESTASSERT(pdcp->nof_discard_timers() == 4); + + // Generate the status report + pdcp->send_status_report(); + rlc->get_last_sdu(out_pdu); + logger.debug(out_pdu->msg, out_pdu->N_bytes, "Status PDU:"); + + // Check status PDU + /* + * | D/C | TYPE | FMS | -> | 0 | 0 | 0001 | + * | FMS | -> | 00000001 | + * | bitmap | -> | 11000000 | + * | bitmap (cont.) | -> | 00000011 | + */ + TESTASSERT(out_pdu->N_bytes == 4); + TESTASSERT(out_pdu->msg[0] == 0b00000001); + TESTASSERT(out_pdu->msg[1] == 0b00000001); + TESTASSERT(out_pdu->msg[2] == 0b11000000); + TESTASSERT(out_pdu->msg[3] == 0b00000011); + return 0; +} + +/* + * Test reception of status report + */ +int test_rx_status_report(const srslte::pdcp_lte_state_t& init_state, srslog::basic_logger& logger) +{ + srslte::pdcp_config_t cfg = {1, + srslte::PDCP_RB_IS_DRB, + srslte::SECURITY_DIRECTION_UPLINK, + srslte::SECURITY_DIRECTION_DOWNLINK, + srslte::PDCP_SN_LEN_12, + srslte::pdcp_t_reordering_t::ms500, + srslte::pdcp_discard_timer_t::ms500}; + + pdcp_lte_test_helper pdcp_hlp(cfg, sec_cfg, logger); + srslte::pdcp_entity_lte* pdcp = &pdcp_hlp.pdcp; + rlc_dummy* rlc = &pdcp_hlp.rlc; + srsue::stack_test_dummy* stack = &pdcp_hlp.stack; + + pdcp_hlp.set_pdcp_initial_state(init_state); + srslte::unique_byte_buffer_t status_pdu = srslte::make_byte_buffer(); + srslte::unique_byte_buffer_t out_pdu = srslte::make_byte_buffer(); + + // Write 256 SDUs and notify imediatly -> FMS 0001 0000 0001 + for (uint32_t i = 0; i < 257; i++) { + srslte::unique_byte_buffer_t sdu = srslte::make_byte_buffer(); + sdu->append_bytes(sdu1, sizeof(sdu1)); + pdcp->write_sdu(std::move(sdu)); + pdcp->notify_delivery({i}); + } + + // Write another 16 SDUs but don't notify SN=257, SN=258, SN=271 and SN=272 + for (uint32_t i = 257; i < 273; i++) { + srslte::unique_byte_buffer_t sdu = srslte::make_byte_buffer(); + sdu->append_bytes(sdu1, sizeof(sdu1)); + pdcp->write_sdu(std::move(sdu)); + if (i != 257 && i != 258 && i != 271 && i != 272) { + pdcp->notify_delivery({i}); + } + } + + // Check undelivered SDUs queue size + TESTASSERT(pdcp->nof_discard_timers() == 4); + + // Set status PDU + // Notify up to 270 wit FMS + // Notify 272 with bitmap + /* + * | D/C | TYPE | FMS | -> | 0 | 0 | 0001 | + * | FMS | -> | 00001110 | + * | bitmap | -> | 01000000 | + */ + status_pdu->N_bytes = 3; + status_pdu->msg[0] = 0b00000001; + status_pdu->msg[1] = 0b00001110; + status_pdu->msg[2] = 0b01000000; + + pdcp->write_pdu(std::move(status_pdu)); + + TESTASSERT(pdcp->nof_discard_timers() == 1); + + // Check if the SDUs were correctly discarded with the status report + pdcp->send_status_report(); + rlc->get_last_sdu(out_pdu); + logger.debug(out_pdu->msg, out_pdu->N_bytes, "Status PDU:"); + + // Check status PDU, only 271 is missing => FMS = 271 + /* + * | D/C | TYPE | FMS | -> | 0 | 0 | 0001 | + * | FMS | -> | 00001111 | + */ + TESTASSERT(out_pdu->N_bytes == 3); + TESTASSERT(out_pdu->msg[0] == 0b00000001); + TESTASSERT(out_pdu->msg[1] == 0b00001111); + return 0; +} + +// Setup all tests +int run_all_tests() +{ + // Setup log + srslog::basic_logger& logger = srslog::fetch_basic_logger("PDCP LTE Test Status Report", false); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(128); + + // This is the normal initial state. All state variables are set to zero + srslte::pdcp_lte_state_t normal_init_state = {}; + + TESTASSERT(test_tx_status_report(normal_init_state, logger) == 0); + + TESTASSERT(test_rx_status_report(normal_init_state, logger) == 0); + return 0; +} + +int main() +{ + srslog::init(); + + if (run_all_tests() != SRSLTE_SUCCESS) { + fprintf(stderr, "pdcp_lte_tests() failed\n"); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +}