mirror of https://github.com/PentHertz/srsLTE.git
Merge pull request #191 from softwareradiosystems/rlc_updates
RLC updates
This commit is contained in:
commit
d51bec49bf
|
@ -34,6 +34,7 @@
|
|||
|
||||
#define MAC_LTE_DLT 147
|
||||
#define NAS_LTE_DLT 148
|
||||
#define RLC_LTE_DLT 149 // UDP needs to be selected as protocol
|
||||
|
||||
|
||||
/* This structure gets written to the start of the file */
|
||||
|
@ -104,6 +105,67 @@ typedef struct NAS_Context_Info_s {
|
|||
} NAS_Context_Info_t;
|
||||
|
||||
|
||||
/* RLC-LTE disector */
|
||||
|
||||
/* rlcMode */
|
||||
#define RLC_TM_MODE 1
|
||||
#define RLC_UM_MODE 2
|
||||
#define RLC_AM_MODE 4
|
||||
#define RLC_PREDEF 8
|
||||
|
||||
/* priority ? */
|
||||
|
||||
/* channelType */
|
||||
#define CHANNEL_TYPE_CCCH 1
|
||||
#define CHANNEL_TYPE_BCCH_BCH 2
|
||||
#define CHANNEL_TYPE_PCCH 3
|
||||
#define CHANNEL_TYPE_SRB 4
|
||||
#define CHANNEL_TYPE_DRB 5
|
||||
#define CHANNEL_TYPE_BCCH_DL_SCH 6
|
||||
#define CHANNEL_TYPE_MCCH 7
|
||||
#define CHANNEL_TYPE_MTCH 8
|
||||
|
||||
/* sequenceNumberLength */
|
||||
#define UM_SN_LENGTH_5_BITS 5
|
||||
#define UM_SN_LENGTH_10_BITS 10
|
||||
#define AM_SN_LENGTH_10_BITS 10
|
||||
#define AM_SN_LENGTH_16_BITS 16
|
||||
|
||||
/* Narrow band mode */
|
||||
typedef enum {
|
||||
rlc_no_nb_mode = 0,
|
||||
rlc_nb_mode = 1
|
||||
} rlc_lte_nb_mode;
|
||||
|
||||
/* Context information for every RLC PDU that will be logged */
|
||||
typedef struct {
|
||||
unsigned char rlcMode;
|
||||
unsigned char direction;
|
||||
unsigned char priority;
|
||||
unsigned char sequenceNumberLength;
|
||||
unsigned short ueid;
|
||||
unsigned short channelType;
|
||||
unsigned short channelId; /* for SRB: 1=SRB1, 2=SRB2, 3=SRB1bis; for DRB: DRB ID */
|
||||
unsigned short pduLength;
|
||||
bool extendedLiField;
|
||||
rlc_lte_nb_mode nbMode;
|
||||
} RLC_Context_Info_t;
|
||||
|
||||
|
||||
// See Wireshark's packet-rlc-lte.h for details
|
||||
#define RLC_LTE_START_STRING "rlc-lte"
|
||||
#define RLC_LTE_SN_LENGTH_TAG 0x02
|
||||
#define RLC_LTE_DIRECTION_TAG 0x03
|
||||
#define RLC_LTE_PRIORITY_TAG 0x04
|
||||
#define RLC_LTE_UEID_TAG 0x05
|
||||
#define RLC_LTE_CHANNEL_TYPE_TAG 0x06
|
||||
#define RLC_LTE_CHANNEL_ID_TAG 0x07
|
||||
#define RLC_LTE_EXT_LI_FIELD_TAG 0x08
|
||||
#define RLC_LTE_NB_MODE_TAG 0x09
|
||||
#define RLC_LTE_PAYLOAD_TAG 0x01
|
||||
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* API functions for opening/closing LTE PCAP files *
|
||||
**************************************************************************/
|
||||
|
@ -247,4 +309,93 @@ inline int LTE_PCAP_NAS_WritePDU(FILE *fd, NAS_Context_Info_t *context,
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* API functions for writing RLC-LTE PCAP files *
|
||||
**************************************************************************/
|
||||
|
||||
/* Write an individual RLC PDU (PCAP packet header + UDP header + rlc-context + rlc-pdu) */
|
||||
inline int LTE_PCAP_RLC_WritePDU(FILE *fd, RLC_Context_Info_t *context,
|
||||
const unsigned char *PDU, unsigned int length)
|
||||
{
|
||||
pcaprec_hdr_t packet_header;
|
||||
char context_header[256];
|
||||
int offset = 0;
|
||||
uint16_t tmp16;
|
||||
|
||||
/* Can't write if file wasn't successfully opened */
|
||||
if (fd == NULL) {
|
||||
printf("Error: Can't write to empty file handle\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
|
||||
// Add dummy UDP header, start with src and dest port
|
||||
context_header[offset++] = 0xde;
|
||||
context_header[offset++] = 0xad;
|
||||
context_header[offset++] = 0xbe;
|
||||
context_header[offset++] = 0xef;
|
||||
// length
|
||||
tmp16 = length + 12;
|
||||
memcpy(context_header+offset, &tmp16, 2);
|
||||
offset += 2;
|
||||
// dummy CRC
|
||||
context_header[offset++] = 0xde;
|
||||
context_header[offset++] = 0xad;
|
||||
|
||||
// Start magic string
|
||||
memcpy(&context_header[offset], RLC_LTE_START_STRING, strlen(RLC_LTE_START_STRING));
|
||||
offset += strlen(RLC_LTE_START_STRING);
|
||||
|
||||
// Fixed field RLC mode
|
||||
context_header[offset++] = context->rlcMode;
|
||||
|
||||
// Conditional fields
|
||||
if (context->rlcMode == RLC_UM_MODE) {
|
||||
context_header[offset++] = RLC_LTE_SN_LENGTH_TAG;
|
||||
context_header[offset++] = context->sequenceNumberLength;
|
||||
}
|
||||
|
||||
// Optional fields
|
||||
context_header[offset++] = RLC_LTE_DIRECTION_TAG;
|
||||
context_header[offset++] = context->direction;
|
||||
|
||||
context_header[offset++] = RLC_LTE_PRIORITY_TAG;
|
||||
context_header[offset++] = context->priority;
|
||||
|
||||
context_header[offset++] = RLC_LTE_UEID_TAG;
|
||||
tmp16 = htons(context->ueid);
|
||||
memcpy(context_header+offset, &tmp16, 2);
|
||||
offset += 2;
|
||||
|
||||
context_header[offset++] = RLC_LTE_CHANNEL_TYPE_TAG;
|
||||
tmp16 = htons(context->channelType);
|
||||
memcpy(context_header+offset, &tmp16, 2);
|
||||
offset += 2;
|
||||
|
||||
context_header[offset++] = RLC_LTE_CHANNEL_ID_TAG;
|
||||
tmp16 = htons(context->channelId);
|
||||
memcpy(context_header+offset, &tmp16, 2);
|
||||
offset += 2;
|
||||
|
||||
// Now the actual PDU
|
||||
context_header[offset++] = RLC_LTE_PAYLOAD_TAG;
|
||||
|
||||
// PCAP header
|
||||
struct timeval t;
|
||||
gettimeofday(&t, NULL);
|
||||
packet_header.ts_sec = t.tv_sec;
|
||||
packet_header.ts_usec = t.tv_usec;
|
||||
packet_header.incl_len = offset + length;
|
||||
packet_header.orig_len = offset + length;
|
||||
|
||||
// Write everything to file
|
||||
fwrite(&packet_header, sizeof(pcaprec_hdr_t), 1, fd);
|
||||
fwrite(context_header, 1, offset, fd);
|
||||
fwrite(PDU, 1, length, fd);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif // SRSLTE_PCAP_H
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2015 Software Radio Systems Limited
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* This file is part of the srsUE library.
|
||||
*
|
||||
* srsUE 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.
|
||||
*
|
||||
* srsUE 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 RLCPCAP_H
|
||||
#define RLCPCAP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "srslte/common/pcap.h"
|
||||
|
||||
namespace srslte {
|
||||
|
||||
class rlc_pcap
|
||||
{
|
||||
public:
|
||||
rlc_pcap() {enable_write=false; ue_id=0; pcap_file = NULL; };
|
||||
void enable(bool en);
|
||||
void open(const char *filename, uint32_t ue_id = 0);
|
||||
void close();
|
||||
|
||||
void set_ue_id(uint16_t ue_id);
|
||||
|
||||
void write_dl_am_ccch(uint8_t* pdu, uint32_t pdu_len_bytes);
|
||||
void write_ul_am_ccch(uint8_t* pdu, uint32_t pdu_len_bytes);
|
||||
|
||||
private:
|
||||
bool enable_write;
|
||||
FILE *pcap_file;
|
||||
uint32_t ue_id;
|
||||
void pack_and_write(uint8_t* pdu,
|
||||
uint32_t pdu_len_bytes,
|
||||
uint8_t mode,
|
||||
uint8_t direction,
|
||||
uint8_t priority,
|
||||
uint8_t seqnumberlength,
|
||||
uint16_t ueid,
|
||||
uint16_t channel_type,
|
||||
uint16_t channel_id);
|
||||
};
|
||||
|
||||
} // namespace srsue
|
||||
|
||||
#endif // RLCPCAP_H
|
|
@ -217,8 +217,10 @@ bool rlc_am_is_control_pdu(byte_buffer_t *pdu);
|
|||
bool rlc_am_is_control_pdu(uint8_t *payload);
|
||||
bool rlc_am_is_pdu_segment(uint8_t *payload);
|
||||
std::string rlc_am_to_string(rlc_status_pdu_t *status);
|
||||
bool rlc_am_start_aligned(uint8_t fi);
|
||||
bool rlc_am_end_aligned(uint8_t fi);
|
||||
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 srslte
|
||||
|
||||
|
|
|
@ -83,6 +83,9 @@ public:
|
|||
srslte_rlc_am_config_t am;
|
||||
srslte_rlc_um_config_t um;
|
||||
|
||||
// Default ctor
|
||||
srslte_rlc_config_t(): rlc_mode(LIBLTE_RRC_RLC_MODE_AM), am(), um() {};
|
||||
|
||||
// Constructor based on liblte's RLC config
|
||||
srslte_rlc_config_t(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg) : rlc_mode(cnfg->rlc_mode), am(), um()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
*
|
||||
* \section COPYRIGHT
|
||||
*
|
||||
* Copyright 2013-2015 Software Radio Systems Limited
|
||||
*
|
||||
* \section LICENSE
|
||||
*
|
||||
* This file is part of the srsUE library.
|
||||
*
|
||||
* srsUE 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.
|
||||
*
|
||||
* srsUE 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 <stdint.h>
|
||||
#include "srslte/srslte.h"
|
||||
#include "srslte/common/pcap.h"
|
||||
#include "srslte/common/rlc_pcap.h"
|
||||
|
||||
namespace srslte {
|
||||
|
||||
void rlc_pcap::enable(bool en)
|
||||
{
|
||||
enable_write = true;
|
||||
}
|
||||
void rlc_pcap::open(const char* filename, uint32_t ue_id)
|
||||
{
|
||||
fprintf(stdout, "Opening RLC PCAP with DLT=%d\n", RLC_LTE_DLT);
|
||||
pcap_file = LTE_PCAP_Open(RLC_LTE_DLT, filename);
|
||||
this->ue_id = ue_id;
|
||||
enable_write = true;
|
||||
}
|
||||
void rlc_pcap::close()
|
||||
{
|
||||
fprintf(stdout, "Saving RLC PCAP file\n");
|
||||
LTE_PCAP_Close(pcap_file);
|
||||
}
|
||||
|
||||
void rlc_pcap::set_ue_id(uint16_t ue_id) {
|
||||
this->ue_id = ue_id;
|
||||
}
|
||||
|
||||
void rlc_pcap::pack_and_write(uint8_t* pdu, uint32_t pdu_len_bytes, uint8_t mode, uint8_t direction, uint8_t priority, uint8_t seqnumberlength, uint16_t ueid, uint16_t channel_type, uint16_t channel_id)
|
||||
{
|
||||
if (enable_write) {
|
||||
RLC_Context_Info_t context;
|
||||
context.rlcMode = mode;
|
||||
context.direction = direction;
|
||||
context.priority = priority;
|
||||
context.sequenceNumberLength = seqnumberlength;
|
||||
context.ueid = ueid;
|
||||
context.channelType = channel_type;
|
||||
context.channelId = channel_id;
|
||||
context.pduLength = pdu_len_bytes;
|
||||
if (pdu) {
|
||||
LTE_PCAP_RLC_WritePDU(pcap_file, &context, pdu, pdu_len_bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rlc_pcap::write_dl_am_ccch(uint8_t* pdu, uint32_t pdu_len_bytes)
|
||||
{
|
||||
uint8_t priority = 0;
|
||||
uint8_t seqnumberlength = 0; // normal length of 10bit
|
||||
uint8_t channel_id = 0;
|
||||
pack_and_write(pdu, pdu_len_bytes, RLC_AM_MODE, DIRECTION_DOWNLINK, priority, seqnumberlength, ue_id, CHANNEL_TYPE_CCCH, channel_id);
|
||||
}
|
||||
|
||||
void rlc_pcap::write_ul_am_ccch(uint8_t* pdu, uint32_t pdu_len_bytes)
|
||||
{
|
||||
uint8_t priority = 0;
|
||||
uint8_t seqnumberlength = 0; // normal length of 10bit
|
||||
uint8_t channel_id = 0;
|
||||
pack_and_write(pdu, pdu_len_bytes, RLC_AM_MODE, DIRECTION_UPLINK, priority, seqnumberlength, ue_id, CHANNEL_TYPE_CCCH, channel_id);
|
||||
}
|
||||
|
||||
}
|
|
@ -670,6 +670,13 @@ int rlc_am::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_amd_retx_t r
|
|||
lower += old_header.li[i];
|
||||
}
|
||||
|
||||
// Make sure LI is not deleted in case the SDU boundary is crossed
|
||||
// FIXME: fix if N_li > 1
|
||||
if (new_header.N_li == 1 && retx.so_start + new_header.li[0] < retx.so_end && retx.so_end <= retx.so_start + pdu_space) {
|
||||
// This segment crosses a SDU boundary
|
||||
new_header.N_li++;
|
||||
}
|
||||
|
||||
// Update retx_queue
|
||||
if(tx_window[retx.sn].buf->N_bytes == retx.so_end) {
|
||||
retx_queue.pop_front();
|
||||
|
@ -913,6 +920,13 @@ void rlc_am::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rlc_amd_pdu_h
|
|||
#endif
|
||||
}
|
||||
|
||||
// check available space for payload
|
||||
if (nof_bytes > pdu.buf->get_tailroom()) {
|
||||
log->error("%s Discarding SN: %d of size %d B (available space %d B)\n",
|
||||
rrc->get_rb_name(lcid).c_str(), header.sn, nof_bytes, pdu.buf->get_tailroom());
|
||||
pool->deallocate(pdu.buf);
|
||||
return;
|
||||
}
|
||||
memcpy(pdu.buf->msg, payload, nof_bytes);
|
||||
pdu.buf->N_bytes = nof_bytes;
|
||||
memcpy(&pdu.header, &header, sizeof(rlc_amd_pdu_header_t));
|
||||
|
@ -1173,6 +1187,11 @@ void rlc_am::reassemble_rx_sdus()
|
|||
for(uint32_t i=0; i<rx_window[vr_r].header.N_li; i++)
|
||||
{
|
||||
uint32_t len = rx_window[vr_r].header.li[i];
|
||||
// sanity check to avoid zero-size SDUs
|
||||
if (len == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (rx_sdu->get_tailroom() >= len) {
|
||||
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_r].buf->msg, len);
|
||||
rx_sdu->N_bytes += len;
|
||||
|
@ -1345,7 +1364,13 @@ bool rlc_am::add_segment_and_check(rlc_amd_rx_pdu_segments_t *pdu, rlc_amd_rx_pd
|
|||
count += it->header.li[i];
|
||||
}
|
||||
}
|
||||
carryover = it->buf->N_bytes - count;
|
||||
|
||||
// accumulate segment sizes until end aligned PDU is received
|
||||
if (rlc_am_not_start_aligned(it->header.fi)) {
|
||||
carryover += it->buf->N_bytes - count;
|
||||
} else {
|
||||
carryover = it->buf->N_bytes - count;
|
||||
}
|
||||
tmpit = it;
|
||||
if(rlc_am_end_aligned(it->header.fi) && ++tmpit != pdu->segments.end()) {
|
||||
header.li[header.N_li++] = carryover;
|
||||
|
@ -1741,14 +1766,24 @@ std::string rlc_am_to_string(rlc_status_pdu_t *status)
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
bool rlc_am_start_aligned(uint8_t fi)
|
||||
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(uint8_t fi)
|
||||
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 srsue
|
||||
|
|
|
@ -52,7 +52,7 @@ void rlc_tm::init(srslte::log *log_,
|
|||
|
||||
void rlc_tm::configure(srslte_rlc_config_t cnfg)
|
||||
{
|
||||
log->error("Attempted to configure TM RLC entity");
|
||||
log->error("Attempted to configure TM RLC entity\n");
|
||||
}
|
||||
|
||||
void rlc_tm::empty_queue()
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
namespace srslte {
|
||||
|
||||
rlc_um::rlc_um() : tx_sdu_queue(16)
|
||||
rlc_um::rlc_um() : tx_sdu_queue(32)
|
||||
{
|
||||
log = NULL;
|
||||
pdcp = NULL;
|
||||
|
@ -467,6 +467,17 @@ void rlc_um::reassemble_rx_sdus()
|
|||
for(uint32_t i=0; i<rx_window[vr_ur].header.N_li; i++)
|
||||
{
|
||||
int len = rx_window[vr_ur].header.li[i];
|
||||
|
||||
// Check if we received a middle or end segment
|
||||
if (rx_sdu->N_bytes == 0 && i == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) {
|
||||
log->warning("Dropping PDU %d due to lost start segment\n", vr_ur);
|
||||
// Advance data pointers and continue with next segment
|
||||
rx_window[vr_ur].buf->msg += len;
|
||||
rx_window[vr_ur].buf->N_bytes -= len;
|
||||
rx_sdu->reset();
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len);
|
||||
rx_sdu->N_bytes += len;
|
||||
rx_window[vr_ur].buf->msg += len;
|
||||
|
@ -488,27 +499,30 @@ void rlc_um::reassemble_rx_sdus()
|
|||
}
|
||||
|
||||
// Handle last segment
|
||||
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes);
|
||||
rx_sdu->N_bytes += rx_window[vr_ur].buf->N_bytes;
|
||||
log->debug("Writting last segment in SDU buffer. Lower edge vr_ur=%d, Buffer size=%d, segment size=%d\n",
|
||||
vr_ur, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes);
|
||||
vr_ur_in_rx_sdu = vr_ur;
|
||||
if(rlc_um_end_aligned(rx_window[vr_ur].header.fi))
|
||||
{
|
||||
if(pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) {
|
||||
log->warning("Dropping remainder of lost PDU (lower edge last segments)\n");
|
||||
rx_sdu->reset();
|
||||
} else {
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (lower edge last segments)", rrc->get_rb_name(lcid).c_str(), vr_ur);
|
||||
rx_sdu->set_timestamp();
|
||||
pdcp->write_pdu(lcid, rx_sdu);
|
||||
rx_sdu = pool_allocate;
|
||||
if (!rx_sdu) {
|
||||
log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n");
|
||||
return;
|
||||
if (rx_sdu->N_bytes > 0 || rlc_um_start_aligned(rx_window[vr_ur].header.fi)) {
|
||||
log->debug("Writing last segment in SDU buffer. Lower edge vr_ur=%d, Buffer size=%d, segment size=%d\n",
|
||||
vr_ur, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes);
|
||||
|
||||
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes);
|
||||
rx_sdu->N_bytes += rx_window[vr_ur].buf->N_bytes;
|
||||
vr_ur_in_rx_sdu = vr_ur;
|
||||
if(rlc_um_end_aligned(rx_window[vr_ur].header.fi))
|
||||
{
|
||||
if(pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) {
|
||||
log->warning("Dropping remainder of lost PDU (lower edge last segments)\n");
|
||||
rx_sdu->reset();
|
||||
} else {
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (lower edge last segments)", rrc->get_rb_name(lcid).c_str(), vr_ur);
|
||||
rx_sdu->set_timestamp();
|
||||
pdcp->write_pdu(lcid, rx_sdu);
|
||||
rx_sdu = pool_allocate;
|
||||
if (!rx_sdu) {
|
||||
log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
pdu_lost = false;
|
||||
}
|
||||
pdu_lost = false;
|
||||
}
|
||||
|
||||
// Clean up rx_window
|
||||
|
@ -527,10 +541,21 @@ void rlc_um::reassemble_rx_sdus()
|
|||
for(uint32_t i=0; i<rx_window[vr_ur].header.N_li; i++)
|
||||
{
|
||||
int len = rx_window[vr_ur].header.li[i];
|
||||
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len);
|
||||
|
||||
// Check if the first part of the PDU is a middle or end segment
|
||||
if (rx_sdu->N_bytes == 0 && i == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) {
|
||||
log->warning("Dropping PDU %d due to lost start segment\n", vr_ur);
|
||||
// Advance data pointers and continue with next segment
|
||||
rx_window[vr_ur].buf->msg += len;
|
||||
rx_window[vr_ur].buf->N_bytes -= len;
|
||||
rx_sdu->reset();
|
||||
break;
|
||||
}
|
||||
|
||||
log->debug("Concatenating %d bytes in to current length %d. rx_window remaining bytes=%d, vr_ur_in_rx_sdu=%d, vr_ur=%d, rx_mod=%d, last_mod=%d\n",
|
||||
len, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes, vr_ur_in_rx_sdu, vr_ur, cfg.rx_mod, (vr_ur_in_rx_sdu+1)%cfg.rx_mod);
|
||||
rx_sdu->N_bytes += len;
|
||||
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len);
|
||||
rx_sdu->N_bytes += len;
|
||||
rx_window[vr_ur].buf->msg += len;
|
||||
rx_window[vr_ur].buf->N_bytes -= len;
|
||||
if((pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) || (vr_ur != ((vr_ur_in_rx_sdu+1)%cfg.rx_mod))) {
|
||||
|
@ -548,8 +573,14 @@ void rlc_um::reassemble_rx_sdus()
|
|||
}
|
||||
pdu_lost = false;
|
||||
}
|
||||
|
||||
|
||||
// Handle last segment
|
||||
if (rx_sdu->N_bytes == 0 && rx_window[vr_ur].header.N_li == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) {
|
||||
log->warning("Dropping PDU %d due to lost start segment\n", vr_ur);
|
||||
rx_sdu->reset();
|
||||
goto clean_up_rx_window;
|
||||
}
|
||||
|
||||
if (rx_sdu->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES &&
|
||||
rx_window[vr_ur].buf->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES &&
|
||||
rx_window[vr_ur].buf->N_bytes + rx_sdu->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES)
|
||||
|
@ -557,7 +588,7 @@ void rlc_um::reassemble_rx_sdus()
|
|||
|
||||
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes);
|
||||
rx_sdu->N_bytes += rx_window[vr_ur].buf->N_bytes;
|
||||
log->debug("Writting last segment in SDU buffer. Updating vr_ur=%d, Buffer size=%d, segment size=%d\n",
|
||||
log->debug("Writing last segment in SDU buffer. Updating vr_ur=%d, Buffer size=%d, segment size=%d\n",
|
||||
vr_ur, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes);
|
||||
} else {
|
||||
log->error("Out of bounds while reassembling SDU buffer in UM: sdu_len=%d, window_buffer_len=%d, vr_ur=%d\n",
|
||||
|
@ -582,6 +613,8 @@ void rlc_um::reassemble_rx_sdus()
|
|||
pdu_lost = false;
|
||||
}
|
||||
|
||||
clean_up_rx_window:
|
||||
|
||||
// Clean up rx_window
|
||||
pool->deallocate(rx_window[vr_ur].buf);
|
||||
rx_window.erase(vr_ur);
|
||||
|
|
|
@ -30,9 +30,11 @@ add_executable(rlc_am_test rlc_am_test.cc)
|
|||
target_link_libraries(rlc_am_test srslte_upper srslte_phy srslte_common)
|
||||
add_test(rlc_am_test rlc_am_test)
|
||||
|
||||
add_executable(rlc_am_stress_test rlc_am_stress_test.cc)
|
||||
target_link_libraries(rlc_am_stress_test srslte_upper srslte_phy srslte_common ${Boost_LIBRARIES})
|
||||
add_test(rlc_am_stress_test rlc_am_stress_test --duration 10)
|
||||
add_executable(rlc_stress_test rlc_stress_test.cc)
|
||||
target_link_libraries(rlc_stress_test srslte_upper srslte_phy srslte_common ${Boost_LIBRARIES})
|
||||
add_test(rlc_am_stress_test rlc_stress_test --mode=AM)
|
||||
add_test(rlc_um_stress_test rlc_stress_test --mode=UM)
|
||||
add_test(rlc_tm_stress_test rlc_stress_test --mode=TM --opp_sdu_ratio=1.0)
|
||||
|
||||
add_executable(rlc_um_data_test rlc_um_data_test.cc)
|
||||
target_link_libraries(rlc_um_data_test srslte_upper srslte_phy srslte_common)
|
||||
|
|
|
@ -40,6 +40,20 @@ uint32_t PDU2_LEN = 5;
|
|||
uint8_t pdu3[] = {0x8C, 0x00, 0xDD, 0xCD, 0xDC, 0x5D, 0xC0};
|
||||
uint32_t PDU3_LEN = 7;
|
||||
|
||||
// D/C = 1 = Data PDU
|
||||
// RF = 0 = AMD PDU
|
||||
// P = 0 = Status PDU is not requested
|
||||
// FI = 11 = First byte of the Data field does not corresponds to the first byte of a RLC SDU,
|
||||
// Last byte of the Data field does not corresponds to the last byte of a RLC SDU
|
||||
// E = 1 = A set of E field and LI field follows from the octet following the fixed part of the header
|
||||
// SN = 0000000010 -> SN 2
|
||||
// E = 1
|
||||
// LI1 = 1010011 1110 (1342 Dec)
|
||||
// E = 0
|
||||
// LI2 = 10111011100 (1500 Dec)
|
||||
uint8_t pdu4[] = {0x9C, 0x02, 0xD3, 0xE5, 0xDC };
|
||||
uint32_t PDU4_LEN = 5;
|
||||
|
||||
using namespace srslte;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
@ -106,4 +120,26 @@ int main(int argc, char **argv) {
|
|||
assert(b2.N_bytes == PDU3_LEN);
|
||||
for(uint32_t i=0;i<b2.N_bytes;i++)
|
||||
assert(b2.msg[i] == b1.msg[i]);
|
||||
|
||||
b1.reset();
|
||||
b2.reset();
|
||||
memset(&h, 0, sizeof(srslte::rlc_amd_pdu_header_t));
|
||||
|
||||
memcpy(b1.msg, &pdu4[0], PDU4_LEN);
|
||||
b1.N_bytes = PDU4_LEN;
|
||||
rlc_am_read_data_pdu_header(&b1, &h);
|
||||
assert(RLC_DC_FIELD_DATA_PDU == h.dc);
|
||||
assert(0x03 == h.fi);
|
||||
assert(2 == h.N_li);
|
||||
assert(1342 == h.li[0]);
|
||||
assert(1500 == h.li[1]);
|
||||
assert(0 == h.lsf);
|
||||
assert(0 == h.p);
|
||||
assert(0 == h.rf);
|
||||
assert(0 == h.so);
|
||||
assert(2 == h.sn);
|
||||
rlc_am_write_data_pdu_header(&h, &b2);
|
||||
assert(b2.N_bytes == PDU4_LEN);
|
||||
for(uint32_t i=0;i<b2.N_bytes;i++)
|
||||
assert(b2.msg[i] == b1.msg[i]);
|
||||
}
|
||||
|
|
|
@ -28,8 +28,10 @@
|
|||
#include "srslte/common/log_filter.h"
|
||||
#include "srslte/common/logger_stdout.h"
|
||||
#include "srslte/upper/rlc_am.h"
|
||||
#include "srslte/common/rlc_pcap.h"
|
||||
#include <assert.h>
|
||||
#define NBUFS 5
|
||||
#define HAVE_PCAP 0
|
||||
|
||||
using namespace srsue;
|
||||
using namespace srslte;
|
||||
|
@ -54,9 +56,11 @@ class rlc_am_tester
|
|||
,public rrc_interface_rlc
|
||||
{
|
||||
public:
|
||||
rlc_am_tester(){
|
||||
rlc_am_tester(rlc_pcap *pcap_ = NULL)
|
||||
{
|
||||
bzero(sdus, sizeof(sdus));
|
||||
n_sdus = 0;
|
||||
pcap = pcap_;
|
||||
}
|
||||
|
||||
~rlc_am_tester(){
|
||||
|
@ -83,6 +87,7 @@ public:
|
|||
|
||||
byte_buffer_t *sdus[10];
|
||||
int n_sdus;
|
||||
rlc_pcap *pcap;
|
||||
};
|
||||
|
||||
void basic_test()
|
||||
|
@ -1056,6 +1061,311 @@ void resegment_test_6()
|
|||
}
|
||||
}
|
||||
|
||||
// Retransmission of PDU segments of the same size
|
||||
void resegment_test_7()
|
||||
{
|
||||
// SDUs: | 30 | 30 |
|
||||
// PDUs: | 13 | 13 | 11 | 13 | 10 |
|
||||
// Rxed PDUs | 13 | 13 | | 13 | 10 |
|
||||
// Retx PDU segments: | 4 | 7 |
|
||||
// Retx PDU segments: |3|3]3|2|
|
||||
const uint32_t N_SDU_BUFS = 2;
|
||||
const uint32_t N_PDU_BUFS = 5;
|
||||
const uint32_t sdu_size = 30;
|
||||
|
||||
srslte::log_filter log1("RLC_AM_1");
|
||||
srslte::log_filter log2("RLC_AM_2");
|
||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log2.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log1.set_hex_limit(100);
|
||||
log2.set_hex_limit(100);
|
||||
|
||||
#if HAVE_PCAP
|
||||
rlc_pcap pcap;
|
||||
pcap.open("rlc_am_test7.pcap", 0);
|
||||
rlc_am_tester tester(&pcap);
|
||||
#else
|
||||
rlc_am_tester tester(NULL);
|
||||
#endif
|
||||
mac_dummy_timers timers;
|
||||
|
||||
rlc_am rlc1;
|
||||
rlc_am rlc2;
|
||||
|
||||
int len;
|
||||
|
||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log2.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
|
||||
rlc1.init(&log1, 1, &tester, &tester, &timers);
|
||||
rlc2.init(&log2, 1, &tester, &tester, &timers);
|
||||
|
||||
LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg;
|
||||
cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM;
|
||||
cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5;
|
||||
cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5;
|
||||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
||||
// Push 2 SDUs into RLC1
|
||||
byte_buffer_t sdu_bufs[N_SDU_BUFS];
|
||||
for(uint32_t i=0;i<N_SDU_BUFS;i++)
|
||||
{
|
||||
for(uint32_t j=0;j<sdu_size;j++) {
|
||||
sdu_bufs[i].msg[j] = i;
|
||||
}
|
||||
sdu_bufs[i].N_bytes = sdu_size; // Give each buffer a size of 15 bytes
|
||||
rlc1.write_sdu(&sdu_bufs[i]);
|
||||
}
|
||||
|
||||
assert(65 == rlc1.get_buffer_state());
|
||||
|
||||
// Read PDUs from RLC1 (15 bytes each)
|
||||
byte_buffer_t pdu_bufs[N_PDU_BUFS];
|
||||
for(uint32_t i=0;i<N_PDU_BUFS;i++)
|
||||
{
|
||||
pdu_bufs[i].N_bytes = rlc1.read_pdu(pdu_bufs[i].msg, 15); // 2 bytes for header + 12 B payload
|
||||
assert(pdu_bufs[i].N_bytes);
|
||||
}
|
||||
|
||||
assert(0 == rlc1.get_buffer_state());
|
||||
|
||||
// Skip PDU with SN 2
|
||||
for(uint32_t i=0;i<N_PDU_BUFS;i++) {
|
||||
if (i!=2) {
|
||||
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
||||
#if HAVE_PCAP
|
||||
pcap.write_dl_am_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Sleep to let reordering timeout expire
|
||||
usleep(10000);
|
||||
|
||||
assert(12 == rlc1.get_buffer_state());
|
||||
|
||||
// first round of retx, forcing resegmentation
|
||||
byte_buffer_t retx[4];
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
assert(rlc1.get_buffer_state());
|
||||
retx[i].N_bytes = rlc1.read_pdu(retx[i].msg, 7);
|
||||
assert(retx[i].N_bytes);
|
||||
|
||||
// Write the last two segments to RLC2
|
||||
if (i > 1) {
|
||||
rlc2.write_pdu(retx[i].msg, retx[i].N_bytes);
|
||||
#if HAVE_PCAP
|
||||
pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
usleep(10000);
|
||||
|
||||
// Read status PDU from RLC2
|
||||
assert(rlc2.get_buffer_state());
|
||||
byte_buffer_t status_buf;
|
||||
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
||||
|
||||
// Write status PDU to RLC1
|
||||
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
||||
#if HAVE_PCAP
|
||||
pcap.write_ul_am_ccch(status_buf.msg, status_buf.N_bytes);
|
||||
#endif
|
||||
|
||||
assert(15 == rlc1.get_buffer_state());
|
||||
|
||||
|
||||
// second round of retx, forcing resegmentation
|
||||
byte_buffer_t retx2[4];
|
||||
for (uint32_t i = 0; i < 4; i++) {
|
||||
assert(rlc1.get_buffer_state() != 0);
|
||||
retx2[i].N_bytes = rlc1.read_pdu(retx2[i].msg, 7);
|
||||
assert(retx2[i].N_bytes != 0);
|
||||
|
||||
rlc2.write_pdu(retx2[i].msg, retx2[i].N_bytes);
|
||||
#if HAVE_PCAP
|
||||
pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes);
|
||||
#endif
|
||||
}
|
||||
|
||||
// check buffer states
|
||||
assert(0 == rlc1.get_buffer_state());
|
||||
assert(0 == rlc2.get_buffer_state());
|
||||
|
||||
// Check number of SDUs and their content
|
||||
assert(tester.n_sdus == N_SDU_BUFS);
|
||||
for(int i=0; i<tester.n_sdus; i++)
|
||||
{
|
||||
assert(tester.sdus[i]->N_bytes == sdu_size);
|
||||
for(uint32_t j=0;j<N_SDU_BUFS;j++) {
|
||||
assert(tester.sdus[i]->msg[j] == i);
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_PCAP
|
||||
pcap.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Retransmission of PDU segments with different size
|
||||
void resegment_test_8()
|
||||
{
|
||||
// SDUs: | 30 | 30 |
|
||||
// PDUs: | 15 | 15 | 15 | 15 | 15 |
|
||||
// Rxed PDUs | 15 | | 15 | 15 |
|
||||
// Retx PDU segments: | 7 | 7 | 7 | 7 |
|
||||
// Retx PDU segments: | 6 | 6 ] 6 | 6 | 6 | 6 | 6 | 6 |
|
||||
const uint32_t N_SDU_BUFS = 2;
|
||||
const uint32_t N_PDU_BUFS = 5;
|
||||
const uint32_t sdu_size = 30;
|
||||
|
||||
srslte::log_filter log1("RLC_AM_1");
|
||||
srslte::log_filter log2("RLC_AM_2");
|
||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log2.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log1.set_hex_limit(100);
|
||||
log2.set_hex_limit(100);
|
||||
|
||||
#if HAVE_PCAP
|
||||
rlc_pcap pcap;
|
||||
pcap.open("rlc_am_test8.pcap", 0);
|
||||
rlc_am_tester tester(&pcap);
|
||||
#else
|
||||
rlc_am_tester tester(NULL);
|
||||
#endif
|
||||
mac_dummy_timers timers;
|
||||
|
||||
rlc_am rlc1;
|
||||
rlc_am rlc2;
|
||||
|
||||
|
||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log2.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
|
||||
rlc1.init(&log1, 1, &tester, &tester, &timers);
|
||||
rlc2.init(&log2, 1, &tester, &tester, &timers);
|
||||
|
||||
LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg;
|
||||
cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM;
|
||||
cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5;
|
||||
cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5;
|
||||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
||||
// Push 2 SDUs into RLC1
|
||||
byte_buffer_t sdu_bufs[N_SDU_BUFS];
|
||||
for(uint32_t i=0;i<N_SDU_BUFS;i++)
|
||||
{
|
||||
for(uint32_t j=0;j<sdu_size;j++) {
|
||||
sdu_bufs[i].msg[j] = i;
|
||||
}
|
||||
sdu_bufs[i].N_bytes = sdu_size; // Give each buffer a size of 15 bytes
|
||||
rlc1.write_sdu(&sdu_bufs[i]);
|
||||
}
|
||||
|
||||
assert(65 == rlc1.get_buffer_state());
|
||||
|
||||
// Read PDUs from RLC1 (15 bytes each)
|
||||
byte_buffer_t pdu_bufs[N_PDU_BUFS];
|
||||
for(uint32_t i=0;i<N_PDU_BUFS;i++)
|
||||
{
|
||||
pdu_bufs[i].N_bytes = rlc1.read_pdu(pdu_bufs[i].msg, 15); // 12 bytes for header + payload
|
||||
assert(pdu_bufs[i].N_bytes);
|
||||
}
|
||||
|
||||
assert(0 == rlc1.get_buffer_state());
|
||||
|
||||
// Skip PDU one and two
|
||||
for(uint32_t i=0;i<N_PDU_BUFS;i++) {
|
||||
if (i < 1 || i > 2) {
|
||||
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
||||
#if HAVE_PCAP
|
||||
pcap.write_dl_am_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Sleep to let reordering timeout expire
|
||||
usleep(10000);
|
||||
|
||||
assert(12 == rlc1.get_buffer_state());
|
||||
|
||||
// first round of retx, forcing resegmentation
|
||||
byte_buffer_t retx[4];
|
||||
for (uint32_t i = 0; i < 3; i++) {
|
||||
assert(rlc1.get_buffer_state());
|
||||
retx[i].N_bytes = rlc1.read_pdu(retx[i].msg, 8);
|
||||
assert(retx[i].N_bytes);
|
||||
|
||||
// Write the last two segments to RLC2
|
||||
if (i > 1) {
|
||||
rlc2.write_pdu(retx[i].msg, retx[i].N_bytes);
|
||||
#if HAVE_PCAP
|
||||
pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
usleep(20000);
|
||||
|
||||
// Read status PDU from RLC2
|
||||
assert(rlc2.get_buffer_state());
|
||||
byte_buffer_t status_buf;
|
||||
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
||||
|
||||
// Write status PDU to RLC1
|
||||
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
||||
#if HAVE_PCAP
|
||||
pcap.write_ul_am_ccch(status_buf.msg, status_buf.N_bytes);
|
||||
#endif
|
||||
|
||||
assert(15 == rlc1.get_buffer_state());
|
||||
|
||||
// second round of retx, reduce grant size to force different segment sizes
|
||||
byte_buffer_t retx2[20];
|
||||
for (uint32_t i = 0; i < 9; i++) {
|
||||
assert(rlc1.get_buffer_state() != 0);
|
||||
retx2[i].N_bytes = rlc1.read_pdu(retx2[i].msg, 7);
|
||||
assert(retx2[i].N_bytes != 0);
|
||||
rlc2.write_pdu(retx2[i].msg, retx2[i].N_bytes);
|
||||
#if HAVE_PCAP
|
||||
pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes);
|
||||
#endif
|
||||
}
|
||||
|
||||
// check buffer states
|
||||
assert(0 == rlc1.get_buffer_state());
|
||||
assert(0 == rlc2.get_buffer_state());
|
||||
|
||||
// Check number of SDUs and their content
|
||||
assert(tester.n_sdus == N_SDU_BUFS);
|
||||
for(int i=0; i<tester.n_sdus; i++)
|
||||
{
|
||||
assert(tester.sdus[i]->N_bytes == sdu_size);
|
||||
for(uint32_t j=0;j<N_SDU_BUFS;j++) {
|
||||
assert(tester.sdus[i]->msg[j] == i);
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_PCAP
|
||||
pcap.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void reset_test()
|
||||
{
|
||||
srslte::log_filter log1("RLC_AM_1");
|
||||
|
@ -1141,6 +1451,12 @@ int main(int argc, char **argv) {
|
|||
resegment_test_6();
|
||||
byte_buffer_pool::get_instance()->cleanup();
|
||||
|
||||
resegment_test_7();
|
||||
byte_buffer_pool::get_instance()->cleanup();
|
||||
|
||||
resegment_test_8();
|
||||
byte_buffer_pool::get_instance()->cleanup();
|
||||
|
||||
reset_test();
|
||||
byte_buffer_pool::get_instance()->cleanup();
|
||||
}
|
||||
|
|
|
@ -30,10 +30,14 @@
|
|||
#include "srslte/common/log_filter.h"
|
||||
#include "srslte/common/logger_stdout.h"
|
||||
#include "srslte/common/threads.h"
|
||||
#include "srslte/common/rlc_pcap.h"
|
||||
#include "srslte/upper/rlc.h"
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <assert.h>
|
||||
#include <srslte/upper/rlc_interface.h>
|
||||
|
||||
#define SDU_SIZE 1500
|
||||
|
||||
using namespace std;
|
||||
using namespace srsue;
|
||||
|
@ -41,12 +45,16 @@ using namespace srslte;
|
|||
namespace bpo = boost::program_options;
|
||||
|
||||
typedef struct {
|
||||
uint32_t test_duration_sec;
|
||||
float error_rate;
|
||||
uint32_t sdu_gen_delay_usec;
|
||||
uint32_t pdu_tx_delay_usec;
|
||||
bool reestablish;
|
||||
uint32_t log_level;
|
||||
std::string mode;
|
||||
uint32_t test_duration_sec;
|
||||
float error_rate;
|
||||
uint32_t sdu_gen_delay_usec;
|
||||
uint32_t pdu_tx_delay_usec;
|
||||
bool reestablish;
|
||||
uint32_t log_level;
|
||||
bool single_tx;
|
||||
bool write_pcap;
|
||||
float opp_sdu_ratio;
|
||||
} stress_test_args_t;
|
||||
|
||||
void parse_args(stress_test_args_t *args, int argc, char *argv[]) {
|
||||
|
@ -61,12 +69,16 @@ void parse_args(stress_test_args_t *args, int argc, char *argv[]) {
|
|||
// Command line or config file options
|
||||
bpo::options_description common("Configuration options");
|
||||
common.add_options()
|
||||
("duration", bpo::value<uint32_t>(&args->test_duration_sec)->default_value(10), "Duration (sec)")
|
||||
("sdu_gen_delay", bpo::value<uint32_t>(&args->sdu_gen_delay_usec)->default_value(10), "SDU generation delay (usec)")
|
||||
("pdu_tx_delay", bpo::value<uint32_t>(&args->pdu_tx_delay_usec)->default_value(10), "Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec)")
|
||||
("mode", bpo::value<std::string>(&args->mode)->default_value("AM"), "Whether to test RLC acknowledged or unacknowledged mode (AM/UM)")
|
||||
("duration", bpo::value<uint32_t>(&args->test_duration_sec)->default_value(5), "Duration (sec)")
|
||||
("sdu_gen_delay", bpo::value<uint32_t>(&args->sdu_gen_delay_usec)->default_value(0), "SDU generation delay (usec)")
|
||||
("pdu_tx_delay", bpo::value<uint32_t>(&args->pdu_tx_delay_usec)->default_value(0), "Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec)")
|
||||
("error_rate", bpo::value<float>(&args->error_rate)->default_value(0.1), "Rate at which RLC PDUs are dropped")
|
||||
("opp_sdu_ratio", bpo::value<float>(&args->opp_sdu_ratio)->default_value(0.0), "Ratio between MAC opportunity and SDU size (0==random)")
|
||||
("reestablish", bpo::value<bool>(&args->reestablish)->default_value(false), "Mimic RLC reestablish during execution")
|
||||
("loglevel", bpo::value<uint32_t>(&args->log_level)->default_value(srslte::LOG_LEVEL_DEBUG), "Log level (1=Error,2=Warning,3=Info,4=Debug");
|
||||
("loglevel", bpo::value<uint32_t>(&args->log_level)->default_value(srslte::LOG_LEVEL_DEBUG), "Log level (1=Error,2=Warning,3=Info,4=Debug)")
|
||||
("singletx", bpo::value<bool>(&args->single_tx)->default_value(false), "If set to true, only one node is generating data")
|
||||
("pcap", bpo::value<bool>(&args->write_pcap)->default_value(false), "Whether to write all RLC PDU to PCAP file");
|
||||
|
||||
// these options are allowed on the command line
|
||||
bpo::options_description cmdline_options;
|
||||
|
@ -94,14 +106,18 @@ class mac_reader
|
|||
:public thread
|
||||
{
|
||||
public:
|
||||
mac_reader(rlc_interface_mac *rlc1_, rlc_interface_mac *rlc2_, float fail_rate_, uint32_t pdu_tx_delay_usec_)
|
||||
mac_reader(rlc_interface_mac *rlc1_, rlc_interface_mac *rlc2_, float fail_rate_, float opp_sdu_ratio_, uint32_t pdu_tx_delay_usec_, rlc_pcap *pcap_, uint32_t lcid_, bool is_dl_ = true)
|
||||
{
|
||||
rlc1 = rlc1_;
|
||||
rlc2 = rlc2_;
|
||||
fail_rate = fail_rate_;
|
||||
opp_sdu_ratio = opp_sdu_ratio_;
|
||||
run_enable = true;
|
||||
running = false;
|
||||
pdu_tx_delay_usec = pdu_tx_delay_usec_;
|
||||
pcap = pcap_;
|
||||
is_dl = is_dl_;
|
||||
lcid = lcid_;
|
||||
}
|
||||
|
||||
void stop()
|
||||
|
@ -129,14 +145,23 @@ private:
|
|||
}
|
||||
|
||||
while(run_enable) {
|
||||
float r = (float)rand()/RAND_MAX;
|
||||
int opp_size = r*1500;
|
||||
rlc1->get_buffer_state(1);
|
||||
int read = rlc1->read_pdu(1, pdu->msg, opp_size);
|
||||
if(((float)rand()/RAND_MAX > fail_rate) && read>0) {
|
||||
rlc2->write_pdu(1, pdu->msg, opp_size);
|
||||
// generate MAC opportunities of random size or with fixed ratio
|
||||
float r = opp_sdu_ratio ? opp_sdu_ratio : (float)rand()/RAND_MAX;
|
||||
int opp_size = r*SDU_SIZE;
|
||||
uint32_t buf_state = rlc1->get_buffer_state(lcid);
|
||||
if (buf_state) {
|
||||
int read = rlc1->read_pdu(lcid, pdu->msg, opp_size);
|
||||
if (pdu_tx_delay_usec) usleep(pdu_tx_delay_usec);
|
||||
if(((float)rand()/RAND_MAX > fail_rate) && read>0) {
|
||||
pdu->N_bytes = read;
|
||||
rlc2->write_pdu(lcid, pdu->msg, pdu->N_bytes);
|
||||
if (is_dl) {
|
||||
pcap->write_dl_am_ccch(pdu->msg, pdu->N_bytes);
|
||||
} else {
|
||||
pcap->write_ul_am_ccch(pdu->msg, pdu->N_bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
usleep(pdu_tx_delay_usec);
|
||||
}
|
||||
running = false;
|
||||
byte_buffer_pool::get_instance()->deallocate(pdu);
|
||||
|
@ -145,7 +170,11 @@ private:
|
|||
rlc_interface_mac *rlc1;
|
||||
rlc_interface_mac *rlc2;
|
||||
float fail_rate;
|
||||
float opp_sdu_ratio;
|
||||
uint32_t pdu_tx_delay_usec;
|
||||
rlc_pcap *pcap;
|
||||
uint32_t lcid;
|
||||
bool is_dl;
|
||||
|
||||
bool run_enable;
|
||||
bool running;
|
||||
|
@ -155,9 +184,9 @@ class mac_dummy
|
|||
:public srslte::mac_interface_timers
|
||||
{
|
||||
public:
|
||||
mac_dummy(rlc_interface_mac *rlc1_, rlc_interface_mac *rlc2_, float fail_rate_, uint32_t pdu_tx_delay)
|
||||
:r1(rlc1_, rlc2_, fail_rate_, pdu_tx_delay)
|
||||
,r2(rlc2_, rlc1_, fail_rate_, pdu_tx_delay)
|
||||
mac_dummy(rlc_interface_mac *rlc1_, rlc_interface_mac *rlc2_, float fail_rate_, float opp_sdu_ratio_, int32_t pdu_tx_delay, uint32_t lcid, rlc_pcap* pcap = NULL)
|
||||
:r1(rlc1_, rlc2_, fail_rate_, opp_sdu_ratio_, pdu_tx_delay, pcap, lcid, true)
|
||||
,r2(rlc2_, rlc1_, fail_rate_, opp_sdu_ratio_, pdu_tx_delay, pcap, lcid, false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -189,19 +218,20 @@ private:
|
|||
|
||||
|
||||
|
||||
class rlc_am_tester
|
||||
class rlc_tester
|
||||
:public pdcp_interface_rlc
|
||||
,public rrc_interface_rlc
|
||||
,public thread
|
||||
{
|
||||
public:
|
||||
rlc_am_tester(rlc_interface_pdcp *rlc_, std::string name_, uint32_t sdu_gen_delay_usec_){
|
||||
rlc_tester(rlc_interface_pdcp *rlc_, std::string name_, uint32_t sdu_gen_delay_usec_, uint32_t lcid_){
|
||||
rlc = rlc_;
|
||||
run_enable = true;
|
||||
running = false;
|
||||
rx_pdus = 0;
|
||||
name = name_;
|
||||
sdu_gen_delay_usec = sdu_gen_delay_usec_;
|
||||
lcid = lcid_;
|
||||
}
|
||||
|
||||
void stop()
|
||||
|
@ -219,11 +249,16 @@ public:
|
|||
}
|
||||
|
||||
// PDCP interface
|
||||
void write_pdu(uint32_t lcid, byte_buffer_t *sdu)
|
||||
void write_pdu(uint32_t rx_lcid, byte_buffer_t *sdu)
|
||||
{
|
||||
assert(lcid == 1);
|
||||
assert(rx_lcid == lcid);
|
||||
if (sdu->N_bytes != SDU_SIZE) {
|
||||
printf("Received PDU with size %d, expected %d. Exiting.\n", sdu->N_bytes, SDU_SIZE);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
byte_buffer_pool::get_instance()->deallocate(sdu);
|
||||
std::cout << "rlc_am_tester " << name << " received " << rx_pdus++ << " PDUs" << std::endl;
|
||||
rx_pdus++;
|
||||
}
|
||||
void write_pdu_bcch_bch(byte_buffer_t *sdu) {}
|
||||
void write_pdu_bcch_dlsch(byte_buffer_t *sdu) {}
|
||||
|
@ -231,7 +266,9 @@ public:
|
|||
|
||||
// RRC interface
|
||||
void max_retx_attempted(){}
|
||||
std::string get_rb_name(uint32_t lcid) { return std::string(""); }
|
||||
std::string get_rb_name(uint32_t rx_lcid) { return std::string(""); }
|
||||
|
||||
int get_nof_rx_pdus() { return rx_pdus; }
|
||||
|
||||
private:
|
||||
void run_thread()
|
||||
|
@ -239,15 +276,18 @@ private:
|
|||
uint8_t sn = 0;
|
||||
running = true;
|
||||
while(run_enable) {
|
||||
byte_buffer_t *pdu = byte_buffer_pool::get_instance()->allocate("rlc_am_tester::run_thread");
|
||||
byte_buffer_t *pdu = byte_buffer_pool::get_instance()->allocate("rlc_tester::run_thread");
|
||||
if (!pdu) {
|
||||
printf("Fatal Error: Could not allocate PDU in rlc_am_tester::run_thread\n");
|
||||
printf("Fatal Error: Could not allocate PDU in rlc_tester::run_thread\n");
|
||||
exit(-1);
|
||||
}
|
||||
pdu->N_bytes = 1500;
|
||||
pdu->msg[0] = sn++;
|
||||
rlc->write_sdu(1, pdu);
|
||||
usleep(sdu_gen_delay_usec);
|
||||
for (uint32_t i = 0; i < SDU_SIZE; i++) {
|
||||
pdu->msg[i] = sn;
|
||||
}
|
||||
sn++;
|
||||
pdu->N_bytes = SDU_SIZE;
|
||||
rlc->write_sdu(lcid, pdu);
|
||||
if (sdu_gen_delay_usec) usleep(sdu_gen_delay_usec);
|
||||
}
|
||||
running = false;
|
||||
}
|
||||
|
@ -255,6 +295,7 @@ private:
|
|||
bool run_enable;
|
||||
bool running;
|
||||
long rx_pdus;
|
||||
uint32_t lcid;
|
||||
|
||||
std::string name;
|
||||
|
||||
|
@ -265,40 +306,67 @@ private:
|
|||
|
||||
void stress_test(stress_test_args_t args)
|
||||
{
|
||||
srslte::log_filter log1("RLC_AM_1");
|
||||
srslte::log_filter log2("RLC_AM_2");
|
||||
srslte::log_filter log1("RLC_1");
|
||||
srslte::log_filter log2("RLC_2");
|
||||
log1.set_level((LOG_LEVEL_ENUM)args.log_level);
|
||||
log2.set_level((LOG_LEVEL_ENUM)args.log_level);
|
||||
log1.set_hex_limit(-1);
|
||||
log2.set_hex_limit(-1);
|
||||
rlc_pcap pcap;
|
||||
uint32_t lcid = 1;
|
||||
|
||||
if (args.write_pcap) {
|
||||
pcap.open("rlc_stress_test.pcap", 0);
|
||||
}
|
||||
|
||||
srslte_rlc_config_t cnfg_;
|
||||
if (args.mode == "AM") {
|
||||
// config RLC AM bearer
|
||||
cnfg_.rlc_mode = LIBLTE_RRC_RLC_MODE_AM;
|
||||
cnfg_.am.max_retx_thresh = 4;
|
||||
cnfg_.am.poll_byte = 25*1000;
|
||||
cnfg_.am.poll_pdu = 4;
|
||||
cnfg_.am.t_poll_retx = 5;
|
||||
cnfg_.am.t_reordering = 5;
|
||||
cnfg_.am.t_status_prohibit = 5;
|
||||
} else if (args.mode == "UM") {
|
||||
// config UM bearer
|
||||
cnfg_.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI;
|
||||
cnfg_.um.t_reordering = 5;
|
||||
cnfg_.um.rx_mod = 32;
|
||||
cnfg_.um.rx_sn_field_length = RLC_UMD_SN_SIZE_5_BITS;
|
||||
cnfg_.um.rx_window_size = 16;
|
||||
cnfg_.um.tx_sn_field_length = RLC_UMD_SN_SIZE_5_BITS;
|
||||
cnfg_.um.tx_mod = 32;
|
||||
} else if (args.mode == "TM") {
|
||||
// use default LCID in TM
|
||||
lcid = 0;
|
||||
} else {
|
||||
cout << "Unsupported RLC mode " << args.mode << ", exiting." << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
rlc rlc1;
|
||||
rlc rlc2;
|
||||
|
||||
rlc_am_tester tester1(&rlc1, "tester1", args.sdu_gen_delay_usec);
|
||||
rlc_am_tester tester2(&rlc2, "tester2", args.sdu_gen_delay_usec);
|
||||
mac_dummy mac(&rlc1, &rlc2, args.error_rate, args.pdu_tx_delay_usec);
|
||||
rlc_tester tester1(&rlc1, "tester1", args.sdu_gen_delay_usec, lcid);
|
||||
rlc_tester tester2(&rlc2, "tester2", args.sdu_gen_delay_usec, lcid);
|
||||
mac_dummy mac(&rlc1, &rlc2, args.error_rate, args.opp_sdu_ratio, args.pdu_tx_delay_usec, lcid, &pcap);
|
||||
ue_interface ue;
|
||||
|
||||
rlc1.init(&tester1, &tester1, &ue, &log1, &mac, 0);
|
||||
rlc2.init(&tester2, &tester2, &ue, &log2, &mac, 0);
|
||||
|
||||
LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg;
|
||||
cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM;
|
||||
cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5;
|
||||
cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5;
|
||||
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
|
||||
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25;
|
||||
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4;
|
||||
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS5;
|
||||
|
||||
srslte_rlc_config_t cnfg_(&cnfg);
|
||||
|
||||
rlc1.add_bearer(1, cnfg_);
|
||||
rlc2.add_bearer(1, cnfg_);
|
||||
// only add AM and UM bearers
|
||||
if (args.mode != "TM") {
|
||||
rlc1.add_bearer(lcid, cnfg_);
|
||||
rlc2.add_bearer(lcid, cnfg_);
|
||||
}
|
||||
|
||||
tester1.start(7);
|
||||
tester2.start(7);
|
||||
if (!args.single_tx) {
|
||||
tester2.start(7);
|
||||
}
|
||||
mac.start();
|
||||
|
||||
for (uint32_t i = 0; i < args.test_duration_sec; i++) {
|
||||
|
@ -313,6 +381,19 @@ void stress_test(stress_test_args_t args)
|
|||
tester1.stop();
|
||||
tester2.stop();
|
||||
mac.stop();
|
||||
if (args.write_pcap) {
|
||||
pcap.close();
|
||||
}
|
||||
|
||||
printf("RLC1 received %d SDUs in %ds (%.2f PDU/s)\n",
|
||||
tester1.get_nof_rx_pdus(),
|
||||
args.test_duration_sec,
|
||||
(float)tester1.get_nof_rx_pdus()/args.test_duration_sec);
|
||||
|
||||
printf("RLC2 received %d SDUs in %ds (%.2f PDU/s)\n",
|
||||
tester2.get_nof_rx_pdus(),
|
||||
args.test_duration_sec,
|
||||
(float)tester2.get_nof_rx_pdus()/args.test_duration_sec);
|
||||
}
|
||||
|
||||
|
||||
|
@ -322,4 +403,6 @@ int main(int argc, char **argv) {
|
|||
|
||||
stress_test(args);
|
||||
byte_buffer_pool::get_instance()->cleanup();
|
||||
|
||||
exit(0);
|
||||
}
|
|
@ -29,6 +29,7 @@
|
|||
#include "srslte/upper/rlc_um.h"
|
||||
#include <assert.h>
|
||||
|
||||
#define MAX_NBUFS 100
|
||||
#define NBUFS 5
|
||||
|
||||
using namespace srslte;
|
||||
|
@ -60,6 +61,7 @@ public:
|
|||
rlc_um_tester(){
|
||||
bzero(sdus, sizeof(sdus));
|
||||
n_sdus = 0;
|
||||
expected_sdu_len = 0;
|
||||
}
|
||||
|
||||
~rlc_um_tester(){
|
||||
|
@ -74,6 +76,10 @@ public:
|
|||
void write_pdu(uint32_t lcid, byte_buffer_t *sdu)
|
||||
{
|
||||
assert(lcid == 3);
|
||||
if (sdu->N_bytes != expected_sdu_len) {
|
||||
printf("Received PDU with size %d, expected %d. Exiting.\n", sdu->N_bytes, expected_sdu_len);
|
||||
exit(-1);
|
||||
}
|
||||
sdus[n_sdus++] = sdu;
|
||||
}
|
||||
void write_pdu_bcch_bch(byte_buffer_t *sdu) {}
|
||||
|
@ -83,9 +89,11 @@ public:
|
|||
// RRC interface
|
||||
void max_retx_attempted(){}
|
||||
std::string get_rb_name(uint32_t lcid) { return std::string(""); }
|
||||
void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; }
|
||||
|
||||
byte_buffer_t *sdus[5];
|
||||
byte_buffer_t *sdus[MAX_NBUFS];
|
||||
int n_sdus;
|
||||
uint32_t expected_sdu_len;
|
||||
};
|
||||
|
||||
void basic_test()
|
||||
|
@ -119,6 +127,8 @@ void basic_test()
|
|||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
||||
tester.set_expected_sdu_len(1);
|
||||
|
||||
// Push 5 SDUs into RLC1
|
||||
byte_buffer_t sdu_bufs[NBUFS];
|
||||
for(int i=0;i<NBUFS;i++)
|
||||
|
@ -187,6 +197,8 @@ void loss_test()
|
|||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
||||
tester.set_expected_sdu_len(1);
|
||||
|
||||
// Push 5 SDUs into RLC1
|
||||
byte_buffer_t sdu_bufs[NBUFS];
|
||||
for(int i=0;i<NBUFS;i++)
|
||||
|
@ -222,9 +234,243 @@ void loss_test()
|
|||
assert(NBUFS-1 == tester.n_sdus);
|
||||
}
|
||||
|
||||
|
||||
// This test checks the reassembly routines when a PDU
|
||||
// is lost that contains the beginning of SDU segment.
|
||||
// The PDU that contains the end of this SDU _also_ contains
|
||||
// a segment of another SDU.
|
||||
// On reassembly of the SDUs, the missing start segment
|
||||
// should be detected and the complete SDU be discarded
|
||||
// Therefore, one SDU less should be received than was tx'ed.
|
||||
// This test sends PDU in two batches so it's not the reordering
|
||||
// timeout that detects the missing PDU but the fact more
|
||||
// PDUs than rx_mod are received.
|
||||
void reassmble_test()
|
||||
{
|
||||
srslte::log_filter log1("RLC_UM_1");
|
||||
srslte::log_filter log2("RLC_UM_2");
|
||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log2.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log1.set_hex_limit(-1);
|
||||
log2.set_hex_limit(-1);
|
||||
rlc_um_tester tester;
|
||||
mac_dummy_timers timers;
|
||||
|
||||
rlc_um rlc1;
|
||||
rlc_um rlc2;
|
||||
|
||||
int len;
|
||||
|
||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log2.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
|
||||
rlc1.init(&log1, 3, &tester, &tester, &timers);
|
||||
rlc2.init(&log2, 3, &tester, &tester, &timers);
|
||||
|
||||
LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg;
|
||||
cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI;
|
||||
cnfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5;
|
||||
cnfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE5;
|
||||
cnfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
||||
// Push SDUs into RLC1
|
||||
const int n_sdus = 25;
|
||||
const int sdu_len = 100;
|
||||
|
||||
tester.set_expected_sdu_len(sdu_len);
|
||||
|
||||
byte_buffer_t sdu_bufs[n_sdus];
|
||||
|
||||
const int n_sdu_first_batch = 17;
|
||||
|
||||
for(int i=0;i<n_sdu_first_batch;i++) {
|
||||
for (int k = 0; k < sdu_len; ++k) {
|
||||
sdu_bufs[i].msg[k] = i;
|
||||
}
|
||||
sdu_bufs[i].N_bytes = sdu_len; // Give each buffer a size of 1 byte
|
||||
rlc1.write_sdu(&sdu_bufs[i]);
|
||||
}
|
||||
|
||||
// Read PDUs from RLC1 (use smaller grant for first PDU and large for the rest)
|
||||
const int max_n_pdus = 100;
|
||||
int n_pdus = 0;
|
||||
byte_buffer_t pdu_bufs[max_n_pdus];
|
||||
for(int i=0;i<max_n_pdus;i++)
|
||||
{
|
||||
len = rlc1.read_pdu(pdu_bufs[i].msg, (i == 0) ? sdu_len*3/4 : sdu_len*1.25);
|
||||
pdu_bufs[i].N_bytes = len;
|
||||
if (len) {
|
||||
n_pdus++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Generated %d PDUs in first batch\n", n_pdus);
|
||||
assert(0 == rlc1.get_buffer_state());
|
||||
|
||||
// push second batch of SDUs
|
||||
for (int i = n_sdu_first_batch; i < n_sdus; ++i) {
|
||||
for (int k = 0; k < sdu_len; ++k) {
|
||||
sdu_bufs[i].msg[k] = i;
|
||||
}
|
||||
sdu_bufs[i].N_bytes = sdu_len; // Give each buffer a size of 1 byte
|
||||
rlc1.write_sdu(&sdu_bufs[i]);
|
||||
}
|
||||
|
||||
// Read second batch of PDUs (use large grants)
|
||||
for(int i=n_pdus;i<max_n_pdus;i++)
|
||||
{
|
||||
len = rlc1.read_pdu(pdu_bufs[i].msg, sdu_len*1.25);
|
||||
pdu_bufs[i].N_bytes = len;
|
||||
if (len) {
|
||||
n_pdus++;
|
||||
} else {
|
||||
// stop reading PDUs after first zero length PDU
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Generated %d PDUs in total\n", n_pdus);
|
||||
|
||||
// Write all PDUs into RLC2 except first one
|
||||
for(int i=0;i<n_pdus;i++)
|
||||
{
|
||||
if (i!=0) {
|
||||
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// We should have received one SDU less than we tx'ed
|
||||
assert(tester.n_sdus == n_sdus - 1);
|
||||
for (int i = 0; i < tester.n_sdus; ++i) {
|
||||
assert(tester.sdus[i]->N_bytes == sdu_len);
|
||||
}
|
||||
}
|
||||
|
||||
// This reassmble test checks the reassembly routines when a PDU
|
||||
// is lost that _only_ contains the beginning of SDU segment,
|
||||
// while the next PDU contains the middle part of this SDU (and
|
||||
// yet another PDU the end part).
|
||||
// On reassembly of the SDUs, the missing start segment
|
||||
// should be detected and the complete SDU be discarded
|
||||
// Therefore, one SDU less should be received than was tx'ed.
|
||||
void reassmble_test2()
|
||||
{
|
||||
srslte::log_filter log1("RLC_UM_1");
|
||||
srslte::log_filter log2("RLC_UM_2");
|
||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log2.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log1.set_hex_limit(-1);
|
||||
log2.set_hex_limit(-1);
|
||||
rlc_um_tester tester;
|
||||
mac_dummy_timers timers;
|
||||
|
||||
rlc_um rlc1;
|
||||
rlc_um rlc2;
|
||||
|
||||
int len;
|
||||
|
||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log2.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
|
||||
rlc1.init(&log1, 3, &tester, &tester, &timers);
|
||||
rlc2.init(&log2, 3, &tester, &tester, &timers);
|
||||
|
||||
LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg;
|
||||
cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI;
|
||||
cnfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5;
|
||||
cnfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE5;
|
||||
cnfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE5;
|
||||
|
||||
rlc1.configure(&cnfg);
|
||||
rlc2.configure(&cnfg);
|
||||
|
||||
// Push SDUs into RLC1
|
||||
const int n_sdus = 25;
|
||||
const int sdu_len = 100;
|
||||
|
||||
tester.set_expected_sdu_len(sdu_len);
|
||||
|
||||
byte_buffer_t sdu_bufs[n_sdus];
|
||||
const int n_sdu_first_batch = 17;
|
||||
|
||||
for(int i=0;i<n_sdu_first_batch;i++) {
|
||||
for (int k = 0; k < sdu_len; ++k) {
|
||||
sdu_bufs[i].msg[k] = i;
|
||||
}
|
||||
sdu_bufs[i].N_bytes = sdu_len;
|
||||
rlc1.write_sdu(&sdu_bufs[i]);
|
||||
}
|
||||
|
||||
const int max_n_pdus = 100;
|
||||
int n_pdus = 0;
|
||||
byte_buffer_t pdu_bufs[max_n_pdus];
|
||||
for(int i=0;i<max_n_pdus;i++)
|
||||
{
|
||||
len = rlc1.read_pdu(pdu_bufs[i].msg, (i == 0) ? sdu_len*.75 : sdu_len*.25);
|
||||
pdu_bufs[i].N_bytes = len;
|
||||
if (len) {
|
||||
n_pdus++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Generated %d PDUs in first batch\n", n_pdus);
|
||||
assert(0 == rlc1.get_buffer_state());
|
||||
|
||||
// push second batch of SDUs
|
||||
for (int i = n_sdu_first_batch; i < n_sdus; ++i) {
|
||||
for (int k = 0; k < sdu_len; ++k) {
|
||||
sdu_bufs[i].msg[k] = i;
|
||||
}
|
||||
sdu_bufs[i].N_bytes = sdu_len; // Give each buffer a size of 1 byte
|
||||
rlc1.write_sdu(&sdu_bufs[i]);
|
||||
}
|
||||
|
||||
// Read second batch of PDUs
|
||||
for(int i=n_pdus;i<max_n_pdus;i++)
|
||||
{
|
||||
len = rlc1.read_pdu(pdu_bufs[i].msg, sdu_len*1.25);
|
||||
pdu_bufs[i].N_bytes = len;
|
||||
if (len) {
|
||||
n_pdus++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Generated %d PDUs in total\n", n_pdus);
|
||||
|
||||
// Write all PDUs into RLC2 except first one
|
||||
for(int i=0;i<n_pdus;i++) {
|
||||
if (i!=0) {
|
||||
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// We should have received one SDU less than we tx'ed
|
||||
assert(tester.n_sdus == n_sdus - 1);
|
||||
for (int i = 0; i < tester.n_sdus; ++i) {
|
||||
assert(tester.sdus[i]->N_bytes == sdu_len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
basic_test();
|
||||
byte_buffer_pool::get_instance()->cleanup();
|
||||
|
||||
loss_test();
|
||||
byte_buffer_pool::get_instance()->cleanup();
|
||||
|
||||
reassmble_test();
|
||||
byte_buffer_pool::get_instance()->cleanup();
|
||||
|
||||
reassmble_test2();
|
||||
byte_buffer_pool::get_instance()->cleanup();
|
||||
}
|
||||
|
|
|
@ -366,7 +366,6 @@ void phch_recv::run_thread()
|
|||
bool is_end_of_burst = false;
|
||||
|
||||
cf_t *dummy_buffer[SRSLTE_MAX_PORTS];
|
||||
|
||||
for (int i=0;i<SRSLTE_MAX_PORTS;i++) {
|
||||
dummy_buffer[i] = (cf_t*) malloc(sizeof(cf_t)*SRSLTE_SF_LEN_PRB(100));
|
||||
}
|
||||
|
@ -555,6 +554,12 @@ void phch_recv::run_thread()
|
|||
// Increase TTI counter
|
||||
tti = (tti+1) % 10240;
|
||||
}
|
||||
|
||||
for (int i=0;i<SRSLTE_MAX_PORTS;i++) {
|
||||
if (dummy_buffer[i]) {
|
||||
free(dummy_buffer[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue