You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
srsRAN_4G/lib/src/mac/mac_sch_pdu_nr.cc

413 lines
10 KiB
C++

/**
*
* \section COPYRIGHT
*
* Copyright 2013-2020 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 "srslte/mac/mac_sch_pdu_nr.h"
namespace srslte {
mac_sch_subpdu_nr::mac_sch_subpdu_nr(mac_sch_pdu_nr* parent_) : parent(parent_) {}
mac_sch_subpdu_nr::nr_lcid_sch_t mac_sch_subpdu_nr::get_type()
{
if (lcid >= 32) {
return (nr_lcid_sch_t)lcid;
}
return CCCH;
}
bool mac_sch_subpdu_nr::is_sdu()
{
// for UL-SCH LCID 52 is also valid for carrying SDUs
return (lcid <= 32 || (parent->is_ulsch() && lcid == 52));
}
// returns false for all reserved values in Table 6.2.1-1 and 6.2.1-2
bool mac_sch_subpdu_nr::is_valid_lcid()
{
return (lcid <= 63 && ((parent->is_ulsch() && (lcid <= 32 || lcid >= 52)) || (lcid <= 32 || lcid >= 47)));
}
bool mac_sch_subpdu_nr::is_var_len_ce()
{
return false;
}
// return length of PDU (or SRSLTE_ERROR otherwise)
int32_t mac_sch_subpdu_nr::read_subheader(const uint8_t* ptr)
{
// Skip R, read F bit and LCID
F_bit = (bool)(*ptr & 0x40) ? true : false;
lcid = (uint8_t)*ptr & 0x3f;
ptr++;
header_length = 1;
if (is_valid_lcid()) {
if ((is_sdu() || is_var_len_ce()) && not is_ul_ccch()) {
// Read first length byte
sdu_length = (uint32_t)*ptr;
ptr++;
header_length++;
if (F_bit) {
// add second length byte
sdu_length = sdu_length << 8 | ((uint32_t)*ptr & 0xff);
ptr++;
header_length++;
}
} else {
sdu_length = sizeof_ce(lcid, parent->is_ulsch());
}
sdu = (uint8_t*)ptr;
} else {
srslog::fetch_basic_logger("MAC").warning("Invalid LCID (%d) in MAC PDU", lcid);
return SRSLTE_ERROR;
}
return header_length;
}
void mac_sch_subpdu_nr::set_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_)
{
lcid = lcid_;
sdu = const_cast<uint8_t*>(payload_);
header_length = is_ul_ccch() ? 1 : 2;
sdu_length = len_;
if (is_ul_ccch()) {
F_bit = false;
sdu_length = sizeof_ce(lcid, parent->is_ulsch());
if (len_ != static_cast<uint32_t>(sdu_length)) {
srslog::fetch_basic_logger("MAC").warning("Invalid SDU length of UL-SCH SDU (%d != %d)", len_, sdu_length);
}
}
if (sdu_length >= 256) {
F_bit = true;
header_length += 1;
}
}
void mac_sch_subpdu_nr::set_padding(const uint32_t len_)
{
lcid = PADDING;
// 1 Byte R/LCID MAC subheader
sdu_length = len_ - 1;
header_length = 1;
}
// Turn a subPDU into a C-RNTI CE, error checking takes place in the caller
void mac_sch_subpdu_nr::set_c_rnti(const uint16_t crnti_)
{
lcid = CRNTI;
header_length = 1;
sdu_length = sizeof_ce(lcid, parent->is_ulsch());
sdu = ce_write_buffer.data();
uint16_t crnti = htole32(crnti_);
ce_write_buffer.at(0) = (uint8_t)((crnti & 0xff00) >> 8);
ce_write_buffer.at(1) = (uint8_t)((crnti & 0x00ff));
}
// Turn a subPDU into a single entry PHR CE, error checking takes place in the caller
void mac_sch_subpdu_nr::set_se_phr(const uint8_t phr_, const uint8_t pcmax_)
{
lcid = SE_PHR;
header_length = 1;
sdu_length = sizeof_ce(lcid, parent->is_ulsch());
sdu = ce_write_buffer.data();
ce_write_buffer.at(0) = (uint8_t)(phr_ & 0x3f);
ce_write_buffer.at(1) = (uint8_t)(pcmax_ & 0x3f);
}
// Turn a subPDU into a single short BSR
void mac_sch_subpdu_nr::set_sbsr(const lcg_bsr_t bsr_)
{
lcid = SHORT_BSR;
header_length = 1;
sdu_length = sizeof_ce(lcid, parent->is_ulsch());
sdu = ce_write_buffer.data();
ce_write_buffer.at(0) = ((bsr_.lcg_id & 0x07) << 5) | (bsr_.buffer_size & 0x1f);
}
// Section 6.1.2
uint32_t mac_sch_subpdu_nr::write_subpdu(const uint8_t* start_)
{
uint8_t* ptr = const_cast<uint8_t*>(start_);
*ptr = (uint8_t)((F_bit ? 1 : 0) << 6) | ((uint8_t)lcid & 0x3f);
ptr += 1;
if (header_length == 3) {
// 3 Byte R/F/LCID/L MAC subheader with 16-bit L field
*ptr = ((sdu_length & 0xff00) >> 8);
ptr += 1;
*ptr = static_cast<uint8_t>(sdu_length);
ptr += 1;
} else if (header_length == 2) {
// 2 Byte R/F/LCID/L MAC subheader with 8-bit L field
*ptr = static_cast<uint8_t>(sdu_length);
ptr += 1;
} else if (header_length == 1) {
// do nothing
} else {
srslog::fetch_basic_logger("MAC").warning("Error while packing PDU. Unsupported header length (%d)", header_length);
}
// copy SDU payload
if (sdu) {
memcpy(ptr, sdu, sdu_length);
} else {
// clear memory
memset(ptr, 0, sdu_length);
}
ptr += sdu_length;
// return total length of subpdu
return ptr - start_;
}
uint32_t mac_sch_subpdu_nr::get_total_length()
{
return (header_length + sdu_length);
}
uint32_t mac_sch_subpdu_nr::get_sdu_length()
{
return sdu_length;
}
uint32_t mac_sch_subpdu_nr::get_lcid()
{
return lcid;
}
uint8_t* mac_sch_subpdu_nr::get_sdu()
{
return sdu;
}
uint16_t mac_sch_subpdu_nr::get_c_rnti()
{
if (parent->is_ulsch() && lcid == CRNTI) {
return le16toh((uint16_t)sdu[0] << 8 | sdu[1]);
}
return 0;
}
uint8_t mac_sch_subpdu_nr::get_phr()
{
if (parent->is_ulsch() && lcid == SE_PHR) {
return sdu[0] & 0x3f;
}
return 0;
}
uint8_t mac_sch_subpdu_nr::get_pcmax()
{
if (parent->is_ulsch() && lcid == SE_PHR) {
return sdu[1] & 0x3f;
}
return 0;
}
mac_sch_subpdu_nr::lcg_bsr_t mac_sch_subpdu_nr::get_sbsr()
{
lcg_bsr_t sbsr = {};
if (parent->is_ulsch() && lcid == SHORT_BSR) {
sbsr.lcg_id = (sdu[0] & 0xe0) >> 5;
sbsr.buffer_size = sdu[0] & 0x1f;
}
return sbsr;
}
uint32_t mac_sch_subpdu_nr::sizeof_ce(uint32_t lcid, bool is_ul)
{
if (is_ul) {
switch (lcid) {
case CCCH_SIZE_48:
return 6;
case CCCH_SIZE_64:
return 8;
case CRNTI:
return 2;
case SHORT_TRUNC_BSR:
return 1;
case SHORT_BSR:
return 1;
case SE_PHR:
return 2;
case PADDING:
return 0;
}
} else {
switch (lcid) {
case CON_RES_ID:
return 6;
case TA_CMD:
return 1;
case DRX_CMD:
return 0;
case PADDING:
return 0;
}
}
return 0;
}
inline bool mac_sch_subpdu_nr::is_ul_ccch()
{
return (parent->is_ulsch() && (lcid == CCCH_SIZE_48 || lcid == CCCH_SIZE_64));
}
void mac_sch_pdu_nr::pack()
{
// SDU and CEs are written in-place, only add padding if needed
if (remaining_len) {
mac_sch_subpdu_nr padding_subpdu(this);
padding_subpdu.set_padding(remaining_len);
padding_subpdu.write_subpdu(buffer->msg + buffer->N_bytes);
// update length
buffer->N_bytes += padding_subpdu.get_total_length();
remaining_len -= padding_subpdu.get_total_length();
subpdus.push_back(padding_subpdu);
}
}
void mac_sch_pdu_nr::unpack(const uint8_t* payload, const uint32_t& len)
{
uint32_t offset = 0;
while (offset < len) {
mac_sch_subpdu_nr sch_pdu(this);
if (sch_pdu.read_subheader(payload + offset) == SRSLTE_ERROR) {
fprintf(stderr, "Error parsing NR MAC PDU (len=%d, offset=%d)\n", len, offset);
return;
}
offset += sch_pdu.get_total_length();
if (sch_pdu.get_lcid() == mac_sch_subpdu_nr::PADDING) {
// set SDU length to rest of PDU
sch_pdu.set_padding(len - offset + 1); // One byte for Padding header will be substracted again
// skip remaining bytes
offset = len;
}
subpdus.push_back(sch_pdu);
}
if (offset != len) {
fprintf(stderr, "Error parsing NR MAC PDU (len=%d, offset=%d)\n", len, offset);
}
}
uint32_t mac_sch_pdu_nr::get_num_subpdus()
{
return subpdus.size();
}
const mac_sch_subpdu_nr& mac_sch_pdu_nr::get_subpdu(const uint32_t& index)
{
return subpdus.at(index);
}
bool mac_sch_pdu_nr::is_ulsch()
{
return ulsch;
}
void mac_sch_pdu_nr::init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_, bool ulsch_)
{
buffer = buffer_;
subpdus.clear();
pdu_len = pdu_len_;
remaining_len = pdu_len_;
ulsch = ulsch_;
}
void mac_sch_pdu_nr::init_rx(bool ulsch_)
{
buffer = nullptr;
subpdus.clear();
pdu_len = 0;
remaining_len = 0;
ulsch = ulsch_;
}
uint32_t mac_sch_pdu_nr::size_header_sdu(const uint32_t lcid, const uint32_t nbytes)
{
if (ulsch && (lcid == mac_sch_subpdu_nr::CCCH_SIZE_48 || lcid == mac_sch_subpdu_nr::CCCH_SIZE_64)) {
return 1;
} else {
if (nbytes < 256) {
return 2;
} else {
return 3;
}
}
}
uint32_t mac_sch_pdu_nr::get_remaing_len()
{
return remaining_len;
}
uint32_t mac_sch_pdu_nr::add_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_)
{
int header_size = size_header_sdu(lcid_, len_);
if (header_size + len_ > remaining_len) {
printf("Header and SDU exceed space in PDU (%d > %d).\n", header_size + len_, remaining_len);
return SRSLTE_ERROR;
}
mac_sch_subpdu_nr sch_pdu(this);
sch_pdu.set_sdu(lcid_, payload_, len_);
return add_sudpdu(sch_pdu);
}
uint32_t mac_sch_pdu_nr::add_crnti_ce(const uint16_t crnti)
{
mac_sch_subpdu_nr ce(this);
ce.set_c_rnti(crnti);
return add_sudpdu(ce);
}
uint32_t mac_sch_pdu_nr::add_se_phr_ce(const uint8_t phr, const uint8_t pcmax)
{
mac_sch_subpdu_nr ce(this);
ce.set_se_phr(phr, pcmax);
return add_sudpdu(ce);
}
uint32_t mac_sch_pdu_nr::add_sbsr_ce(const mac_sch_subpdu_nr::lcg_bsr_t bsr_)
{
mac_sch_subpdu_nr ce(this);
ce.set_sbsr(bsr_);
return add_sudpdu(ce);
}
uint32_t mac_sch_pdu_nr::add_sudpdu(mac_sch_subpdu_nr& subpdu)
{
uint32_t subpdu_len = subpdu.get_total_length();
if (subpdu_len > remaining_len) {
logger.warning("Not enough space to add subPDU to PDU (%d > %d)", subpdu_len, remaining_len);
return SRSLTE_ERROR;
}
// Write subPDU straigt into provided buffer
subpdu.write_subpdu(buffer->msg + buffer->N_bytes);
// adopt buffer variables
buffer->N_bytes += subpdu_len;
remaining_len -= subpdu_len;
subpdus.push_back(subpdu);
return SRSLTE_SUCCESS;
}
} // namespace srslte