From 6baa89cd2c744a67dd78e7a655666a2a3053ead3 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Fri, 2 Aug 2019 10:43:59 +0200 Subject: [PATCH] add MAC NR PDU packing and associated unit tests --- lib/include/srslte/common/mac_nr_pdu.h | 118 +++++++++ lib/src/common/mac_nr_pdu.cc | 275 +++++++++++++++++++++ lib/test/common/CMakeLists.txt | 4 + lib/test/common/mac_nr_pdu_test.cc | 330 +++++++++++++++++++++++++ 4 files changed, 727 insertions(+) create mode 100644 lib/include/srslte/common/mac_nr_pdu.h create mode 100644 lib/src/common/mac_nr_pdu.cc create mode 100644 lib/test/common/mac_nr_pdu_test.cc diff --git a/lib/include/srslte/common/mac_nr_pdu.h b/lib/include/srslte/common/mac_nr_pdu.h new file mode 100644 index 000000000..f7363785f --- /dev/null +++ b/lib/include/srslte/common/mac_nr_pdu.h @@ -0,0 +1,118 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE 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. + * + * srsLTE 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/. + * + */ + +#ifndef SRSLTE_MAC_NR_PDU_H +#define SRSLTE_MAC_NR_PDU_H + +#include "srslte/common/common.h" +#include +#include +#include + +namespace srslte { + +class mac_nr_sch_pdu; + +class mac_nr_sch_subpdu +{ +public: + // 3GPP 38.321 v15.3.0 Combined Tables 6.2.1-1, 6.2.1-2 + typedef enum { + // Values for DL-SCH + DRX_CMD = 0b111100, + TA_CMD = 0b111101, + CON_RES_ID = 0b111110, + + // Values for UL-SCH + CRNTI = 0b111010, + SHORT_TRUNC_BSR = 0b111011, + LONG_TRUNC_BSR = 0b111100, + + SHORT_BSR = 0b111101, + LONG_BSR = 0b111110, + + // Common + CCCH = 0b000000, + PADDING = 0b111111, + } nr_lcid_sch_t; + + mac_nr_sch_subpdu(mac_nr_sch_pdu* parent_); + + nr_lcid_sch_t get_type(); + bool is_sdu(); + bool is_var_len_ce(); + + uint32_t read_subheader(const uint8_t* ptr); + uint32_t get_total_length(); + uint32_t get_sdu_length(); + uint32_t get_lcid(); + uint8_t* get_sdu(); + + void set_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_); + + void set_padding(const uint32_t len_); + + uint32_t write_subpdu(const uint8_t* start_); + +private: + uint32_t sizeof_ce(uint32_t lcid, bool is_ul); + + // protected: + uint32_t lcid = 0; + int header_length = 0; + int sdu_length = 0; + bool F_bit = false; + uint8_t* sdu = nullptr; + + mac_nr_sch_pdu* parent = nullptr; +}; + +class mac_nr_sch_pdu +{ +public: + mac_nr_sch_pdu(bool ulsch_ = false) : ulsch(ulsch_) {} + + void pack(); + void unpack(const uint8_t* payload, const uint32_t& len); + uint32_t get_num_subpdus(); + const mac_nr_sch_subpdu& get_subpdu(const uint32_t& index); + bool is_ulsch(); + + void init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_, bool is_ulsch_ = false); + + uint32_t add_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_); + + uint32_t get_remaing_len(); + +private: + uint32_t size_header_sdu(const uint32_t nbytes); + + bool ulsch = false; + std::vector subpdus; + + byte_buffer_t* buffer = nullptr; + uint32_t pdu_len = 0; + uint32_t remaining_len = 0; +}; + +} // namespace srslte + +#endif // SRSLTE_MAC_NR_PDU_H diff --git a/lib/src/common/mac_nr_pdu.cc b/lib/src/common/mac_nr_pdu.cc new file mode 100644 index 000000000..8b0878709 --- /dev/null +++ b/lib/src/common/mac_nr_pdu.cc @@ -0,0 +1,275 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE 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. + * + * srsLTE 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 "srslte/common/mac_nr_pdu.h" + +namespace srslte { + +mac_nr_sch_subpdu::mac_nr_sch_subpdu(mac_nr_sch_pdu* parent_) : parent(parent_) {} + +mac_nr_sch_subpdu::nr_lcid_sch_t mac_nr_sch_subpdu::get_type() +{ + if (lcid >= 32) { + return (nr_lcid_sch_t)lcid; + } + + return CCCH; +} + +bool mac_nr_sch_subpdu::is_sdu() +{ + return get_type() == CCCH; +} + +bool mac_nr_sch_subpdu::is_var_len_ce() +{ + return false; +} + +// return length of PDU +uint32_t mac_nr_sch_subpdu::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_sdu() || is_var_len_ce()) { + // 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; + return header_length; +} + +void mac_nr_sch_subpdu::set_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_) +{ + lcid = lcid_; + sdu = const_cast(payload_); + sdu_length = len_; + header_length = 2; + + if (sdu_length >= 256) { + F_bit = true; + header_length += 1; + } +} + +void mac_nr_sch_subpdu::set_padding(const uint32_t len_) +{ + lcid = PADDING; + // 1 Byte R/LCID MAC subheader + sdu_length = len_ - 1; + header_length = 1; +} + +// Section 6.1.2 +uint32_t mac_nr_sch_subpdu::write_subpdu(const uint8_t* start_) +{ + uint8_t* ptr = const_cast(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(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(sdu_length); + ptr += 1; + } else if (header_length == 1) { + // do nothing + } else { + fprintf(stderr, "Error while packing PDU. Unsupported header length (%d)\n", 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_nr_sch_subpdu::get_total_length() +{ + return (header_length + sdu_length); +} + +uint32_t mac_nr_sch_subpdu::get_sdu_length() +{ + return sdu_length; +} + +uint32_t mac_nr_sch_subpdu::get_lcid() +{ + return lcid; +} + +uint8_t* mac_nr_sch_subpdu::get_sdu() +{ + return sdu; +} + +uint32_t mac_nr_sch_subpdu::sizeof_ce(uint32_t lcid, bool is_ul) +{ + if (is_ul) { + switch (lcid) { + case CRNTI: + return 2; + case SHORT_TRUNC_BSR: + return 1; + case SHORT_BSR: + return 1; + 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; +} + +void mac_nr_sch_pdu::pack() +{ + // SDUs are written in place, only add padding if needed + if (remaining_len) { + mac_nr_sch_subpdu 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_nr_sch_pdu::unpack(const uint8_t* payload, const uint32_t& len) +{ + uint32_t offset = 0; + while (offset < len) { + mac_nr_sch_subpdu sch_pdu(this); + sch_pdu.read_subheader(payload + offset); + offset += sch_pdu.get_total_length(); + 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_nr_sch_pdu::get_num_subpdus() +{ + return subpdus.size(); +} + +const mac_nr_sch_subpdu& mac_nr_sch_pdu::get_subpdu(const uint32_t& index) +{ + return subpdus.at(index); +} + +bool mac_nr_sch_pdu::is_ulsch() +{ + return ulsch; +} + +void mac_nr_sch_pdu::init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_, bool ulsch_) +{ + buffer = buffer_; + pdu_len = pdu_len_; + remaining_len = pdu_len_; + ulsch = ulsch_; +} + +uint32_t mac_nr_sch_pdu::size_header_sdu(const uint32_t nbytes) +{ + if (nbytes < 256) { + return 2; + } else { + return 3; + } +} + +uint32_t mac_nr_sch_pdu::get_remaing_len() +{ + return remaining_len; +} + +uint32_t mac_nr_sch_pdu::add_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_) +{ + int header_size = size_header_sdu(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_nr_sch_subpdu sch_pdu(this); + sch_pdu.set_sdu(lcid_, payload_, len_); + uint32_t length = sch_pdu.write_subpdu(buffer->msg + buffer->N_bytes); + + if (length != sch_pdu.get_total_length()) { + fprintf(stderr, "Error writing subPDU (Length error: %d != %d)\n", length, sch_pdu.get_total_length()); + return SRSLTE_ERROR; + } + + // update length and advance payload pointer + buffer->N_bytes += length; + remaining_len -= length; + + subpdus.push_back(sch_pdu); + + return SRSLTE_SUCCESS; +} + +} // namespace srslte diff --git a/lib/test/common/CMakeLists.txt b/lib/test/common/CMakeLists.txt index ca9edf4c3..ec68d1782 100644 --- a/lib/test/common/CMakeLists.txt +++ b/lib/test/common/CMakeLists.txt @@ -65,4 +65,8 @@ add_executable(pdu_test pdu_test.cc) target_link_libraries(pdu_test srslte_phy srslte_common ${CMAKE_THREAD_LIBS_INIT}) add_test(pdu_test pdu_test) +add_executable(mac_nr_pdu_test mac_nr_pdu_test.cc) +target_link_libraries(mac_nr_pdu_test srslte_phy srslte_common ${CMAKE_THREAD_LIBS_INIT}) +add_test(mac_nr_pdu_test mac_nr_pdu_test) + add_executable(stack_procedure_test stack_procedure_test.cc) diff --git a/lib/test/common/mac_nr_pdu_test.cc b/lib/test/common/mac_nr_pdu_test.cc new file mode 100644 index 000000000..e79e0dac0 --- /dev/null +++ b/lib/test/common/mac_nr_pdu_test.cc @@ -0,0 +1,330 @@ +/* + * Copyright 2013-2019 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE 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. + * + * srsLTE 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 "srslte/common/log_filter.h" +#include "srslte/common/mac_nr_pcap.h" +#include "srslte/common/mac_nr_pdu.h" +#include "srslte/config.h" + +#include +#include +#include +#include + +#define TESTASSERT(cond) \ + { \ + if (!(cond)) { \ + std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \ + return -1; \ + } \ + } + +#define PCAP 0 +#define PCAP_CRNTI (0x1001) +#define PCAP_TTI (666) + +using namespace srslte; + +static std::unique_ptr pcap_handle = nullptr; + +int mac_dl_sch_pdu_unpack_and_pack_test1() +{ + // MAC PDU with DL-SCH subheader with 8-bit length field + // Bit 1-8 + // | | | | | | | | | + // | R |F=0| LCID | Octet 1 + // | L | Octet 1 + + // TV1 - MAC PDU with short subheader for CCCH, MAC SDU length is 8 B, total PDU is 10 B + uint8_t mac_dl_sch_pdu_1[] = {0x00, 0x08, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + + if (pcap_handle) { + pcap_handle->write_dl_crnti(mac_dl_sch_pdu_1, sizeof(mac_dl_sch_pdu_1), PCAP_CRNTI, true, PCAP_TTI); + } + + srslte::mac_nr_sch_pdu pdu; + pdu.unpack(mac_dl_sch_pdu_1, sizeof(mac_dl_sch_pdu_1)); + TESTASSERT(pdu.get_num_subpdus() == 1); + + mac_nr_sch_subpdu subpdu = pdu.get_subpdu(0); + TESTASSERT(subpdu.get_total_length() == 10); + TESTASSERT(subpdu.get_sdu_length() == 8); + TESTASSERT(subpdu.get_lcid() == 0); + + // pack PDU again + byte_buffer_t tx_buffer; + + srslte::mac_nr_sch_pdu tx_pdu; + tx_pdu.init_tx(&tx_buffer, sizeof(mac_dl_sch_pdu_1)); + + // Add SDU part of TV from above + tx_pdu.add_sdu(0, &mac_dl_sch_pdu_1[2], 8); + + TESTASSERT(tx_pdu.get_remaing_len() == 0); + TESTASSERT(tx_buffer.N_bytes == sizeof(mac_dl_sch_pdu_1)); + TESTASSERT(memcmp(tx_buffer.msg, mac_dl_sch_pdu_1, tx_buffer.N_bytes) == 0); + + if (pcap_handle) { + pcap_handle->write_dl_crnti(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); + } + + srslte::log_filter log("MAC"); + log.set_level(srslte::LOG_LEVEL_DEBUG); + log.set_hex_limit(100000); + log.info_hex(tx_buffer.msg, tx_buffer.N_bytes, "Generated MAC PDU (%d B)\n", tx_buffer.N_bytes); + + return SRSLTE_SUCCESS; +} + +int mac_dl_sch_pdu_unpack_test2() +{ + // MAC PDU with DL-SCH subheader with 16-bit length field + // Bit 1-8 + // | | | | | | | | | + // | R |F=1| LCID | Octet 1 + // | L | Octet 2 + // | L | Octet 3 + + // Note: Pack test is not working in this case as it would generate a PDU with the short length field only + + // TV2 - MAC PDU with long subheader for LCID=2, MAC SDU length is 8 B, total PDU is 11 B + uint8_t mac_dl_sch_pdu_2[] = {0x42, 0x00, 0x08, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + + if (pcap_handle) { + pcap_handle->write_dl_crnti(mac_dl_sch_pdu_2, sizeof(mac_dl_sch_pdu_2), PCAP_CRNTI, true, PCAP_TTI); + } + + srslte::mac_nr_sch_pdu pdu; + pdu.unpack(mac_dl_sch_pdu_2, sizeof(mac_dl_sch_pdu_2)); + TESTASSERT(pdu.get_num_subpdus() == 1); + mac_nr_sch_subpdu subpdu = pdu.get_subpdu(0); + TESTASSERT(subpdu.get_total_length() == 11); + TESTASSERT(subpdu.get_sdu_length() == 8); + TESTASSERT(subpdu.get_lcid() == 2); + + return SRSLTE_SUCCESS; +} + +int mac_dl_sch_pdu_pack_test3() +{ + // MAC PDU with DL-SCH subheader with 16-bit length field (512 B SDU + 3 B header) + // Bit 1-8 + // | | | | | | | | | + // | R |F=1| LCID | Octet 1 + // | L | Octet 2 + // | L | Octet 3 + + uint8_t sdu[512] = {}; + + // populate SDU payload + for (uint32_t i = 0; i < 512; i++) { + sdu[i] = i % 256; + } + + // pack buffer + byte_buffer_t tx_buffer; + + srslte::mac_nr_sch_pdu tx_pdu; + tx_pdu.init_tx(&tx_buffer, 1024); + + // Add SDU + tx_pdu.add_sdu(4, sdu, sizeof(sdu)); + + TESTASSERT(tx_pdu.get_remaing_len() == 509); + TESTASSERT(tx_buffer.N_bytes == 515); + + if (pcap_handle) { + pcap_handle->write_dl_crnti(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); + } + + srslte::log_filter log("MAC"); + log.set_level(srslte::LOG_LEVEL_DEBUG); + log.set_hex_limit(100000); + log.info_hex(tx_buffer.msg, tx_buffer.N_bytes, "Generated MAC PDU (%d B)\n", tx_buffer.N_bytes); + + return SRSLTE_SUCCESS; +} + +int mac_dl_sch_pdu_pack_test4() +{ + // MAC PDU with only padding + // Bit 1-8 + // | | | | | | | | | + // | R | R | LCID | Octet 1 + // | zeros | Octet .. + + // TV2 - MAC PDU with a single LCID with padding only + uint8_t tv[] = {0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + const uint32_t pdu_size = 8; + byte_buffer_t tx_buffer; + + // modify buffer (to be nulled during PDU packing + tx_buffer.msg[4] = 0xaa; + + srslte::mac_nr_sch_pdu tx_pdu; + tx_pdu.init_tx(&tx_buffer, pdu_size); + + TESTASSERT(tx_pdu.get_remaing_len() == pdu_size); + tx_pdu.pack(); + TESTASSERT(tx_buffer.N_bytes == pdu_size); + TESTASSERT(tx_buffer.N_bytes == sizeof(tv)); + + TESTASSERT(memcmp(tx_buffer.msg, tv, tx_buffer.N_bytes) == 0); + + if (pcap_handle) { + pcap_handle->write_dl_crnti(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); + } + + srslte::log_filter log("MAC"); + log.set_level(srslte::LOG_LEVEL_DEBUG); + log.set_hex_limit(100000); + log.info_hex(tx_buffer.msg, tx_buffer.N_bytes, "Generated MAC PDU (%d B)\n", tx_buffer.N_bytes); + + return SRSLTE_SUCCESS; +} + +int mac_dl_sch_pdu_pack_test5() +{ + // MAC PDU with DL-SCH subheader with 8-bit length field and padding after that + // Bit 1-8 + // | | | | | | | | | + // | R |F=0| LCID | Octet 1 + // | L | Octet 2 + // | zeros | Octet 3-10 + // | R | R | LCID | Octet 11 + // | zeros | Octet .. + + // TV2 - MAC PDU with a single LCID with padding only + uint8_t tv[] = {0x01, 0x08, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00}; + + const uint32_t pdu_size = 16; + byte_buffer_t tx_buffer; + tx_buffer.clear(); + + srslte::mac_nr_sch_pdu tx_pdu; + tx_pdu.init_tx(&tx_buffer, pdu_size); + + // Add SDU part of TV from above + tx_pdu.add_sdu(1, &tv[2], 8); + + TESTASSERT(tx_pdu.get_remaing_len() == 6); + + tx_pdu.pack(); + + TESTASSERT(tx_pdu.get_remaing_len() == 0); + TESTASSERT(tx_buffer.N_bytes == pdu_size); + TESTASSERT(tx_buffer.N_bytes == sizeof(tv)); + TESTASSERT(memcmp(tx_buffer.msg, tv, tx_buffer.N_bytes) == 0); + + if (pcap_handle) { + pcap_handle->write_dl_crnti(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); + } + + srslte::log_filter log("MAC"); + log.set_level(srslte::LOG_LEVEL_DEBUG); + log.set_hex_limit(100000); + log.info_hex(tx_buffer.msg, tx_buffer.N_bytes, "Generated MAC PDU (%d B)\n", tx_buffer.N_bytes); + + return SRSLTE_SUCCESS; +} + +int mac_ul_sch_pdu_unpack_test1() +{ + // UL-SCH MAC PDU with fixed-size CE and DL-SCH subheader with 16-bit length field + // Note: this is a malformed UL-SCH PDU has SDU is placed after the CE + // Bit 1-8 + // | | | | | | | | | + // | R | R | LCID | Octet 1 (C-RNTI LCID = 0x ) + // | C-RNTI | Octet 2 + // | C-RNTI | Octet 3 + // | R |F=1| LCID | Octet 4 + // | L | Octet 5 + // | L | Octet 6 + // | data | Octet 7-x + + // TV3 - UL-SCH MAC PDU with long subheader for LCID=2, MAC SDU length is 4 B, total PDU is 9 B + const uint8_t ul_sch_crnti[] = {0x11, 0x22}; + uint8_t mac_ul_sch_pdu_1[] = {0x3a, 0x11, 0x22, 0x43, 0x00, 0x04, 0x11, 0x22, 0x33, 0x44}; + + if (pcap_handle) { + pcap_handle->write_ul_crnti(mac_ul_sch_pdu_1, sizeof(mac_ul_sch_pdu_1), PCAP_CRNTI, true, PCAP_TTI); + } + + srslte::mac_nr_sch_pdu pdu(true); + pdu.unpack(mac_ul_sch_pdu_1, sizeof(mac_ul_sch_pdu_1)); + TESTASSERT(pdu.get_num_subpdus() == 2); + + // First subpdu is C-RNTI CE + mac_nr_sch_subpdu subpdu0 = pdu.get_subpdu(0); + TESTASSERT(subpdu0.get_total_length() == 3); + TESTASSERT(subpdu0.get_sdu_length() == 2); + TESTASSERT(subpdu0.get_lcid() == mac_nr_sch_subpdu::CRNTI); + TESTASSERT(memcmp(subpdu0.get_sdu(), (uint8_t*)&ul_sch_crnti, sizeof(ul_sch_crnti)) == 0); + + // Second subpdu is UL-SCH + mac_nr_sch_subpdu subpdu1 = pdu.get_subpdu(1); + TESTASSERT(subpdu1.get_total_length() == 7); + TESTASSERT(subpdu1.get_sdu_length() == 4); + TESTASSERT(subpdu1.get_lcid() == 3); + + return SRSLTE_SUCCESS; +} + +int main(int argc, char** argv) +{ +#if PCAP + pcap_handle = std::unique_ptr(new srslte::mac_nr_pcap()); + pcap_handle->open("mac_nr_pdu_test.pcap"); +#endif + + if (mac_dl_sch_pdu_unpack_and_pack_test1()) { + fprintf(stderr, "mac_dl_sch_pdu_unpack_and_pack_test1() failed.\n"); + return SRSLTE_ERROR; + } + + if (mac_dl_sch_pdu_unpack_test2()) { + fprintf(stderr, "mac_dl_sch_pdu_unpack_test2() failed.\n"); + return SRSLTE_ERROR; + } + + if (mac_dl_sch_pdu_pack_test3()) { + fprintf(stderr, "mac_dl_sch_pdu_pack_test3() failed.\n"); + return SRSLTE_ERROR; + } + + if (mac_dl_sch_pdu_pack_test4()) { + fprintf(stderr, "mac_dl_sch_pdu_pack_test4() failed.\n"); + return SRSLTE_ERROR; + } + + if (mac_dl_sch_pdu_pack_test5()) { + fprintf(stderr, "mac_dl_sch_pdu_pack_test5() failed.\n"); + return SRSLTE_ERROR; + } + + if (mac_ul_sch_pdu_unpack_test1()) { + fprintf(stderr, "mac_ul_sch_pdu_unpack_test1() failed.\n"); + return SRSLTE_ERROR; + } + + return SRSLTE_SUCCESS; +}