mirror of https://github.com/PentHertz/srsLTE.git
322 lines
9.5 KiB
C++
322 lines
9.5 KiB
C++
/**
|
|
* Copyright 2013-2022 Software Radio Systems Limited
|
|
*
|
|
* This file is part of srsRAN.
|
|
*
|
|
* srsRAN is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* srsRAN is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* A copy of the GNU Affero General Public License can be found in
|
|
* the LICENSE file in the top-level directory of this distribution
|
|
* and at http://www.gnu.org/licenses/.
|
|
*
|
|
*/
|
|
|
|
#include "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<rlc_amd_pdu_header_t> >& 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);
|
|
}
|
|
|
|
} // namespace srsran
|