diff --git a/lib/include/srsran/rlc/rlc_am_lte.h b/lib/include/srsran/rlc/rlc_am_lte.h index a669b0cd7..30263a1a7 100644 --- a/lib/include/srsran/rlc/rlc_am_lte.h +++ b/lib/include/srsran/rlc/rlc_am_lte.h @@ -23,6 +23,7 @@ #include "srsran/interfaces/pdcp_interface_types.h" #include "srsran/rlc/rlc_am_base.h" #include "srsran/rlc/rlc_am_data_structs.h" +#include "srsran/rlc/rlc_am_lte_packing.h" #include "srsran/rlc/rlc_common.h" #include "srsran/support/srsran_assert.h" #include "srsran/upper/byte_buffer_queue.h" @@ -34,63 +35,6 @@ namespace srsran { #undef RLC_AM_BUFFER_DEBUG -struct rlc_amd_rx_pdu { - rlc_amd_pdu_header_t header; - unique_byte_buffer_t buf; - uint32_t rlc_sn; - - rlc_amd_rx_pdu() = default; - explicit rlc_amd_rx_pdu(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {} -}; - -struct rlc_amd_rx_pdu_segments_t { - std::list segments; -}; - -/// Class that contains the parameters and state (e.g. segments) of a RLC PDU -class rlc_amd_tx_pdu -{ - using list_type = intrusive_forward_list; - const static uint32_t invalid_rlc_sn = std::numeric_limits::max(); - - list_type list; - -public: - using iterator = typename list_type::iterator; - using const_iterator = typename list_type::const_iterator; - - const uint32_t rlc_sn = invalid_rlc_sn; - uint32_t retx_count = 0; - rlc_amd_pdu_header_t header; - unique_byte_buffer_t buf; - - explicit rlc_amd_tx_pdu(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {} - rlc_amd_tx_pdu(const rlc_amd_tx_pdu&) = delete; - rlc_amd_tx_pdu(rlc_amd_tx_pdu&& other) noexcept = default; - rlc_amd_tx_pdu& operator=(const rlc_amd_tx_pdu& other) = delete; - rlc_amd_tx_pdu& operator=(rlc_amd_tx_pdu&& other) = delete; - ~rlc_amd_tx_pdu(); - - // Segment List Interface - void add_segment(rlc_am_pdu_segment& segment) { list.push_front(&segment); } - const_iterator begin() const { return list.begin(); } - const_iterator end() const { return list.end(); } - iterator begin() { return list.begin(); } - iterator end() { return list.end(); } -}; - -struct rlc_amd_retx_t { - uint32_t sn; - bool is_segment; - uint32_t so_start; - uint32_t so_end; -}; - -struct rlc_sn_info_t { - uint32_t sn; - bool is_acked; -}; - class pdu_retx_queue { public: @@ -383,31 +327,6 @@ private: rlc_bearer_metrics_t metrics = {}; }; -/**************************************************************************** - * Header pack/unpack helper functions - * Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1 - ***************************************************************************/ -void rlc_am_read_data_pdu_header(byte_buffer_t* pdu, rlc_amd_pdu_header_t* header); -void rlc_am_read_data_pdu_header(uint8_t** payload, uint32_t* nof_bytes, rlc_amd_pdu_header_t* header); -void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, byte_buffer_t* pdu); -void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, uint8_t** payload); -void rlc_am_read_status_pdu(byte_buffer_t* pdu, rlc_status_pdu_t* status); -void rlc_am_read_status_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_status_pdu_t* status); -void rlc_am_write_status_pdu(rlc_status_pdu_t* status, byte_buffer_t* pdu); -int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload); - -uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header); -uint32_t rlc_am_packed_length(rlc_status_pdu_t* status); -uint32_t rlc_am_packed_length(rlc_amd_retx_t retx); -bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status, uint32_t rx_win_min = 0); -bool rlc_am_is_pdu_segment(uint8_t* payload); -std::string rlc_am_undelivered_sdu_info_to_string(const std::map& info_queue); -void log_rlc_amd_pdu_header_to_string(srslog::log_channel& log_ch, const rlc_amd_pdu_header_t& header); -bool rlc_am_start_aligned(const uint8_t fi); -bool rlc_am_end_aligned(const uint8_t fi); -bool rlc_am_is_unaligned(const uint8_t fi); -bool rlc_am_not_start_aligned(const uint8_t fi); - } // namespace srsran #endif // SRSRAN_RLC_AM_LTE_H diff --git a/lib/include/srsran/rlc/rlc_am_lte_packing.h b/lib/include/srsran/rlc/rlc_am_lte_packing.h new file mode 100644 index 000000000..f4d2d5ea5 --- /dev/null +++ b/lib/include/srsran/rlc/rlc_am_lte_packing.h @@ -0,0 +1,135 @@ +/** + * + * \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. + * + */ + +#ifndef SRSRAN_RLC_AM_LTE_PACKING_H +#define SRSRAN_RLC_AM_LTE_PACKING_H + +#include "srsran/common/string_helpers.h" +#include "srsran/rlc/rlc_am_base.h" +#include "srsran/rlc/rlc_am_data_structs.h" // required for rlc_am_pdu_segment +#include + +namespace srsran { + +/// Class that contains the parameters and state (e.g. segments) of a RLC PDU +class rlc_amd_tx_pdu +{ + using list_type = intrusive_forward_list; + const static uint32_t invalid_rlc_sn = std::numeric_limits::max(); + + list_type list; + +public: + using iterator = typename list_type::iterator; + using const_iterator = typename list_type::const_iterator; + + const uint32_t rlc_sn = invalid_rlc_sn; + uint32_t retx_count = 0; + rlc_amd_pdu_header_t header; + unique_byte_buffer_t buf; + + explicit rlc_amd_tx_pdu(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {} + rlc_amd_tx_pdu(const rlc_amd_tx_pdu&) = delete; + rlc_amd_tx_pdu(rlc_amd_tx_pdu&& other) noexcept = default; + rlc_amd_tx_pdu& operator=(const rlc_amd_tx_pdu& other) = delete; + rlc_amd_tx_pdu& operator=(rlc_amd_tx_pdu&& other) = delete; + ~rlc_amd_tx_pdu(); + + // Segment List Interface + void add_segment(rlc_am_pdu_segment& segment) { list.push_front(&segment); } + const_iterator begin() const { return list.begin(); } + const_iterator end() const { return list.end(); } + iterator begin() { return list.begin(); } + iterator end() { return list.end(); } +}; + +struct rlc_amd_retx_t { + uint32_t sn; + bool is_segment; + uint32_t so_start; + uint32_t so_end; +}; + +struct rlc_sn_info_t { + uint32_t sn; + bool is_acked; +}; + +struct rlc_amd_rx_pdu { + rlc_amd_pdu_header_t header; + unique_byte_buffer_t buf; + uint32_t rlc_sn; + + rlc_amd_rx_pdu() = default; + explicit rlc_amd_rx_pdu(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {} +}; + +struct rlc_amd_rx_pdu_segments_t { + std::list segments; +}; + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1 + ***************************************************************************/ +void rlc_am_read_data_pdu_header(byte_buffer_t* pdu, rlc_amd_pdu_header_t* header); +void rlc_am_read_data_pdu_header(uint8_t** payload, uint32_t* nof_bytes, rlc_amd_pdu_header_t* header); +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, byte_buffer_t* pdu); +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, uint8_t** payload); +void rlc_am_read_status_pdu(byte_buffer_t* pdu, rlc_status_pdu_t* status); +void rlc_am_read_status_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_status_pdu_t* status); +void rlc_am_write_status_pdu(rlc_status_pdu_t* status, byte_buffer_t* pdu); +int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload); + +uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header); +uint32_t rlc_am_packed_length(rlc_status_pdu_t* status); +uint32_t rlc_am_packed_length(rlc_amd_retx_t retx); +bool rlc_am_is_pdu_segment(uint8_t* payload); +bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status, uint32_t rx_win_min = 0); +std::string rlc_am_undelivered_sdu_info_to_string(const std::map& info_queue); +void log_rlc_amd_pdu_header_to_string(srslog::log_channel& log_ch, const rlc_amd_pdu_header_t& header); +bool rlc_am_start_aligned(const uint8_t fi); +bool rlc_am_end_aligned(const uint8_t fi); +bool rlc_am_is_unaligned(const uint8_t fi); +bool rlc_am_not_start_aligned(const uint8_t fi); + +/** + * Logs Status PDU into provided log channel, using fmt_str as format string + */ +template +void log_rlc_am_status_pdu_to_string(srslog::log_channel& log_ch, + const char* fmt_str, + rlc_status_pdu_t* status, + Args&&... args) +{ + if (not log_ch.enabled()) { + return; + } + fmt::memory_buffer buffer; + fmt::format_to(buffer, "ACK_SN = {}, N_nack = {}", status->ack_sn, status->N_nack); + if (status->N_nack > 0) { + fmt::format_to(buffer, ", NACK_SN = "); + for (uint32_t i = 0; i < status->N_nack; ++i) { + if (status->nacks[i].has_so) { + fmt::format_to( + buffer, "[{} {}:{}]", status->nacks[i].nack_sn, status->nacks[i].so_start, status->nacks[i].so_end); + } else { + fmt::format_to(buffer, "[{}]", status->nacks[i].nack_sn); + } + } + } + log_ch(fmt_str, std::forward(args)..., to_c_str(buffer)); +} + +} // namespace srsran + +#endif // SRSRAN_RLC_AM_NR_H diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index c1ac15bf1..cd03e78cb 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -15,6 +15,7 @@ #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" +#include "srsran/common/timers.h" #include "srsran/rlc/rlc_am_base.h" #include "srsran/upper/byte_buffer_queue.h" #include @@ -24,41 +25,7 @@ namespace srsran { -typedef struct { - rlc_am_nr_pdu_header_t header; - unique_byte_buffer_t buf; -} rlc_amd_pdu_nr_t; - -///< add class here - -/**************************************************************************** - * Header pack/unpack helper functions for NR - * Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.3 - ***************************************************************************/ -uint32_t rlc_am_nr_read_data_pdu_header(const byte_buffer_t* pdu, - const rlc_am_nr_sn_size_t sn_size, - rlc_am_nr_pdu_header_t* header); - -uint32_t rlc_am_nr_read_data_pdu_header(const uint8_t* payload, - const uint32_t nof_bytes, - const rlc_am_nr_sn_size_t sn_size, - rlc_am_nr_pdu_header_t* header); - -uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, byte_buffer_t* pdu); - -uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header); - -uint32_t -rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status); - -uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, - const uint32_t nof_bytes, - const rlc_am_nr_sn_size_t sn_size, - rlc_am_nr_status_pdu_t* status); - -int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, - const rlc_am_nr_sn_size_t sn_size, - byte_buffer_t* pdu); +// TODO } // namespace srsran diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h new file mode 100644 index 000000000..f31aa2c42 --- /dev/null +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -0,0 +1,75 @@ +/** + * + * \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. + * + */ + +#ifndef SRSRAN_RLC_AM_NR_PACKING_H +#define SRSRAN_RLC_AM_NR_PACKING_H + +#include "srsran/rlc/rlc_am_base.h" + +namespace srsran { + +///< AM NR PDU header +typedef struct { + rlc_dc_field_t dc; ///< Data/Control (D/C) field + uint8_t p; ///< Polling bit + rlc_nr_si_field_t si; ///< Segmentation info + rlc_am_nr_sn_size_t sn_size; ///< Sequence number size (12 or 18 bits) + uint32_t sn; ///< Sequence number + uint16_t so; ///< Sequence offset +} rlc_am_nr_pdu_header_t; + +struct rlc_amd_pdu_nr_t { + rlc_am_nr_pdu_header_t header; + unique_byte_buffer_t buf; +}; + +///< AM NR Status PDU header (perhaps merge with LTE version) +typedef struct { + rlc_am_nr_control_pdu_type_t cpt; + uint32_t ack_sn; ///< SN of the next not received RLC Data PDU + uint16_t N_nack; ///< number of NACKs + uint8_t nack_range; ///< number of consecutively lost RLC SDUs starting from and including NACK_SN + rlc_status_nack_t nacks[RLC_AM_WINDOW_SIZE]; +} rlc_am_nr_status_pdu_t; + +/**************************************************************************** + * Header pack/unpack helper functions for NR + * Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.3 + ***************************************************************************/ +uint32_t rlc_am_nr_read_data_pdu_header(const byte_buffer_t* pdu, + const rlc_am_nr_sn_size_t sn_size, + rlc_am_nr_pdu_header_t* header); + +uint32_t rlc_am_nr_read_data_pdu_header(const uint8_t* payload, + const uint32_t nof_bytes, + const rlc_am_nr_sn_size_t sn_size, + rlc_am_nr_pdu_header_t* header); + +uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, byte_buffer_t* pdu); + +uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header); + +uint32_t +rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status); + +uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, + const uint32_t nof_bytes, + const rlc_am_nr_sn_size_t sn_size, + rlc_am_nr_status_pdu_t* status); + +int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, + const rlc_am_nr_sn_size_t sn_size, + byte_buffer_t* pdu); + +} // namespace srsran + +#endif // SRSRAN_RLC_AM_NR_H diff --git a/lib/include/srsran/rlc/rlc_common.h b/lib/include/srsran/rlc/rlc_common.h index 561fde19a..bb5848e2c 100644 --- a/lib/include/srsran/rlc/rlc_common.h +++ b/lib/include/srsran/rlc/rlc_common.h @@ -176,27 +176,6 @@ struct rlc_status_pdu_t { } }; -/** RLC AM NR structs */ - -///< AM NR PDU header -typedef struct { - rlc_dc_field_t dc; ///< Data/Control (D/C) field - uint8_t p; ///< Polling bit - rlc_nr_si_field_t si; ///< Segmentation info - rlc_am_nr_sn_size_t sn_size; ///< Sequence number size (12 or 18 bits) - uint32_t sn; ///< Sequence number - uint16_t so; ///< Sequence offset -} rlc_am_nr_pdu_header_t; - -///< AM NR Status PDU header (perhaps merge with LTE version) -typedef struct { - rlc_am_nr_control_pdu_type_t cpt; - uint32_t ack_sn; ///< SN of the next not received RLC Data PDU - uint16_t N_nack; ///< number of NACKs - uint8_t nack_range; ///< number of consecutively lost RLC SDUs starting from and including NACK_SN - rlc_status_nack_t nacks[RLC_AM_WINDOW_SIZE]; -} rlc_am_nr_status_pdu_t; - typedef std::function bsr_callback_t; /**************************************************************************** diff --git a/lib/src/rlc/CMakeLists.txt b/lib/src/rlc/CMakeLists.txt index ed6e085fe..89b44fba6 100644 --- a/lib/src/rlc/CMakeLists.txt +++ b/lib/src/rlc/CMakeLists.txt @@ -10,10 +10,12 @@ set(SOURCES rlc.cc rlc_tm.cc rlc_um_base.cc rlc_um_lte.cc + rlc_um_nr.cc rlc_am_base.cc rlc_am_lte.cc - rlc_um_nr.cc rlc_am_nr.cc + rlc_am_lte_packing.cc + rlc_am_nr_packing.cc bearer_mem_pool.cc) add_library(srsran_rlc STATIC ${SOURCES}) diff --git a/lib/src/rlc/rlc_am_lte.cc b/lib/src/rlc/rlc_am_lte.cc index 5585d8d2d..add79c851 100644 --- a/lib/src/rlc/rlc_am_lte.cc +++ b/lib/src/rlc/rlc_am_lte.cc @@ -11,9 +11,9 @@ */ #include "srsran/rlc/rlc_am_lte.h" -#include "srsran/common/string_helpers.h" #include "srsran/interfaces/ue_pdcp_interfaces.h" #include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/rlc/rlc_am_lte_packing.h" #include "srsran/srslog/event_trace.h" #include @@ -25,38 +25,6 @@ namespace srsran { -/******************************* - * Helper methods - ******************************/ - -/** - * Logs Status PDU into provided log channel, using fmt_str as format string - */ -template -void log_rlc_am_status_pdu_to_string(srslog::log_channel& log_ch, - const char* fmt_str, - rlc_status_pdu_t* status, - Args&&... args) -{ - if (not log_ch.enabled()) { - return; - } - fmt::memory_buffer buffer; - fmt::format_to(buffer, "ACK_SN = {}, N_nack = {}", status->ack_sn, status->N_nack); - if (status->N_nack > 0) { - fmt::format_to(buffer, ", NACK_SN = "); - for (uint32_t i = 0; i < status->N_nack; ++i) { - if (status->nacks[i].has_so) { - fmt::format_to( - buffer, "[{} {}:{}]", status->nacks[i].nack_sn, status->nacks[i].so_start, status->nacks[i].so_end); - } else { - fmt::format_to(buffer, "[{}]", status->nacks[i].nack_sn); - } - } - } - log_ch(fmt_str, std::forward(args)..., to_c_str(buffer)); -} - /******************************* * RLC AM Segments ******************************/ @@ -2259,325 +2227,4 @@ void rlc_am_lte::rlc_am_lte_rx::debug_state() logger.debug("%s vr_r = %d, vr_mr = %d, vr_x = %d, vr_ms = %d, vr_h = %d", RB_NAME, vr_r, vr_mr, vr_x, vr_ms, vr_h); } -/**************************************************************************** - * Header pack/unpack helper functions - * Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1 - ***************************************************************************/ - -// Read header from pdu struct, don't strip header -void rlc_am_read_data_pdu_header(byte_buffer_t* pdu, rlc_amd_pdu_header_t* header) -{ - uint8_t* ptr = pdu->msg; - uint32_t n = 0; - rlc_am_read_data_pdu_header(&ptr, &n, header); -} - -// Read header from raw pointer, strip header -void rlc_am_read_data_pdu_header(uint8_t** payload, uint32_t* nof_bytes, rlc_amd_pdu_header_t* header) -{ - uint8_t ext; - uint8_t* ptr = *payload; - - header->dc = static_cast((*ptr >> 7) & 0x01); - - if (RLC_DC_FIELD_DATA_PDU == header->dc) { - // Fixed part - header->rf = ((*ptr >> 6) & 0x01); - header->p = ((*ptr >> 5) & 0x01); - header->fi = static_cast((*ptr >> 3) & 0x03); - ext = ((*ptr >> 2) & 0x01); - header->sn = (*ptr & 0x03) << 8; // 2 bits SN - ptr++; - header->sn |= (*ptr & 0xFF); // 8 bits SN - ptr++; - - if (header->rf) { - header->lsf = ((*ptr >> 7) & 0x01); - header->so = (*ptr & 0x7F) << 8; // 7 bits of SO - ptr++; - header->so |= (*ptr & 0xFF); // 8 bits of SO - ptr++; - } - - // Extension part - header->N_li = 0; - while (ext) { - if (header->N_li % 2 == 0) { - ext = ((*ptr >> 7) & 0x01); - header->li[header->N_li] = (*ptr & 0x7F) << 4; // 7 bits of LI - ptr++; - header->li[header->N_li] |= (*ptr & 0xF0) >> 4; // 4 bits of LI - header->N_li++; - } else { - ext = (*ptr >> 3) & 0x01; - header->li[header->N_li] = (*ptr & 0x07) << 8; // 3 bits of LI - ptr++; - header->li[header->N_li] |= (*ptr & 0xFF); // 8 bits of LI - header->N_li++; - ptr++; - } - } - - // Account for padding if N_li is odd - if (header->N_li % 2 == 1) { - ptr++; - } - - *nof_bytes -= ptr - *payload; - *payload = ptr; - } -} - -// Write header to pdu struct -void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, byte_buffer_t* pdu) -{ - uint8_t* ptr = pdu->msg; - rlc_am_write_data_pdu_header(header, &ptr); - pdu->N_bytes += ptr - pdu->msg; -} - -// Write header to pointer & move pointer -void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, uint8_t** payload) -{ - uint32_t i; - uint8_t ext = (header->N_li > 0) ? 1 : 0; - - uint8_t* ptr = *payload; - - // Fixed part - *ptr = (header->dc & 0x01) << 7; - *ptr |= (header->rf & 0x01) << 6; - *ptr |= (header->p & 0x01) << 5; - *ptr |= (header->fi & 0x03) << 3; - *ptr |= (ext & 0x01) << 2; - - *ptr |= (header->sn & 0x300) >> 8; // 2 bits SN - ptr++; - *ptr = (header->sn & 0xFF); // 8 bits SN - ptr++; - - // Segment part - if (header->rf) { - *ptr = (header->lsf & 0x01) << 7; - *ptr |= (header->so & 0x7F00) >> 8; // 7 bits of SO - ptr++; - *ptr = (header->so & 0x00FF); // 8 bits of SO - ptr++; - } - - // Extension part - i = 0; - while (i < header->N_li) { - ext = ((i + 1) == header->N_li) ? 0 : 1; - *ptr = (ext & 0x01) << 7; // 1 bit header - *ptr |= (header->li[i] & 0x7F0) >> 4; // 7 bits of LI - ptr++; - *ptr = (header->li[i] & 0x00F) << 4; // 4 bits of LI - i++; - if (i < header->N_li) { - ext = ((i + 1) == header->N_li) ? 0 : 1; - *ptr |= (ext & 0x01) << 3; // 1 bit header - *ptr |= (header->li[i] & 0x700) >> 8; // 3 bits of LI - ptr++; - *ptr = (header->li[i] & 0x0FF); // 8 bits of LI - ptr++; - i++; - } - } - // Pad if N_li is odd - if (header->N_li % 2 == 1) { - ptr++; - } - - *payload = ptr; -} - -void rlc_am_read_status_pdu(byte_buffer_t* pdu, rlc_status_pdu_t* status) -{ - rlc_am_read_status_pdu(pdu->msg, pdu->N_bytes, status); -} - -void rlc_am_read_status_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_status_pdu_t* status) -{ - uint32_t i; - uint8_t ext1, ext2; - bit_buffer_t tmp; - uint8_t* ptr = tmp.msg; - - srsran_bit_unpack_vector(payload, tmp.msg, nof_bytes * 8); - tmp.N_bits = nof_bytes * 8; - - rlc_dc_field_t dc = static_cast(srsran_bit_pack(&ptr, 1)); - - if (RLC_DC_FIELD_CONTROL_PDU == dc) { - uint8_t cpt = srsran_bit_pack(&ptr, 3); // 3-bit Control PDU Type (0 == status) - if (0 == cpt) { - status->ack_sn = srsran_bit_pack(&ptr, 10); // 10 bits ACK_SN - ext1 = srsran_bit_pack(&ptr, 1); // 1 bits E1 - status->N_nack = 0; - while (ext1) { - status->nacks[status->N_nack].nack_sn = srsran_bit_pack(&ptr, 10); - ext1 = srsran_bit_pack(&ptr, 1); // 1 bits E1 - ext2 = srsran_bit_pack(&ptr, 1); // 1 bits E2 - if (ext2) { - status->nacks[status->N_nack].has_so = true; - status->nacks[status->N_nack].so_start = srsran_bit_pack(&ptr, 15); - status->nacks[status->N_nack].so_end = srsran_bit_pack(&ptr, 15); - } - status->N_nack++; - } - } - } -} - -void rlc_am_write_status_pdu(rlc_status_pdu_t* status, byte_buffer_t* pdu) -{ - pdu->N_bytes = rlc_am_write_status_pdu(status, pdu->msg); -} - -int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload) -{ - uint32_t i; - uint8_t ext1; - bit_buffer_t tmp; - uint8_t* ptr = tmp.msg; - - srsran_bit_unpack(RLC_DC_FIELD_CONTROL_PDU, &ptr, 1); // D/C - srsran_bit_unpack(0, &ptr, 3); // CPT (0 == STATUS) - srsran_bit_unpack(status->ack_sn, &ptr, 10); // 10 bit ACK_SN - ext1 = (status->N_nack == 0) ? 0 : 1; - srsran_bit_unpack(ext1, &ptr, 1); // E1 - for (i = 0; i < status->N_nack; i++) { - srsran_bit_unpack(status->nacks[i].nack_sn, &ptr, 10); // 10 bit NACK_SN - ext1 = ((status->N_nack - 1) == i) ? 0 : 1; - srsran_bit_unpack(ext1, &ptr, 1); // E1 - if (status->nacks[i].has_so) { - srsran_bit_unpack(1, &ptr, 1); // E2 - srsran_bit_unpack(status->nacks[i].so_start, &ptr, 15); - srsran_bit_unpack(status->nacks[i].so_end, &ptr, 15); - } else { - srsran_bit_unpack(0, &ptr, 1); // E2 - } - } - - // Pad - tmp.N_bits = ptr - tmp.msg; - uint8_t n_pad = 8 - (tmp.N_bits % 8); - srsran_bit_unpack(0, &ptr, n_pad); - tmp.N_bits = ptr - tmp.msg; - - // Pack bits - srsran_bit_pack_vector(tmp.msg, payload, tmp.N_bits); - return tmp.N_bits / 8; -} - -bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status, uint32_t rx_win_min) -{ - // check if ACK_SN is inside Rx window - if ((MOD + status.ack_sn - rx_win_min) % MOD > RLC_AM_WINDOW_SIZE) { - return false; - } - - for (uint32_t i = 0; i < status.N_nack; ++i) { - // NACK can't be larger than ACK - if ((MOD + status.ack_sn - status.nacks[i].nack_sn) % MOD > RLC_AM_WINDOW_SIZE) { - return false; - } - // Don't NACK the ACK SN - if (status.nacks[i].nack_sn == status.ack_sn) { - return false; - } - } - return true; -} - -uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header) -{ - uint32_t len = 2; // Fixed part is 2 bytes - if (header->rf) { - len += 2; // Segment header is 2 bytes - } - len += header->N_li * 1.5 + 0.5; // Extension part - integer rounding up - return len; -} - -uint32_t rlc_am_packed_length(rlc_status_pdu_t* status) -{ - uint32_t len_bits = 15; // Fixed part is 15 bits - for (uint32_t i = 0; i < status->N_nack; i++) { - if (status->nacks[i].has_so) { - len_bits += 42; // 10 bits SN, 2 bits ext, 15 bits so_start, 15 bits so_end - } else { - len_bits += 12; // 10 bits SN, 2 bits ext - } - } - - return (len_bits + 7) / 8; // Convert to bytes - integer rounding up -} - -bool rlc_am_is_pdu_segment(uint8_t* payload) -{ - return ((*(payload) >> 6) & 0x01) == 1; -} - -void rlc_am_undelivered_sdu_info_to_string(fmt::memory_buffer& buffer, const std::vector& info_queue) -{ - fmt::format_to(buffer, "\n"); - for (const auto& pdcp_pdu : info_queue) { - fmt::format_to(buffer, "\tPDCP_SN = {}, undelivered RLC SNs = [", pdcp_pdu.sn); - for (const auto& nacked_segment : pdcp_pdu) { - fmt::format_to(buffer, "{} ", nacked_segment.rlc_sn()); - } - fmt::format_to(buffer, "]\n"); - } -} - -void log_rlc_amd_pdu_header_to_string(srslog::log_channel& log_ch, const rlc_amd_pdu_header_t& header) -{ - if (not log_ch.enabled()) { - return; - } - fmt::memory_buffer buffer; - fmt::format_to(buffer, - "[{}, RF={}, P={}, FI={}, SN={}, LSF={}, SO={}, N_li={}", - rlc_dc_field_text[header.dc], - (header.rf ? "1" : "0"), - (header.p ? "1" : "0"), - (header.fi ? "1" : "0"), - header.sn, - (header.lsf ? "1" : "0"), - header.so, - header.N_li); - if (header.N_li > 0) { - fmt::format_to(buffer, " ({}", header.li[0]); - for (uint32_t i = 1; i < header.N_li; ++i) { - fmt::format_to(buffer, ", {}", header.li[i]); - } - fmt::format_to(buffer, ")"); - } - fmt::format_to(buffer, "]"); - - log_ch("%s", to_c_str(buffer)); -} - -bool rlc_am_start_aligned(const uint8_t fi) -{ - return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_END_ALIGNED); -} - -bool rlc_am_end_aligned(const uint8_t fi) -{ - return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_START_ALIGNED); -} - -bool rlc_am_is_unaligned(const uint8_t fi) -{ - return (fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED); -} - -bool rlc_am_not_start_aligned(const uint8_t fi) -{ - return (fi == RLC_FI_FIELD_NOT_START_ALIGNED || fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED); -} - } // namespace srsran diff --git a/lib/src/rlc/rlc_am_lte_packing.cc b/lib/src/rlc/rlc_am_lte_packing.cc new file mode 100644 index 000000000..02643f3fc --- /dev/null +++ b/lib/src/rlc/rlc_am_lte_packing.cc @@ -0,0 +1,339 @@ +/** + * + * \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 "srsran/rlc/rlc_am_lte_packing.h" +#include + +namespace srsran { + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1 + ***************************************************************************/ + +// Read header from pdu struct, don't strip header +void rlc_am_read_data_pdu_header(byte_buffer_t* pdu, rlc_amd_pdu_header_t* header) +{ + uint8_t* ptr = pdu->msg; + uint32_t n = 0; + rlc_am_read_data_pdu_header(&ptr, &n, header); +} + +// Read header from raw pointer, strip header +void rlc_am_read_data_pdu_header(uint8_t** payload, uint32_t* nof_bytes, rlc_amd_pdu_header_t* header) +{ + uint8_t ext; + uint8_t* ptr = *payload; + + header->dc = static_cast((*ptr >> 7) & 0x01); + + if (RLC_DC_FIELD_DATA_PDU == header->dc) { + // Fixed part + header->rf = ((*ptr >> 6) & 0x01); + header->p = ((*ptr >> 5) & 0x01); + header->fi = static_cast((*ptr >> 3) & 0x03); + ext = ((*ptr >> 2) & 0x01); + header->sn = (*ptr & 0x03) << 8; // 2 bits SN + ptr++; + header->sn |= (*ptr & 0xFF); // 8 bits SN + ptr++; + + if (header->rf) { + header->lsf = ((*ptr >> 7) & 0x01); + header->so = (*ptr & 0x7F) << 8; // 7 bits of SO + ptr++; + header->so |= (*ptr & 0xFF); // 8 bits of SO + ptr++; + } + + // Extension part + header->N_li = 0; + while (ext) { + if (header->N_li % 2 == 0) { + ext = ((*ptr >> 7) & 0x01); + header->li[header->N_li] = (*ptr & 0x7F) << 4; // 7 bits of LI + ptr++; + header->li[header->N_li] |= (*ptr & 0xF0) >> 4; // 4 bits of LI + header->N_li++; + } else { + ext = (*ptr >> 3) & 0x01; + header->li[header->N_li] = (*ptr & 0x07) << 8; // 3 bits of LI + ptr++; + header->li[header->N_li] |= (*ptr & 0xFF); // 8 bits of LI + header->N_li++; + ptr++; + } + } + + // Account for padding if N_li is odd + if (header->N_li % 2 == 1) { + ptr++; + } + + *nof_bytes -= ptr - *payload; + *payload = ptr; + } +} + +// Write header to pdu struct +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, byte_buffer_t* pdu) +{ + uint8_t* ptr = pdu->msg; + rlc_am_write_data_pdu_header(header, &ptr); + pdu->N_bytes += ptr - pdu->msg; +} + +// Write header to pointer & move pointer +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, uint8_t** payload) +{ + uint32_t i; + uint8_t ext = (header->N_li > 0) ? 1 : 0; + + uint8_t* ptr = *payload; + + // Fixed part + *ptr = (header->dc & 0x01) << 7; + *ptr |= (header->rf & 0x01) << 6; + *ptr |= (header->p & 0x01) << 5; + *ptr |= (header->fi & 0x03) << 3; + *ptr |= (ext & 0x01) << 2; + + *ptr |= (header->sn & 0x300) >> 8; // 2 bits SN + ptr++; + *ptr = (header->sn & 0xFF); // 8 bits SN + ptr++; + + // Segment part + if (header->rf) { + *ptr = (header->lsf & 0x01) << 7; + *ptr |= (header->so & 0x7F00) >> 8; // 7 bits of SO + ptr++; + *ptr = (header->so & 0x00FF); // 8 bits of SO + ptr++; + } + + // Extension part + i = 0; + while (i < header->N_li) { + ext = ((i + 1) == header->N_li) ? 0 : 1; + *ptr = (ext & 0x01) << 7; // 1 bit header + *ptr |= (header->li[i] & 0x7F0) >> 4; // 7 bits of LI + ptr++; + *ptr = (header->li[i] & 0x00F) << 4; // 4 bits of LI + i++; + if (i < header->N_li) { + ext = ((i + 1) == header->N_li) ? 0 : 1; + *ptr |= (ext & 0x01) << 3; // 1 bit header + *ptr |= (header->li[i] & 0x700) >> 8; // 3 bits of LI + ptr++; + *ptr = (header->li[i] & 0x0FF); // 8 bits of LI + ptr++; + i++; + } + } + // Pad if N_li is odd + if (header->N_li % 2 == 1) { + ptr++; + } + + *payload = ptr; +} + +void rlc_am_read_status_pdu(byte_buffer_t* pdu, rlc_status_pdu_t* status) +{ + rlc_am_read_status_pdu(pdu->msg, pdu->N_bytes, status); +} + +void rlc_am_read_status_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_status_pdu_t* status) +{ + uint32_t i; + uint8_t ext1, ext2; + bit_buffer_t tmp; + uint8_t* ptr = tmp.msg; + + srsran_bit_unpack_vector(payload, tmp.msg, nof_bytes * 8); + tmp.N_bits = nof_bytes * 8; + + rlc_dc_field_t dc = static_cast(srsran_bit_pack(&ptr, 1)); + + if (RLC_DC_FIELD_CONTROL_PDU == dc) { + uint8_t cpt = srsran_bit_pack(&ptr, 3); // 3-bit Control PDU Type (0 == status) + if (0 == cpt) { + status->ack_sn = srsran_bit_pack(&ptr, 10); // 10 bits ACK_SN + ext1 = srsran_bit_pack(&ptr, 1); // 1 bits E1 + status->N_nack = 0; + while (ext1) { + status->nacks[status->N_nack].nack_sn = srsran_bit_pack(&ptr, 10); + ext1 = srsran_bit_pack(&ptr, 1); // 1 bits E1 + ext2 = srsran_bit_pack(&ptr, 1); // 1 bits E2 + if (ext2) { + status->nacks[status->N_nack].has_so = true; + status->nacks[status->N_nack].so_start = srsran_bit_pack(&ptr, 15); + status->nacks[status->N_nack].so_end = srsran_bit_pack(&ptr, 15); + } + status->N_nack++; + } + } + } +} + +void rlc_am_write_status_pdu(rlc_status_pdu_t* status, byte_buffer_t* pdu) +{ + pdu->N_bytes = rlc_am_write_status_pdu(status, pdu->msg); +} + +int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload) +{ + uint32_t i; + uint8_t ext1; + bit_buffer_t tmp; + uint8_t* ptr = tmp.msg; + + srsran_bit_unpack(RLC_DC_FIELD_CONTROL_PDU, &ptr, 1); // D/C + srsran_bit_unpack(0, &ptr, 3); // CPT (0 == STATUS) + srsran_bit_unpack(status->ack_sn, &ptr, 10); // 10 bit ACK_SN + ext1 = (status->N_nack == 0) ? 0 : 1; + srsran_bit_unpack(ext1, &ptr, 1); // E1 + for (i = 0; i < status->N_nack; i++) { + srsran_bit_unpack(status->nacks[i].nack_sn, &ptr, 10); // 10 bit NACK_SN + ext1 = ((status->N_nack - 1) == i) ? 0 : 1; + srsran_bit_unpack(ext1, &ptr, 1); // E1 + if (status->nacks[i].has_so) { + srsran_bit_unpack(1, &ptr, 1); // E2 + srsran_bit_unpack(status->nacks[i].so_start, &ptr, 15); + srsran_bit_unpack(status->nacks[i].so_end, &ptr, 15); + } else { + srsran_bit_unpack(0, &ptr, 1); // E2 + } + } + + // Pad + tmp.N_bits = ptr - tmp.msg; + uint8_t n_pad = 8 - (tmp.N_bits % 8); + srsran_bit_unpack(0, &ptr, n_pad); + tmp.N_bits = ptr - tmp.msg; + + // Pack bits + srsran_bit_pack_vector(tmp.msg, payload, tmp.N_bits); + return tmp.N_bits / 8; +} + +uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header) +{ + uint32_t len = 2; // Fixed part is 2 bytes + if (header->rf) { + len += 2; // Segment header is 2 bytes + } + len += header->N_li * 1.5 + 0.5; // Extension part - integer rounding up + return len; +} + +uint32_t rlc_am_packed_length(rlc_status_pdu_t* status) +{ + uint32_t len_bits = 15; // Fixed part is 15 bits + for (uint32_t i = 0; i < status->N_nack; i++) { + if (status->nacks[i].has_so) { + len_bits += 42; // 10 bits SN, 2 bits ext, 15 bits so_start, 15 bits so_end + } else { + len_bits += 12; // 10 bits SN, 2 bits ext + } + } + + return (len_bits + 7) / 8; // Convert to bytes - integer rounding up +} + +bool rlc_am_is_pdu_segment(uint8_t* payload) +{ + return ((*(payload) >> 6) & 0x01) == 1; +} + +bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status, uint32_t rx_win_min) +{ + // check if ACK_SN is inside Rx window + if ((MOD + status.ack_sn - rx_win_min) % MOD > RLC_AM_WINDOW_SIZE) { + return false; + } + + for (uint32_t i = 0; i < status.N_nack; ++i) { + // NACK can't be larger than ACK + if ((MOD + status.ack_sn - status.nacks[i].nack_sn) % MOD > RLC_AM_WINDOW_SIZE) { + return false; + } + // Don't NACK the ACK SN + if (status.nacks[i].nack_sn == status.ack_sn) { + return false; + } + } + return true; +} + +void rlc_am_undelivered_sdu_info_to_string(fmt::memory_buffer& buffer, const std::vector& info_queue) +{ + fmt::format_to(buffer, "\n"); + for (const auto& pdcp_pdu : info_queue) { + fmt::format_to(buffer, "\tPDCP_SN = {}, undelivered RLC SNs = [", pdcp_pdu.sn); + for (const auto& nacked_segment : pdcp_pdu) { + fmt::format_to(buffer, "{} ", nacked_segment.rlc_sn()); + } + fmt::format_to(buffer, "]\n"); + } +} + +bool rlc_am_start_aligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_END_ALIGNED); +} + +bool rlc_am_end_aligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_START_ALIGNED); +} + +bool rlc_am_is_unaligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED); +} + +bool rlc_am_not_start_aligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_NOT_START_ALIGNED || fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED); +} + +void log_rlc_amd_pdu_header_to_string(srslog::log_channel& log_ch, const rlc_amd_pdu_header_t& header) +{ + if (not log_ch.enabled()) { + return; + } + fmt::memory_buffer buffer; + fmt::format_to(buffer, + "[{}, RF={}, P={}, FI={}, SN={}, LSF={}, SO={}, N_li={}", + rlc_dc_field_text[header.dc], + (header.rf ? "1" : "0"), + (header.p ? "1" : "0"), + (header.fi ? "1" : "0"), + header.sn, + (header.lsf ? "1" : "0"), + header.so, + header.N_li); + if (header.N_li > 0) { + fmt::format_to(buffer, " ({}", header.li[0]); + for (uint32_t i = 1; i < header.N_li; ++i) { + fmt::format_to(buffer, ", {}", header.li[i]); + } + fmt::format_to(buffer, ")"); + } + fmt::format_to(buffer, "]"); + + log_ch("%s", to_c_str(buffer)); +} + +} // namespace srsran diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index 1dedca3c8..cef2d1935 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -11,240 +11,17 @@ */ #include "srsran/rlc/rlc_am_nr.h" -#include +#include "srsran/common/string_helpers.h" +#include "srsran/interfaces/ue_pdcp_interfaces.h" +#include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/srslog/event_trace.h" +#include namespace srsran { -/**************************************************************************** - * Header pack/unpack helper functions - * Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.4 - ***************************************************************************/ - -uint32_t rlc_am_nr_read_data_pdu_header(const byte_buffer_t* pdu, - const rlc_am_nr_sn_size_t sn_size, - rlc_am_nr_pdu_header_t* header) -{ - return rlc_am_nr_read_data_pdu_header(pdu->msg, pdu->N_bytes, sn_size, header); -} - -uint32_t rlc_am_nr_read_data_pdu_header(const uint8_t* payload, - const uint32_t nof_bytes, - const rlc_am_nr_sn_size_t sn_size, - rlc_am_nr_pdu_header_t* header) -{ - uint8_t* ptr = const_cast(payload); - - header->sn_size = sn_size; - - // Fixed part - header->dc = (rlc_dc_field_t)((*ptr >> 7) & 0x01); // 1 bit D/C field - header->p = (*ptr >> 6) & 0x01; // 1 bit P flag - header->si = (rlc_nr_si_field_t)((*ptr >> 4) & 0x03); // 2 bits SI - - if (sn_size == rlc_am_nr_sn_size_t::size12bits) { - header->sn = (*ptr & 0x0F) << 8; // first 4 bits SN - ptr++; - - header->sn |= (*ptr & 0xFF); // last 8 bits SN - ptr++; - } else if (sn_size == rlc_am_nr_sn_size_t::size18bits) { - // sanity check - if ((*ptr & 0x0c) != 0) { - fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); - return 0; - } - header->sn = (*ptr & 0x03) << 16; // first 4 bits SN - ptr++; - header->sn |= (*ptr & 0xFF) << 8; // bit 2-10 of SN - ptr++; - header->sn |= (*ptr & 0xFF); // last 8 bits SN - ptr++; - } else { - fprintf(stderr, "Unsupported SN length\n"); - return 0; - } - - // Read optional part - if (header->si == rlc_nr_si_field_t::last_segment || - header->si == rlc_nr_si_field_t::neither_first_nor_last_segment) { - // read SO - header->so = (*ptr & 0xFF) << 8; - ptr++; - header->so |= (*ptr & 0xFF); - ptr++; - } - - // return consumed bytes - return (ptr - payload); -} - -uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header) -{ - uint32_t len = 0; - if (header.si == rlc_nr_si_field_t::full_sdu || header.si == rlc_nr_si_field_t::first_segment) { - len = 2; - if (header.sn_size == rlc_am_nr_sn_size_t::size18bits) { - len++; - } - } else { - // PDU contains SO - len = 4; - if (header.sn_size == rlc_am_nr_sn_size_t::size18bits) { - len++; - } - } - return len; -} - -uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, byte_buffer_t* pdu) -{ - // Make room for the header - uint32_t len = rlc_am_nr_packed_length(header); - pdu->msg -= len; - uint8_t* ptr = pdu->msg; - - // fixed header part - *ptr = (header.dc & 0x01) << 7; ///< 1 bit D/C field - *ptr |= (header.p & 0x01) << 6; ///< 1 bit P flag - *ptr |= (header.si & 0x03) << 4; ///< 2 bits SI - - if (header.sn_size == rlc_am_nr_sn_size_t::size12bits) { - // write first 4 bit of SN - *ptr |= (header.sn >> 8) & 0x0f; // 4 bit SN - ptr++; - *ptr = header.sn & 0xff; // remaining 8 bit of SN - ptr++; - } else { - // 18bit SN - *ptr |= (header.sn >> 16) & 0x3; // 2 bit SN - ptr++; - *ptr = header.sn >> 8; // bit 3 - 10 of SN - ptr++; - *ptr = (header.sn & 0xff); // remaining 8 bit of SN - ptr++; - } - - if (header.so) { - // write SO - *ptr = header.so >> 8; // first part of SO - ptr++; - *ptr = (header.so & 0xff); // second part of SO - ptr++; - } - - pdu->N_bytes += ptr - pdu->msg; - - return len; -} - -uint32_t -rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status) -{ - return rlc_am_nr_read_status_pdu(pdu->msg, pdu->N_bytes, sn_size, status); -} - -uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, - const uint32_t nof_bytes, - const rlc_am_nr_sn_size_t sn_size, - rlc_am_nr_status_pdu_t* status) -{ - uint8_t* ptr = const_cast(payload); - - // fixed part - status->cpt = (rlc_am_nr_control_pdu_type_t)((*ptr >> 4) & 0x07); // 3 bits CPT - - // sanity check - if (status->cpt != rlc_am_nr_control_pdu_type_t::status_pdu) { - fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); - return 0; - } - - if (sn_size == rlc_am_nr_sn_size_t::size12bits) { - status->ack_sn = (*ptr & 0x0F) << 8; // first 4 bits SN - ptr++; - - status->ack_sn |= (*ptr & 0xFF); // last 8 bits SN - ptr++; - - // read E1 flag - uint8_t e1 = *ptr & 0x80; - - // sanity check for reserved bits - if ((*ptr & 0x7f) != 0) { - fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); - return 0; - } - - // all good, continue with next byte depending on E1 - ptr++; - - // reset number of acks - status->N_nack = 0; - - if (e1) { - // E1 flag set, read a NACK_SN - rlc_status_nack_t nack = {}; - nack.nack_sn = (*ptr & 0xff) << 4; - ptr++; - // uint8_t len2 = (*ptr & 0xF0) >> 4; - nack.nack_sn |= (*ptr & 0xF0) >> 4; - status->nacks[status->N_nack] = nack; - - status->N_nack++; - } - } - - return SRSRAN_SUCCESS; -} - -/** - * Write a RLC AM NR status PDU to a PDU buffer and eets the length of the generate PDU accordingly - * @param status_pdu The status PDU - * @param pdu A pointer to a unique bytebuffer - * @return SRSRAN_SUCCESS if PDU was written, SRSRAN_ERROR otherwise - */ -int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, - const rlc_am_nr_sn_size_t sn_size, - byte_buffer_t* pdu) -{ - uint8_t* ptr = pdu->msg; - - // fixed header part - *ptr = 0; ///< 1 bit D/C field and 3bit CPT are all zero - - if (sn_size == rlc_am_nr_sn_size_t::size12bits) { - // write first 4 bit of ACK_SN - *ptr |= (status_pdu.ack_sn >> 8) & 0x0f; // 4 bit ACK_SN - ptr++; - *ptr = status_pdu.ack_sn & 0xff; // remaining 8 bit of SN - ptr++; - - // write E1 flag in octet 3 - *ptr = (status_pdu.N_nack > 0) ? 0x80 : 0x00; - ptr++; - - if (status_pdu.N_nack > 0) { - // write first 8 bit of NACK_SN - *ptr = (status_pdu.nacks[0].nack_sn >> 4) & 0xff; - ptr++; - - // write remaining 4 bits of NACK_SN - *ptr = status_pdu.nacks[0].nack_sn & 0xf0; - ptr++; - } - } else { - // 18bit SN - *ptr |= (status_pdu.ack_sn >> 14) & 0x0f; // 4 bit ACK_SN - ptr++; - *ptr = status_pdu.ack_sn >> 8; // bit 3 - 10 of SN - ptr++; - *ptr = (status_pdu.ack_sn & 0xff); // remaining 6 bit of SN - ptr++; - } - - pdu->N_bytes = ptr - pdu->msg; - - return SRSRAN_SUCCESS; -} +/******************************* + * RLC AM NR class + ******************************/ +// TODO } // namespace srsran diff --git a/lib/src/rlc/rlc_am_nr_packing.cc b/lib/src/rlc/rlc_am_nr_packing.cc new file mode 100644 index 000000000..b2f976f8f --- /dev/null +++ b/lib/src/rlc/rlc_am_nr_packing.cc @@ -0,0 +1,250 @@ +/** + * + * \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 "srsran/rlc/rlc_am_nr_packing.h" +#include + +namespace srsran { + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.4 + ***************************************************************************/ + +uint32_t rlc_am_nr_read_data_pdu_header(const byte_buffer_t* pdu, + const rlc_am_nr_sn_size_t sn_size, + rlc_am_nr_pdu_header_t* header) +{ + return rlc_am_nr_read_data_pdu_header(pdu->msg, pdu->N_bytes, sn_size, header); +} + +uint32_t rlc_am_nr_read_data_pdu_header(const uint8_t* payload, + const uint32_t nof_bytes, + const rlc_am_nr_sn_size_t sn_size, + rlc_am_nr_pdu_header_t* header) +{ + uint8_t* ptr = const_cast(payload); + + header->sn_size = sn_size; + + // Fixed part + header->dc = (rlc_dc_field_t)((*ptr >> 7) & 0x01); // 1 bit D/C field + header->p = (*ptr >> 6) & 0x01; // 1 bit P flag + header->si = (rlc_nr_si_field_t)((*ptr >> 4) & 0x03); // 2 bits SI + + if (sn_size == rlc_am_nr_sn_size_t::size12bits) { + header->sn = (*ptr & 0x0F) << 8; // first 4 bits SN + ptr++; + + header->sn |= (*ptr & 0xFF); // last 8 bits SN + ptr++; + } else if (sn_size == rlc_am_nr_sn_size_t::size18bits) { + // sanity check + if ((*ptr & 0x0c) != 0) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } + header->sn = (*ptr & 0x03) << 16; // first 4 bits SN + ptr++; + header->sn |= (*ptr & 0xFF) << 8; // bit 2-10 of SN + ptr++; + header->sn |= (*ptr & 0xFF); // last 8 bits SN + ptr++; + } else { + fprintf(stderr, "Unsupported SN length\n"); + return 0; + } + + // Read optional part + if (header->si == rlc_nr_si_field_t::last_segment || + header->si == rlc_nr_si_field_t::neither_first_nor_last_segment) { + // read SO + header->so = (*ptr & 0xFF) << 8; + ptr++; + header->so |= (*ptr & 0xFF); + ptr++; + } + + // return consumed bytes + return (ptr - payload); +} + +uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header) +{ + uint32_t len = 0; + if (header.si == rlc_nr_si_field_t::full_sdu || header.si == rlc_nr_si_field_t::first_segment) { + len = 2; + if (header.sn_size == rlc_am_nr_sn_size_t::size18bits) { + len++; + } + } else { + // PDU contains SO + len = 4; + if (header.sn_size == rlc_am_nr_sn_size_t::size18bits) { + len++; + } + } + return len; +} + +uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, byte_buffer_t* pdu) +{ + // Make room for the header + uint32_t len = rlc_am_nr_packed_length(header); + pdu->msg -= len; + uint8_t* ptr = pdu->msg; + + // fixed header part + *ptr = (header.dc & 0x01) << 7; ///< 1 bit D/C field + *ptr |= (header.p & 0x01) << 6; ///< 1 bit P flag + *ptr |= (header.si & 0x03) << 4; ///< 2 bits SI + + if (header.sn_size == rlc_am_nr_sn_size_t::size12bits) { + // write first 4 bit of SN + *ptr |= (header.sn >> 8) & 0x0f; // 4 bit SN + ptr++; + *ptr = header.sn & 0xff; // remaining 8 bit of SN + ptr++; + } else { + // 18bit SN + *ptr |= (header.sn >> 16) & 0x3; // 2 bit SN + ptr++; + *ptr = header.sn >> 8; // bit 3 - 10 of SN + ptr++; + *ptr = (header.sn & 0xff); // remaining 8 bit of SN + ptr++; + } + + if (header.so) { + // write SO + *ptr = header.so >> 8; // first part of SO + ptr++; + *ptr = (header.so & 0xff); // second part of SO + ptr++; + } + + pdu->N_bytes += ptr - pdu->msg; + + return len; +} + +uint32_t +rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status) +{ + return rlc_am_nr_read_status_pdu(pdu->msg, pdu->N_bytes, sn_size, status); +} + +uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, + const uint32_t nof_bytes, + const rlc_am_nr_sn_size_t sn_size, + rlc_am_nr_status_pdu_t* status) +{ + uint8_t* ptr = const_cast(payload); + + // fixed part + status->cpt = (rlc_am_nr_control_pdu_type_t)((*ptr >> 4) & 0x07); // 3 bits CPT + + // sanity check + if (status->cpt != rlc_am_nr_control_pdu_type_t::status_pdu) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } + + if (sn_size == rlc_am_nr_sn_size_t::size12bits) { + status->ack_sn = (*ptr & 0x0F) << 8; // first 4 bits SN + ptr++; + + status->ack_sn |= (*ptr & 0xFF); // last 8 bits SN + ptr++; + + // read E1 flag + uint8_t e1 = *ptr & 0x80; + + // sanity check for reserved bits + if ((*ptr & 0x7f) != 0) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } + + // all good, continue with next byte depending on E1 + ptr++; + + // reset number of acks + status->N_nack = 0; + + if (e1) { + // E1 flag set, read a NACK_SN + rlc_status_nack_t nack = {}; + nack.nack_sn = (*ptr & 0xff) << 4; + ptr++; + // uint8_t len2 = (*ptr & 0xF0) >> 4; + nack.nack_sn |= (*ptr & 0xF0) >> 4; + status->nacks[status->N_nack] = nack; + + status->N_nack++; + } + } + + return SRSRAN_SUCCESS; +} + +/** + * Write a RLC AM NR status PDU to a PDU buffer and eets the length of the generate PDU accordingly + * @param status_pdu The status PDU + * @param pdu A pointer to a unique bytebuffer + * @return SRSRAN_SUCCESS if PDU was written, SRSRAN_ERROR otherwise + */ +int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, + const rlc_am_nr_sn_size_t sn_size, + byte_buffer_t* pdu) +{ + uint8_t* ptr = pdu->msg; + + // fixed header part + *ptr = 0; ///< 1 bit D/C field and 3bit CPT are all zero + + if (sn_size == rlc_am_nr_sn_size_t::size12bits) { + // write first 4 bit of ACK_SN + *ptr |= (status_pdu.ack_sn >> 8) & 0x0f; // 4 bit ACK_SN + ptr++; + *ptr = status_pdu.ack_sn & 0xff; // remaining 8 bit of SN + ptr++; + + // write E1 flag in octet 3 + *ptr = (status_pdu.N_nack > 0) ? 0x80 : 0x00; + ptr++; + + if (status_pdu.N_nack > 0) { + // write first 8 bit of NACK_SN + *ptr = (status_pdu.nacks[0].nack_sn >> 4) & 0xff; + ptr++; + + // write remaining 4 bits of NACK_SN + *ptr = status_pdu.nacks[0].nack_sn & 0xf0; + ptr++; + } + } else { + // 18bit SN + *ptr |= (status_pdu.ack_sn >> 14) & 0x0f; // 4 bit ACK_SN + ptr++; + *ptr = status_pdu.ack_sn >> 8; // bit 3 - 10 of SN + ptr++; + *ptr = (status_pdu.ack_sn & 0xff); // remaining 6 bit of SN + ptr++; + } + + pdu->N_bytes = ptr - pdu->msg; + + return SRSRAN_SUCCESS; +} + +} // namespace srsran diff --git a/lib/test/rlc/rlc_am_nr_pdu_test.cc b/lib/test/rlc/rlc_am_nr_pdu_test.cc index 6b72a6fc2..a2257c9d9 100644 --- a/lib/test/rlc/rlc_am_nr_pdu_test.cc +++ b/lib/test/rlc/rlc_am_nr_pdu_test.cc @@ -12,7 +12,7 @@ #include "srsran/config.h" #include "srsran/rlc/rlc.h" -#include "srsran/rlc/rlc_am_nr.h" +#include "srsran/rlc/rlc_am_nr_packing.h" #include #include