mirror of https://github.com/pvnis/srsRAN_4G.git
Moved RLC AM LTE and NR packing functions to their own file.
parent
f93d428e49
commit
2bb249bf09
@ -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 <list>
|
||||
|
||||
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<rlc_am_pdu_segment>;
|
||||
const static uint32_t invalid_rlc_sn = std::numeric_limits<uint32_t>::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<rlc_amd_rx_pdu> 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<uint32_t, pdcp_pdu_info>& 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 <typename... Args>
|
||||
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>(args)..., to_c_str(buffer));
|
||||
}
|
||||
|
||||
} // namespace srsran
|
||||
|
||||
#endif // SRSRAN_RLC_AM_NR_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
|
@ -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 <sstream>
|
||||
|
||||
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<rlc_dc_field_t>((*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<rlc_fi_field_t>((*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<rlc_dc_field_t>(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<pdcp_pdu_info>& 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
|
@ -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 <sstream>
|
||||
|
||||
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<uint8_t*>(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<uint8_t*>(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
|
Loading…
Reference in New Issue