From e976508232dc1ae3ff29fb615f1c2ad20535e854 Mon Sep 17 00:00:00 2001 From: Manuel Requena Date: Mon, 5 Dec 2011 12:58:34 +0100 Subject: [PATCH] Add AM RLC entity --- src/lte/model/lte-rlc-am.cc | 1430 +++++++++++++++++++++++++++++++++++ src/lte/model/lte-rlc-am.h | 169 +++++ 2 files changed, 1599 insertions(+) create mode 100644 src/lte/model/lte-rlc-am.cc create mode 100644 src/lte/model/lte-rlc-am.h diff --git a/src/lte/model/lte-rlc-am.cc b/src/lte/model/lte-rlc-am.cc new file mode 100644 index 000000000..65c6bb51b --- /dev/null +++ b/src/lte/model/lte-rlc-am.cc @@ -0,0 +1,1430 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Manuel Requena + */ + +#include "ns3/simulator.h" +#include "ns3/log.h" + +#include "ns3/lte-rlc-am-header.h" +#include "ns3/lte-rlc-am.h" +#include "ns3/lte-rlc-sdu-status-tag.h" +#include "ns3/lte-rlc-tag.h" + +NS_LOG_COMPONENT_DEFINE ("LteRlcAm"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (LteRlcAm); + +LteRlcAm::LteRlcAm () +{ + NS_LOG_FUNCTION (this); + + // Buffers + m_txonBufferSize = 0; + m_retxBuffer.resize (512); + m_retxBufferSize = 0; + m_txedBuffer.resize (512); + m_txedBufferSize = 0; + + m_statusPduRequested = false; + m_statusPduBufferSize = 0; + + // State variables: transmitting side + m_windowSize = 512; + m_vtA = 0; + m_vtMs = m_vtA + m_windowSize; + m_vtS = 0; + m_pollSn = 0; + + // State variables: receiving side + m_vrR = 0; + m_vrMr = m_vrR + m_windowSize; + m_vrX = 0; + m_vrMs = 0; + m_vrH = 0; + + // Counters + m_pduWithoutPoll = 0; + m_byteWithoutPoll = 0; + + // Configurable parameters + m_maxRetxThreshold = 5; + m_pollPdu = 1; + m_pollByte = 50; + + // SDU reassembling process + m_reassemblingState = WAITING_S0_FULL; + m_expectedSeqNumber = 0; + + Simulator::ScheduleNow (&LteRlcAm::Start, this); +} + +LteRlcAm::~LteRlcAm () +{ +} + +TypeId +LteRlcAm::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::LteRlcAm") + .SetParent () + .AddConstructor () + ; + return tid; +} + + +/** + * RLC SAP + */ + +void +LteRlcAm::DoTransmitPdcpPdu (Ptr p) +{ + NS_LOG_FUNCTION (this); + + /** Store arrival time */ + Time now = Simulator::Now (); + RlcTag timeTag (now); + p->AddPacketTag (timeTag); + + /** Store PDCP PDU */ + + LteRlcSduStatusTag tag; + tag.SetStatus (LteRlcSduStatusTag::FULL_SDU); + p->AddPacketTag (tag); + + NS_LOG_LOGIC ("Txon Buffer: New packet added"); + m_txonBuffer.push_back (p); + m_txonBufferSize += p->GetSize (); + NS_LOG_LOGIC ("NumOfBuffers = " << m_txonBuffer.size() ); + NS_LOG_LOGIC ("txonBufferSize = " << m_txonBufferSize); + + /** Report Buffer Status */ + + // Transmission Queue HOL time + RlcTag txonQueueHolTimeTag; + m_txonBuffer.front ()->PeekPacketTag (txonQueueHolTimeTag); + Time txonQueueHolDelay = now - txonQueueHolTimeTag.GetSenderTimestamp (); + + // Retransmission Queue HOL time + RlcTag retxQueueHolTimeTag; + Time retxQueueHolDelay (0); + if ( m_retxBufferSize ) + { + m_retxBuffer.front ().m_pdu->PeekPacketTag (retxQueueHolTimeTag); + Time retxQueueHolDelay = now - retxQueueHolTimeTag.GetSenderTimestamp (); + } + + LteMacSapProvider::ReportBufferStatusParameters r; + r.rnti = m_rnti; + r.lcid = m_lcid; + r.txQueueSize = m_txonBufferSize; + r.txQueueHolDelay = txonQueueHolDelay.GetMilliSeconds (); + r.retxQueueSize = m_retxBufferSize; + r.retxQueueHolDelay = retxQueueHolDelay.GetMilliSeconds (); + + if ( m_statusPduRequested && ! m_statusProhibitTimer.IsRunning () ) + { + r.statusPduSize = m_statusPduBufferSize; + } + else + { + r.statusPduSize = 0; + } + + NS_LOG_LOGIC ("Send ReportBufferStatus"); + NS_LOG_LOGIC ("Txon queue size = " << r.txQueueSize); + NS_LOG_LOGIC ("HOL delay = " << r.txQueueHolDelay); + NS_LOG_LOGIC ("Retx queue size = " << r.retxQueueSize); + NS_LOG_LOGIC ("HOL delay = " << r.retxQueueHolDelay); + NS_LOG_LOGIC ("Status PDU size = " << r.statusPduSize); + m_macSapProvider->ReportBufferStatus (r); +} + + +/** + * MAC SAP + */ + +void +LteRlcAm::DoNotifyTxOpportunity (uint32_t bytes) +{ + NS_LOG_FUNCTION (this << bytes); + NS_ASSERT_MSG (bytes > 2, "Tx opportunity too small = " << bytes); + + if ( m_statusPduRequested && ! m_statusProhibitTimer.IsRunning () ) + { + NS_LOG_LOGIC ("Sending STATUS PDU"); + + Ptr packet = Create (); + LteRlcAmHeader rlcAmHeader; + rlcAmHeader.SetControlPdu (LteRlcAmHeader::STATUS_PDU); + rlcAmHeader.SetAckSn (m_vrR); + + NS_LOG_LOGIC ("AM RLC header: " << rlcAmHeader); + packet->AddHeader (rlcAmHeader); + + // Send RLC PDU to MAC layer + LteMacSapProvider::TransmitPduParameters params; + params.pdu = packet; + params.rnti = m_rnti; + params.lcid = m_lcid; + + m_macSapProvider->TransmitPdu (params); + } + else if ( m_retxBufferSize > 0 ) + { + NS_LOG_LOGIC ("Sending data from Retransmission Buffer"); + } + else if ( m_txonBufferSize > 0 ) + { + NS_LOG_LOGIC ("Sending data from Transmission Buffer"); + } + else + { + NS_LOG_LOGIC ("No data pending"); + return; + } + + // + // + // Build new PDU + // + // + + Ptr packet = Create (); + LteRlcAmHeader rlcAmHeader; + rlcAmHeader.SetDataPdu (); + + // Build Data field + uint32_t nextSegmentSize = bytes - 2; + uint32_t nextSegmentId = 1; + uint32_t dataFieldTotalSize = 0; + uint32_t dataFieldAddedSize = 0; + std::vector < Ptr > dataField; + + // Remove the first packet from the transmission buffer. + // If only a segment of the packet is taken, then the remaining is given back later + if ( m_txonBuffer.size () == 0 ) + { + NS_LOG_LOGIC ("No data pending"); + return; + } + + NS_LOG_LOGIC ("SDUs in TxonBuffer = " << m_txonBuffer.size ()); + NS_LOG_LOGIC ("First SDU buffer = " << *(m_txonBuffer.begin())); + NS_LOG_LOGIC ("First SDU size = " << (*(m_txonBuffer.begin()))->GetSize ()); + NS_LOG_LOGIC ("Next segment size = " << nextSegmentSize); + NS_LOG_LOGIC ("Remove SDU from TxBuffer"); + Ptr firstSegment = (*(m_txonBuffer.begin ()))->Copy (); + m_txonBufferSize -= (*(m_txonBuffer.begin()))->GetSize (); + NS_LOG_LOGIC ("txBufferSize = " << m_txonBufferSize ); + m_txonBuffer.erase (m_txonBuffer.begin ()); + + while ( firstSegment && (firstSegment->GetSize () > 0) && (nextSegmentSize > 0) ) + { + NS_LOG_LOGIC ("WHILE .. firstSegment && firstSegment->GetSize > 0 && nextSegmentSize > 0 .."); + NS_LOG_LOGIC (" FirstSegment size = " << firstSegment->GetSize ()); + NS_LOG_LOGIC (" Next segment size = " << nextSegmentSize); + if ( firstSegment->GetSize () > nextSegmentSize ) + { + NS_LOG_LOGIC (" IF firstSegment > NextSegmentSize"); + // Segment txBuffer.FirstBuffer and + // Give back the remaining segment to the transmission buffer + Ptr newSegment = firstSegment->CreateFragment (0, nextSegmentSize); + + // Status tag of the new and remaining segments + // Note: This is the only place where a PDU is segmented and + // therefore its status can change + LteRlcSduStatusTag oldTag, newTag; // TODO CreateFragment copy the tag??? + firstSegment->RemovePacketTag (oldTag); + newSegment->RemovePacketTag (newTag); + if (oldTag.GetStatus () == LteRlcSduStatusTag::FULL_SDU) + { + newTag.SetStatus (LteRlcSduStatusTag::FIRST_SEGMENT); + oldTag.SetStatus (LteRlcSduStatusTag::LAST_SEGMENT); + } + else if (oldTag.GetStatus () == LteRlcSduStatusTag::LAST_SEGMENT) + { + newTag.SetStatus (LteRlcSduStatusTag::MIDDLE_SEGMENT); + //oldTag.SetStatus (LteRlcSduStatusTag::LAST_SEGMENT); + } + firstSegment->AddPacketTag (oldTag); + newSegment->AddPacketTag (newTag); // TODO What happens if we add two tags??? + + // Give back the remaining segment to the transmission buffer + firstSegment->RemoveAtStart (nextSegmentSize); + m_txonBuffer.insert (m_txonBuffer.begin (), firstSegment); + m_txonBufferSize += (*(m_txonBuffer.begin()))->GetSize (); + firstSegment = 0; // TODO how to put a null ptr to Packet? + + NS_LOG_LOGIC (" TX buffer: Give back the remaining segment"); + NS_LOG_LOGIC (" TX buffers = " << m_txonBuffer.size ()); + NS_LOG_LOGIC (" Front buffer size = " << (*(m_txonBuffer.begin()))->GetSize ()); + NS_LOG_LOGIC (" txBufferSize = " << m_txonBufferSize ); + + // Add Segment to Data field + dataFieldAddedSize = newSegment->GetSize (); + dataFieldTotalSize += dataFieldAddedSize; + dataField.push_back (newSegment); + newSegment = 0; // TODO how to put a null ptr to Packet? + + // ExtensionBit (Next_Segment - 1) = 0 + rlcAmHeader.PushExtensionBit (LteRlcAmHeader::DATA_FIELD_FOLLOWS); + + // no LengthIndicator for the last one + + nextSegmentSize -= dataFieldAddedSize; + nextSegmentId++; + + // nextSegmentSize MUST be zero + + // (NO more segments) → exit + // break; + } + else if ( (firstSegment->GetSize () == nextSegmentSize) || (m_txonBuffer.size () == 0) ) + { + NS_LOG_LOGIC (" IF firstSegment == NextSegmentSize || txonBuffer.size == 0"); + // Add txBuffer.FirstBuffer to DataField + dataFieldAddedSize = firstSegment->GetSize (); + dataFieldTotalSize += dataFieldAddedSize; + dataField.push_back (firstSegment); + firstSegment = 0; // TODO how to put a null ptr to Packet? + + // ExtensionBit (Next_Segment - 1) = 0 + rlcAmHeader.PushExtensionBit (LteRlcAmHeader::DATA_FIELD_FOLLOWS); + + // no LengthIndicator for the last one + + nextSegmentSize -= dataFieldAddedSize; + nextSegmentId++; + + NS_LOG_LOGIC (" SDUs in TxBuffer = " << m_txonBuffer.size ()); + if (m_txonBuffer.size () > 0) + { + NS_LOG_LOGIC (" First SDU buffer = " << *(m_txonBuffer.begin())); + NS_LOG_LOGIC (" First SDU size = " << (*(m_txonBuffer.begin()))->GetSize ()); + } + NS_LOG_LOGIC (" Next segment size = " << nextSegmentSize << " (should be 0)"); + + // nextSegmentSize MUST be zero + + // (NO more segments) → exit + // break; + } + else // (firstSegment->GetSize () < m_nextSegmentSize) && (m_txBuffer.size () > 0) + { + NS_LOG_LOGIC (" IF firstSegment < NextSegmentSize && txonBuffer.size > 0"); + // Add txBuffer.FirstBuffer to DataField + dataFieldAddedSize = firstSegment->GetSize (); + dataFieldTotalSize += dataFieldAddedSize; + dataField.push_back (firstSegment); + + // ExtensionBit (Next_Segment - 1) = 1 + rlcAmHeader.PushExtensionBit (LteRlcAmHeader::E_LI_FIELDS_FOLLOWS); + + // LengthIndicator (Next_Segment) = txBuffer.FirstBuffer.length() + rlcAmHeader.PushLengthIndicator (firstSegment->GetSize ()); + + nextSegmentSize -= ((nextSegmentId % 2) ? (2) : (1)) + dataFieldAddedSize; // TODO??? + nextSegmentId++; + + NS_LOG_LOGIC (" SDUs in TxBuffer = " << m_txonBuffer.size ()); + if (m_txonBuffer.size () > 0) + { + NS_LOG_LOGIC (" First SDU buffer = " << *(m_txonBuffer.begin())); + NS_LOG_LOGIC (" First SDU size = " << (*(m_txonBuffer.begin()))->GetSize ()); + } + NS_LOG_LOGIC (" Next segment size = " << nextSegmentSize); + NS_LOG_LOGIC (" Remove SDU from TxBuffer"); + + // (more segments) + firstSegment = (*(m_txonBuffer.begin ()))->Copy (); + m_txonBufferSize -= (*(m_txonBuffer.begin()))->GetSize (); + m_txonBuffer.erase (m_txonBuffer.begin ()); + NS_LOG_LOGIC (" txBufferSize = " << m_txonBufferSize ); + } + + } + + // + // Build RLC header + // + + rlcAmHeader.SetSequenceNumber ( m_vtS ); + m_vtS = (m_vtS + 1) % 1024; + + rlcAmHeader.SetResegmentationFlag (LteRlcAmHeader::PDU); + rlcAmHeader.SetLastSegmentFlag (LteRlcAmHeader::LAST_PDU_SEGMENT); + rlcAmHeader.SetSegmentOffset (0); + + // Calculate FramingInfo flag according the status of the SDUs in the DataField + uint8_t framingInfo = 0; + std::vector< Ptr >::iterator it; + it = dataField.begin (); + + // FIRST SEGMENT + LteRlcSduStatusTag tag; + (*it)->RemovePacketTag (tag); + if ( (tag.GetStatus () == LteRlcSduStatusTag::FULL_SDU) || + (tag.GetStatus () == LteRlcSduStatusTag::FIRST_SEGMENT) + ) + { + framingInfo |= LteRlcAmHeader::FIRST_BYTE; + } + else + { + framingInfo |= LteRlcAmHeader::NO_FIRST_BYTE; + } + (*it)->AddPacketTag (tag); + + // Add all SDUs (in DataField) to the Packet + while (it < dataField.end ()) + { + NS_LOG_LOGIC ("Adding SDU/segment to packet, length = " << (*it)->GetSize ()); + + packet->AddAtEnd (*it); + it++; + } + + // LAST SEGMENT (Note: There could be only one and be the first one) + it--; + (*it)->RemovePacketTag (tag); + if ( (tag.GetStatus () == LteRlcSduStatusTag::FULL_SDU) || + (tag.GetStatus () == LteRlcSduStatusTag::LAST_SEGMENT) ) + { + framingInfo |= LteRlcAmHeader::LAST_BYTE; + } + else + { + framingInfo |= LteRlcAmHeader::NO_LAST_BYTE; + } + (*it)->AddPacketTag (tag); + + // Set the FramingInfo flag after the calculation + rlcAmHeader.SetFramingInfo (framingInfo); + + + // Calculate the Polling Bit (5.2.2.1) + rlcAmHeader.SetPollingBit (LteRlcAmHeader::STATUS_REPORT_NOT_REQUESTED); + + m_pduWithoutPoll++; + NS_LOG_LOGIC ("PDU_WITHOUT_POLL = " << m_pduWithoutPoll); + m_byteWithoutPoll += packet->GetSize (); + NS_LOG_LOGIC ("BYTE_WITHOUT_POLL = " << m_byteWithoutPoll); + + if ( (m_pduWithoutPoll >= m_pollPdu) || (m_byteWithoutPoll >= m_pollByte) || + ( (m_txonBuffer.empty ()) && (m_retxBuffer.empty ()) ) || + (m_vtS >= m_vtMs) + ) + { + rlcAmHeader.SetPollingBit (LteRlcAmHeader::STATUS_REPORT_IS_REQUESTED); + m_pduWithoutPoll = 0; + m_byteWithoutPoll = 0; + + m_pollSn = m_vtS - 1; + NS_LOG_LOGIC ("New POLL_SN = " << m_pollSn); + + if (! m_pollRetransmitTimer.IsRunning () ) + { + NS_LOG_LOGIC ("Start PollRetransmit timer"); + + m_pollRetransmitTimer = Simulator::Schedule (Time ("1.0s"), + &LteRlcAm::ExpirePollRetransmitTimer, this); + } + else + { + NS_LOG_LOGIC ("Restart PollRetransmit timer"); + + m_pollRetransmitTimer.Cancel (); + m_pollRetransmitTimer = Simulator::Schedule (Time ("1.0s"), + &LteRlcAm::ExpirePollRetransmitTimer, this); + } + } + + + // Build RLC PDU with DataField and Header + NS_LOG_LOGIC ("AM RLC header: " << rlcAmHeader); + packet->AddHeader (rlcAmHeader); + + // Store new PDU into the Transmitted PDU Buffer + NS_LOG_LOGIC ("Put transmitted PDU in the txedBuffer"); + m_txedBufferSize += packet->GetSize (); + m_txedBuffer.at ( rlcAmHeader.GetSequenceNumber () ) = packet; + + // Send RLC PDU to MAC layer + LteMacSapProvider::TransmitPduParameters params; + params.pdu = packet; + params.rnti = m_rnti; + params.lcid = m_lcid; + + m_macSapProvider->TransmitPdu (params); +} + +void +LteRlcAm::DoNotifyHarqDeliveryFailure () +{ + NS_LOG_FUNCTION (this); +} + +void +LteRlcAm::DoReceivePdu (Ptr p) +{ + NS_LOG_FUNCTION (this); + + // Get RLC header parameters + LteRlcAmHeader rlcAmHeader; + p->PeekHeader (rlcAmHeader); + + if ( rlcAmHeader.IsDataPdu () ) + { + + // 5.1.3.1 Transmit operations + + // 5.1.3.1.1 General + // + // The transmitting side of an AM RLC entity shall prioritize transmission of RLC control PDUs + // over RLC data PDUs. The transmitting side of an AM RLC entity shall prioritize retransmission + // of RLC data PDUs over transmission of new AMD PDUs. + // + // The transmitting side of an AM RLC entity shall maintain a transmitting window according to + // state variables VT(A) and VT(MS) as follows: + // - a SN falls within the transmitting window if VT(A) <= SN < VT(MS); + // - a SN falls outside of the transmitting window otherwise. + // + // The transmitting side of an AM RLC entity shall not deliver to lower layer any RLC data PDU + // whose SN falls outside of the transmitting window. + // + // When delivering a new AMD PDU to lower layer, the transmitting side of an AM RLC entity shall: + // - set the SN of the AMD PDU to VT(S), and then increment VT(S) by one. + // + // The transmitting side of an AM RLC entity can receive a positive acknowledgement (confirmation + // of successful reception by its peer AM RLC entity) for a RLC data PDU by the following: + // - STATUS PDU from its peer AM RLC entity. + // + // When receiving a positive acknowledgement for an AMD PDU with SN = VT(A), the transmitting + // side of an AM RLC entity shall: + // - set VT(A) equal to the SN of the AMD PDU with the smallest SN, whose SN falls within the + // range VT(A) <= SN <= VT(S) and for which a positive acknowledgment has not been received yet. + // - if positive acknowledgements have been received for all AMD PDUs associated with + // a transmitted RLC SDU: + // - send an indication to the upper layers of successful delivery of the RLC SDU. + + + // 5.1.3.2 Receive operations + // + // 5.1.3.2.1 General + // + // The receiving side of an AM RLC entity shall maintain a receiving window according to state + // variables VR(R) and VR(MR) as follows: + // - a SN falls within the receiving window if VR(R) <= SN < VR(MR); + // - a SN falls outside of the receiving window otherwise. + // + // When receiving a RLC data PDU from lower layer, the receiving side of an AM RLC entity shall: + // - either discard the received RLC data PDU or place it in the reception buffer (see sub clause 5.1.3.2.2); + // - if the received RLC data PDU was placed in the reception buffer: + // - update state variables, reassemble and deliver RLC SDUs to upper layer and start/stop t-Reordering as needed (see sub clause 5.1.3.2.3). + // + // When t-Reordering expires, the receiving side of an AM RLC entity shall: + // - update state variables and start t-Reordering as needed (see sub clause 5.1.3.2.4). + + + uint16_t seqNumber = rlcAmHeader.GetSequenceNumber (); + + if ( rlcAmHeader.GetResegmentationFlag () == LteRlcAmHeader::SEGMENT ) + { + NS_LOG_LOGIC ("AMD PDU segment received"); + } + else if ( rlcAmHeader.GetResegmentationFlag () == LteRlcAmHeader::PDU ) + { + NS_LOG_LOGIC ("AMD PDU received"); + } + else + { + NS_ASSERT_MSG (false, "Neither an AMD PDU segment nor a AMD PDU received"); + return ; + } + + // STATUS PDU is requested + if ( rlcAmHeader.GetPollingBit () == LteRlcAmHeader::STATUS_REPORT_IS_REQUESTED ) + { + m_statusPduRequested = true; + m_statusPduBufferSize = 4; + + if (! m_statusProhibitTimer.IsRunning ()) + { + Time now = Simulator::Now (); + + // Transmission Queue HOL time + RlcTag txonQueueHolTimeTag; + Time txonQueueHolDelay (0); + if ( ! m_txonBuffer.empty () ) + { + m_txonBuffer.front ()->PeekPacketTag (txonQueueHolTimeTag); + txonQueueHolDelay = now - txonQueueHolTimeTag.GetSenderTimestamp (); + } + + // Retransmission Queue HOL time + RlcTag retxQueueHolTimeTag; + Time retxQueueHolDelay (0); + if ( m_retxBufferSize ) + { + m_retxBuffer.front ().m_pdu->PeekPacketTag (retxQueueHolTimeTag); + Time retxQueueHolDelay = now - retxQueueHolTimeTag.GetSenderTimestamp (); + } + + LteMacSapProvider::ReportBufferStatusParameters r; + r.rnti = m_rnti; + r.lcid = m_lcid; + r.txQueueSize = m_txonBufferSize; + r.txQueueHolDelay = txonQueueHolDelay.GetMilliSeconds (); + r.retxQueueSize = m_retxBufferSize; + r.retxQueueHolDelay = retxQueueHolDelay.GetMilliSeconds (); + + if ( m_statusPduRequested && ! m_statusProhibitTimer.IsRunning () ) + { + r.statusPduSize = m_statusPduBufferSize; + } + else + { + r.statusPduSize = 0; + } + + NS_LOG_LOGIC ("Send ReportBufferStatus"); + NS_LOG_LOGIC ("Txon queue size = " << r.txQueueSize); + NS_LOG_LOGIC ("HOL delay = " << r.txQueueHolDelay); + NS_LOG_LOGIC ("Retx queue size = " << r.retxQueueSize); + NS_LOG_LOGIC ("HOL delay = " << r.retxQueueHolDelay); + NS_LOG_LOGIC ("Status PDU size = " << r.statusPduSize); + m_macSapProvider->ReportBufferStatus (r); + } + } + + // 5.1.3.2.2 Actions when a RLC data PDU is received from lower layer + // + // When a RLC data PDU is received from lower layer, where the RLC data PDU contains + // byte segment numbers y to z of an AMD PDU with SN = x, the receiving side of an AM RLC entity shall: + // - if x falls outside of the receiving window; or + // - if byte segment numbers y to z of the AMD PDU with SN = x have been received before: + // - discard the received RLC data PDU; + // - else: + // - place the received RLC data PDU in the reception buffer; + // - if some byte segments of the AMD PDU contained in the RLC data PDU have been received before: + // - discard the duplicate byte segments. + + NS_LOG_LOGIC ("VR(R) = " << m_vrR); + NS_LOG_LOGIC ("VR(MR) = " << m_vrMr); + NS_LOG_LOGIC ("VR(X) = " << m_vrX); + NS_LOG_LOGIC ("VR(MS) = " << m_vrMs); + NS_LOG_LOGIC ("VR(H) = " << m_vrH); + NS_LOG_LOGIC ("SN = " << seqNumber); + + // - if x falls outside of the receiving window; or + // - if byte segment numbers y to z of the AMD PDU with SN = x have been received before: + if ( ! IsInsideReceivingWindow (seqNumber) ) + { + NS_LOG_LOGIC ("AMD PDU discarded"); + return; + } + else + { + NS_LOG_LOGIC ("Place AMD PDU in the reception buffer. SN = " << seqNumber); + m_rxonBuffer[ seqNumber ].m_byteSegments.push_back (p); + m_rxonBuffer[ seqNumber ].m_pduComplete = true; + + // - if some byte segments of the AMD PDU contained in the RLC data PDU have been received before: + // - discard the duplicate byte segments. + } + + // 5.1.3.2.3 Actions when a RLC data PDU is placed in the reception buffer + // When a RLC data PDU with SN = x is placed in the reception buffer, + // the receiving side of an AM RLC entity shall: + + // - if x >= VR(H) + // - update VR(H) to x+ 1; + + if ( seqNumber >= m_vrH ) + { + m_vrH = (seqNumber + 1) % 1024; + NS_LOG_LOGIC ("New VR(H) = " << m_vrH); + } + + // - if all byte segments of the AMD PDU with SN = VR(MS) are received: + // - update VR(MS) to the SN of the first AMD PDU with SN > current VR(MS) for + // which not all byte segments have been received; + + if ( m_rxonBuffer[ m_vrMs ].m_pduComplete ) + { + while ( m_rxonBuffer[ m_vrMs ].m_pduComplete ) + { + m_vrMs = (m_vrMs + 1) % 1024; + } + NS_LOG_LOGIC ("New VR(MS) = " << m_vrMs); + } + + // - if x = VR(R): + // - if all byte segments of the AMD PDU with SN = VR(R) are received: + // - update VR(R) to the SN of the first AMD PDU with SN > current VR(R) for which not all byte segments have been received; + // - update VR(MR) to the updated VR(R) + AM_Window_Size; + // - reassemble RLC SDUs from any byte segments of AMD PDUs with SN that falls outside of the receiving window and in-sequence byte segments of the AMD PDU with SN = VR(R), remove RLC headers when doing so and deliver the reassembled RLC SDUs to upper layer in sequence if not delivered before; + + if ( seqNumber == m_vrR ) + { + if ( m_rxonBuffer[ seqNumber ].m_pduComplete ) + { + while ( m_rxonBuffer[ m_vrR ].m_pduComplete ) + { + m_vrR = (m_vrR + 1) % 1024; + } + NS_LOG_LOGIC ("New VR(R) = " << m_vrR); + m_vrMr = m_vrR + m_windowSize; + NS_LOG_LOGIC ("New VR(MR) = " << m_vrMr); + } + + NS_LOG_LOGIC ("Reassemble and Deliver. SN = " << seqNumber); + NS_ASSERT_MSG (m_rxonBuffer[ seqNumber ].m_byteSegments.size () == 1, + "Too many segments. PDU Reassembly process didn't work"); + ReassembleAndDeliver (m_rxonBuffer[ seqNumber ].m_byteSegments.front ()); + } + + // - if t-Reordering is running: + // - if VR(X) = VR(R); or + // - if VR(X) falls outside of the receiving window and VR(X) is not equal to VR(MR): + // - stop and reset t-Reordering; + + if ( m_reorderingTimer.IsRunning () ) + { + NS_LOG_LOGIC ("Reordering timer is running"); + if ( (m_vrX == m_vrR) || + ( (! IsInsideReceivingWindow (m_vrX)) && (m_vrX != m_vrMr) ) + ) + { + // TODO stop and reset the t-Reordering + NS_LOG_LOGIC ("Stop reordering timer"); + m_reorderingTimer.Cancel (); + } + } + + // - if t-Reordering is not running (includes the case t-Reordering is stopped due to actions above): + // - if VR (H) > VR(R): + // - start t-Reordering; + // - set VR(X) to VR(H). + + if ( ! m_reorderingTimer.IsRunning () ) + { + NS_LOG_LOGIC ("Reordering timer is not running"); + if ( m_vrH > m_vrR ) + { + NS_LOG_LOGIC ("Start reordering timer"); + m_reorderingTimer = Simulator::Schedule (Time ("1.0s"), + &LteRlcAm::ExpireReorderingTimer ,this); + m_vrX = m_vrH; + NS_LOG_LOGIC ("New VR(X) = " << m_vrX); + } + } + + + + // TODO To remove + + // 5.1.2.2.3 Actions when an UMD PDU is placed in the reception buffer + // When an UMD PDU with SN = x is placed in the reception buffer, the receiving UM RLC entity shall: + + // - if x falls outside of the reordering window: + // - update VR(UH) to x + 1; + // - reassemble RLC SDUs from any UMD PDUs with SN that falls outside of the reordering window, remove + // RLC headers when doing so and deliver the reassembled RLC SDUs to upper layer in ascending order of the + // RLC SN if not delivered before; + // - if VR(UR) falls outside of the reordering window: + // - set VR(UR) to (VR(UH) - UM_Window_Size); + +// if ( ! IsInsideReorderingWindow (seqNumber)) +// { +// NS_LOG_LOGIC ("SN outside the reordering window"); +// +// m_vrUh = seqNumber + 1; +// NS_LOG_LOGIC ("New VR(UH) = " << m_vrUh); +// +// ReassembleOutsideWindow (); +// +// if ( ! IsInsideReorderingWindow (m_vrUr) ) +// { +// m_vrUr = m_vrUh - m_windowSize; +// NS_LOG_LOGIC ("VR(UR) outside the reordering window"); +// NS_LOG_LOGIC ("New VR(UR) = " << m_vrUr); +// } +// } + + // - if the reception buffer contains an UMD PDU with SN = VR(UR): + // - update VR(UR) to the SN of the first UMD PDU with SN > current VR(UR) that has not been received; + // - reassemble RLC SDUs from any UMD PDUs with SN < updated VR(UR), remove RLC headers when doing + // so and deliver the reassembled RLC SDUs to upper layer in ascending order of the RLC SN if not delivered + // before; + +// if ( m_rxBuffer.count (m_vrUr) > 0 ) +// { +// NS_LOG_LOGIC ("Reception buffer contains SN = " << m_vrUr); +// +// std::map >::iterator it; +// uint16_t newVrUr; +// +// it = m_rxBuffer.find (m_vrUr); +// newVrUr = (it->first) + 1; +// while ( m_rxBuffer.count (newVrUr) > 0 ) +// { +// newVrUr++; +// } +// m_vrUr = newVrUr; +// NS_LOG_LOGIC ("New VR(UR) = " << m_vrUr); +// +// ReassembleSnLessThan (m_vrUr); +// } + + // - if t-Reordering is running: + // - if VR(UX) <= VR(UR); or + // - if VR(UX) falls outside of the reordering window and VR(UX) is not equal to VR(UH):: + // - stop and reset t-Reordering; +// if ( m_reorderingTimer.IsRunning () ) +// { +// NS_LOG_LOGIC ("Reordering timer is running"); +// +// if ( (m_vrUx <= m_vrUr) || +// ((! IsInsideReorderingWindow (m_vrUx)) && (m_vrUx != m_vrUh)) ) +// { +// NS_LOG_LOGIC ("Stop reordering timer"); +// m_reorderingTimer.Cancel (); +// } +// } + + // - if t-Reordering is not running (includes the case when t-Reordering is stopped due to actions above): + // - if VR(UH) > VR(UR): + // - start t-Reordering; + // - set VR(UX) to VR(UH). +// if ( ! m_reorderingTimer.IsRunning () ) +// { +// NS_LOG_LOGIC ("Reordering timer is not running"); +// +// if ( m_vrUx > m_vrUr ) +// { +// NS_LOG_LOGIC ("VR(UX) > VR(UR). " << m_vrUx << " > " << m_vrUr); +// NS_LOG_LOGIC ("Start reordering timer"); +// m_reorderingTimer = Simulator::Schedule (Time ("1.0s"), +// &LteRlcAm::ExpireReorderingTimer ,this); +// m_vrUx = m_vrUh; +// NS_LOG_LOGIC ("New VR(UX) = " << m_vrUx); +// } +// } + + } + else if ( rlcAmHeader.IsControlPdu () ) + { + NS_LOG_INFO ("Control AM RLC PDU"); + + uint16_t ackSn = rlcAmHeader.GetAckSn (); + uint16_t seqNumber = m_vtA; + + while (seqNumber < ackSn) + { + m_txedBufferSize -= m_txedBuffer.at (seqNumber)->GetSize (); + m_txedBuffer.at (seqNumber) = 0; + seqNumber++; + } + + while (seqNumber < m_vtS) + { + if (m_txedBuffer.at (seqNumber)) + { + m_retxBuffer.at (seqNumber).m_pdu = m_txedBuffer.at (seqNumber); + m_retxBuffer.at (seqNumber).m_retxCount = 0; + m_retxBufferSize += m_retxBuffer.at (seqNumber).m_pdu->GetSize (); + + m_txedBufferSize -= m_txedBuffer.at (seqNumber)->GetSize (); + m_txedBuffer.at (seqNumber) = 0; + } + else if (m_retxBuffer.at (seqNumber).m_pdu) + { + m_retxBuffer.at (seqNumber).m_retxCount++; + if (m_retxBuffer.at (seqNumber).m_retxCount >= m_maxRetxThreshold) + { + NS_LOG_INFO ("Max RETX_COUNT for SN = " << seqNumber); + } + } + + seqNumber++; + } + + return; + } + else + { + NS_LOG_WARN ("Wrong AM RLC PDU type"); + return; + } + +} + + +void +LteRlcAm::Start () +{ + NS_LOG_FUNCTION (this); + + LteMacSapProvider::ReportBufferStatusParameters p; + p.rnti = m_rnti; + p.lcid = m_lcid; + p.txQueueSize = 0; + p.txQueueHolDelay = 0; + p.retxQueueSize = 0; + p.retxQueueHolDelay = 0; + p.statusPduSize = 0; + + m_macSapProvider->ReportBufferStatus (p); +} + + +bool +LteRlcAm::IsInsideReceivingWindow (uint16_t seqNumber) +{ + if ( ( ((m_vrR - m_vrR) % 1024) <= ((seqNumber - m_vrR) % 1024) ) && + ( ((seqNumber - m_vrR) % 1024) < ((m_vrMr - m_vrR) % 1024) ) + ) + { + return true; + } + else + { + return false; + } +} + + +void +LteRlcAm::ReassembleAndDeliver (Ptr packet) +{ + LteRlcAmHeader rlcAmHeader; + packet->RemoveHeader (rlcAmHeader); + uint8_t framingInfo = rlcAmHeader.GetFramingInfo (); + uint16_t currSeqNumber = rlcAmHeader.GetSequenceNumber (); + bool expectedSnLost; + + if ( currSeqNumber != m_expectedSeqNumber ) + { + expectedSnLost = true; + NS_LOG_LOGIC ("There are losses. Expected SN = " << m_expectedSeqNumber << ". Current SN = " << currSeqNumber); + m_expectedSeqNumber = (currSeqNumber + 1) % 1024; + } + else + { + expectedSnLost = false; + NS_LOG_LOGIC ("No losses. Expected SN = " << m_expectedSeqNumber << ". Current SN = " << currSeqNumber); + m_expectedSeqNumber = (m_expectedSeqNumber + 1) % 1024; + } + + // Build list of SDUs + uint8_t extensionBit; + uint16_t lengthIndicator; + do + { + extensionBit = rlcAmHeader.PopExtensionBit (); + NS_LOG_LOGIC ("E = " << (uint16_t)extensionBit); + + if ( extensionBit == 0 ) + { + m_sdusBuffer.push_back (packet); + } + else // extensionBit == 1 + { + lengthIndicator = rlcAmHeader.PopLengthIndicator (); + NS_LOG_LOGIC ("LI = " << lengthIndicator); + + // Check if there is enough data in the packet + if ( lengthIndicator >= packet->GetSize () ) + { + NS_LOG_LOGIC ("INTERNAL ERROR: Not enough data in the packet (" << packet->GetSize () << "). Needed LI=" << lengthIndicator); + // TODO What to do in this case? Discard packet and continue? Or Assert? + } + + // Split packet in two fragments + Ptr data_field = packet->CreateFragment (0, lengthIndicator); + packet->RemoveAtStart (lengthIndicator); + + m_sdusBuffer.push_back (data_field); + } + } + while ( extensionBit == 1 ); + + std::list < Ptr >::iterator it; + + // Current reassembling state + if (m_reassemblingState == WAITING_S0_FULL) NS_LOG_LOGIC ("Reassembling State = 'WAITING_S0_FULL'"); + else if (m_reassemblingState == WAITING_SI_SF) NS_LOG_LOGIC ("Reassembling State = 'WAITING_SI_SF'"); + else NS_LOG_LOGIC ("Reassembling State = Unknown state"); + + // Received framing Info + NS_LOG_LOGIC ("Framing Info = " << (uint16_t)framingInfo); + + // Reassemble the list of SDUs (when there is no losses) + if (!expectedSnLost) + { + switch (m_reassemblingState) + { + case WAITING_S0_FULL: + switch (framingInfo) + { + case (LteRlcAmHeader::FIRST_BYTE | LteRlcAmHeader::LAST_BYTE): + m_reassemblingState = WAITING_S0_FULL; + + /** + * Deliver one or multiple PDUs + */ + for ( it = m_sdusBuffer.begin () ; it != m_sdusBuffer.end () ; it++ ) + { + m_rlcSapUser->ReceivePdcpPdu (*it); + } + m_sdusBuffer.clear (); + break; + + case (LteRlcAmHeader::FIRST_BYTE | LteRlcAmHeader::NO_LAST_BYTE): + m_reassemblingState = WAITING_SI_SF; + + /** + * Deliver full PDUs + */ + while ( m_sdusBuffer.size () > 1 ) + { + m_rlcSapUser->ReceivePdcpPdu (m_sdusBuffer.front ()); + m_sdusBuffer.pop_front (); + } + + /** + * Keep S0 + */ + m_keepS0 = m_sdusBuffer.front (); + m_sdusBuffer.pop_front (); + break; + + case (LteRlcAmHeader::NO_FIRST_BYTE | LteRlcAmHeader::LAST_BYTE): + case (LteRlcAmHeader::NO_FIRST_BYTE | LteRlcAmHeader::NO_LAST_BYTE): + default: + /** + * ERROR: Transition not possible + */ + NS_LOG_LOGIC ("INTERNAL ERROR: Transition not possible. FI = " << framingInfo); + break; + } + break; + + case WAITING_SI_SF: + switch (framingInfo) + { + case (LteRlcAmHeader::NO_FIRST_BYTE | LteRlcAmHeader::LAST_BYTE): + m_reassemblingState = WAITING_S0_FULL; + + /** + * Deliver (Kept)S0 + SN + */ + m_keepS0->AddAtEnd (m_sdusBuffer.front ()); + m_sdusBuffer.pop_front (); + m_rlcSapUser->ReceivePdcpPdu (m_keepS0); + + /** + * Deliver zero, one or multiple PDUs + */ + while ( ! m_sdusBuffer.empty () ) + { + m_rlcSapUser->ReceivePdcpPdu (m_sdusBuffer.front ()); + m_sdusBuffer.pop_front (); + } + break; + + case (LteRlcAmHeader::NO_FIRST_BYTE | LteRlcAmHeader::NO_LAST_BYTE): + m_reassemblingState = WAITING_SI_SF; + + /** + * Keep SI + */ + if ( m_sdusBuffer.size () == 1 ) + { + m_keepS0->AddAtEnd (m_sdusBuffer.front ()); + m_sdusBuffer.pop_front (); + } + else // m_sdusBuffer.size () > 1 + { + /** + * Deliver (Kept)S0 + SN + */ + m_keepS0->AddAtEnd (m_sdusBuffer.front ()); + m_sdusBuffer.pop_front (); + m_rlcSapUser->ReceivePdcpPdu (m_keepS0); + + /** + * Deliver zero, one or multiple PDUs + */ + while ( m_sdusBuffer.size () > 1 ) + { + m_rlcSapUser->ReceivePdcpPdu (m_sdusBuffer.front ()); + m_sdusBuffer.pop_front (); + } + + /** + * Keep S0 + */ + m_keepS0 = m_sdusBuffer.front (); + m_sdusBuffer.pop_front (); + } + break; + + case (LteRlcAmHeader::FIRST_BYTE | LteRlcAmHeader::LAST_BYTE): + case (LteRlcAmHeader::FIRST_BYTE | LteRlcAmHeader::NO_LAST_BYTE): + default: + /** + * ERROR: Transition not possible + */ + NS_LOG_LOGIC ("INTERNAL ERROR: Transition not possible. FI = " << framingInfo); + break; + } + break; + + default: + NS_LOG_LOGIC ("INTERNAL ERROR: Wrong reassembling state = " << m_reassemblingState); + break; + } + } + else // Reassemble the list of SDUs (when there are losses, i.e. the received SN is not the expected one) + { + switch (m_reassemblingState) + { + case WAITING_S0_FULL: + switch (framingInfo) + { + case (LteRlcAmHeader::FIRST_BYTE | LteRlcAmHeader::LAST_BYTE): + m_reassemblingState = WAITING_S0_FULL; + + /** + * Deliver one or multiple PDUs + */ + for ( it = m_sdusBuffer.begin () ; it != m_sdusBuffer.end () ; it++ ) + { + m_rlcSapUser->ReceivePdcpPdu (*it); + } + m_sdusBuffer.clear (); + break; + + case (LteRlcAmHeader::FIRST_BYTE | LteRlcAmHeader::NO_LAST_BYTE): + m_reassemblingState = WAITING_SI_SF; + + /** + * Deliver full PDUs + */ + while ( m_sdusBuffer.size () > 1 ) + { + m_rlcSapUser->ReceivePdcpPdu (m_sdusBuffer.front ()); + m_sdusBuffer.pop_front (); + } + + /** + * Keep S0 + */ + m_keepS0 = m_sdusBuffer.front (); + m_sdusBuffer.pop_front (); + break; + + case (LteRlcAmHeader::NO_FIRST_BYTE | LteRlcAmHeader::LAST_BYTE): + m_reassemblingState = WAITING_S0_FULL; + + /** + * Discard SN + */ + m_sdusBuffer.pop_front (); + + /** + * Deliver zero, one or multiple PDUs + */ + while ( ! m_sdusBuffer.empty () ) + { + m_rlcSapUser->ReceivePdcpPdu (m_sdusBuffer.front ()); + m_sdusBuffer.pop_front (); + } + break; + + case (LteRlcAmHeader::NO_FIRST_BYTE | LteRlcAmHeader::NO_LAST_BYTE): + if ( m_sdusBuffer.size () == 1 ) + { + m_reassemblingState = WAITING_S0_FULL; + } + else + { + m_reassemblingState = WAITING_SI_SF; + } + + /** + * Discard SI or SN + */ + m_sdusBuffer.pop_front (); + + if ( m_sdusBuffer.size () > 0 ) + { + /** + * Deliver zero, one or multiple PDUs + */ + while ( m_sdusBuffer.size () > 1 ) + { + m_rlcSapUser->ReceivePdcpPdu (m_sdusBuffer.front ()); + m_sdusBuffer.pop_front (); + } + + /** + * Keep S0 + */ + m_keepS0 = m_sdusBuffer.front (); + m_sdusBuffer.pop_front (); + } + break; + + default: + /** + * ERROR: Transition not possible + */ + NS_LOG_LOGIC ("INTERNAL ERROR: Transition not possible. FI = " << framingInfo); + break; + } + break; + + case WAITING_SI_SF: + switch (framingInfo) + { + case (LteRlcAmHeader::FIRST_BYTE | LteRlcAmHeader::LAST_BYTE): + m_reassemblingState = WAITING_S0_FULL; + + /** + * Discard S0 + */ + m_keepS0 = 0; + + /** + * Deliver one or multiple PDUs + */ + while ( ! m_sdusBuffer.empty () ) + { + m_rlcSapUser->ReceivePdcpPdu (m_sdusBuffer.front ()); + m_sdusBuffer.pop_front (); + } + break; + + case (LteRlcAmHeader::FIRST_BYTE | LteRlcAmHeader::NO_LAST_BYTE): + m_reassemblingState = WAITING_SI_SF; + + /** + * Discard S0 + */ + m_keepS0 = 0; + + /** + * Deliver zero, one or multiple PDUs + */ + while ( m_sdusBuffer.size () > 1 ) + { + m_rlcSapUser->ReceivePdcpPdu (m_sdusBuffer.front ()); + m_sdusBuffer.pop_front (); + } + + /** + * Keep S0 + */ + m_keepS0 = m_sdusBuffer.front (); + m_sdusBuffer.pop_front (); + + break; + + case (LteRlcAmHeader::NO_FIRST_BYTE | LteRlcAmHeader::LAST_BYTE): + m_reassemblingState = WAITING_S0_FULL; + + /** + * Discard S0 + */ + m_keepS0 = 0; + + /** + * Discard SI or SN + */ + m_sdusBuffer.pop_front (); + + /** + * Deliver zero, one or multiple PDUs + */ + while ( ! m_sdusBuffer.empty () ) + { + m_rlcSapUser->ReceivePdcpPdu (m_sdusBuffer.front ()); + m_sdusBuffer.pop_front (); + } + break; + + case (LteRlcAmHeader::NO_FIRST_BYTE | LteRlcAmHeader::NO_LAST_BYTE): + if ( m_sdusBuffer.size () == 1 ) + { + m_reassemblingState = WAITING_S0_FULL; + } + else + { + m_reassemblingState = WAITING_SI_SF; + } + + /** + * Discard S0 + */ + m_keepS0 = 0; + + /** + * Discard SI or SN + */ + m_sdusBuffer.pop_front (); + + if ( m_sdusBuffer.size () > 0 ) + { + /** + * Deliver zero, one or multiple PDUs + */ + while ( m_sdusBuffer.size () > 1 ) + { + m_rlcSapUser->ReceivePdcpPdu (m_sdusBuffer.front ()); + m_sdusBuffer.pop_front (); + } + + /** + * Keep S0 + */ + m_keepS0 = m_sdusBuffer.front (); + m_sdusBuffer.pop_front (); + } + break; + + default: + /** + * ERROR: Transition not possible + */ + NS_LOG_LOGIC ("INTERNAL ERROR: Transition not possible. FI = " << framingInfo); + break; + } + break; + + default: + NS_LOG_LOGIC ("INTERNAL ERROR: Wrong reassembling state = " << m_reassemblingState); + break; + } + } + +} + + +// TODO To remove +// void +// LteRlcAm::ReassembleOutsideWindow (void) +// { +// NS_LOG_LOGIC ("Reassemble Outside Window"); +// +// std::map >::iterator it; +// it = m_rxBuffer.begin (); +// +// while ( (it != m_rxBuffer.end ()) && ! IsInsideReorderingWindow (it->first) ) +// { +// NS_LOG_LOGIC ("SN = " << it->first); +// +// // Reassemble RLC SDUs and deliver the PDCP PDU to upper layer +// ReassembleAndDeliver (it->second); +// m_rxBuffer.erase (it); +// it++; +// } +// +// if (it != m_rxBuffer.end ()) +// { +// NS_LOG_LOGIC ("(SN = " << it->first << ") is inside the reordering window"); +// } +// } + + +// TODO To remove +// void +// LteRlcAm::ReassembleSnLessThan (uint16_t seqNumber) +// { +// NS_LOG_LOGIC ("Reassemble SN < updated VR(UR)" ); +// +// std::map >::iterator it; +// it = m_rxBuffer.begin (); +// +// while ( (it != m_rxBuffer.end ()) && (it->first < seqNumber) ) +// { +// NS_LOG_LOGIC ("SN = " << it->first); +// +// // Reassemble RLC SDUs and deliver the PDCP PDU to upper layer +// ReassembleAndDeliver (it->second); +// m_rxBuffer.erase (it); +// it++; +// } +// +// if (it != m_rxBuffer.end ()) +// { +// NS_LOG_LOGIC ("(SN = " << it->first << ") >= " << m_vrUr); +// } +// } + + +// TODO To check +void +LteRlcAm::ExpireReorderingTimer (void) +{ + NS_LOG_LOGIC ("Reordering Timer has expired"); + + // 5.1.3.2.4 Actions when t-Reordering expires + // When t-Reordering expires, the receiving side of an AM RLC entity shall: + // - update VR(MS) to the SN of the first AMD PDU with SN >= VR(X) for which not all byte segments + // have been received; + // - if VR(H) > VR(MS): + // - start t-Reordering; + // - set VR(X) to VR(H). + + m_vrMs = m_vrX; + while ( m_rxonBuffer[ m_vrMs ].m_pduComplete ) + { + m_vrMs = (m_vrMs + 1) % 1024; + } + NS_LOG_LOGIC ("New VR(MS) = " << m_vrMs); + + if ( m_vrH > m_vrMs ) + { + NS_LOG_LOGIC ("Start reordering timer"); + m_reorderingTimer = Simulator::Schedule (Time ("1.0s"), + &LteRlcAm::ExpireReorderingTimer ,this); + m_vrX = m_vrH; + NS_LOG_LOGIC ("New VR(MS) = " << m_vrMs); + } +} + +// TODO To check +void +LteRlcAm::ExpirePollRetransmitTimer (void) +{ + NS_LOG_LOGIC ("PollRetransmit Timer has expired"); + NS_LOG_LOGIC ("TODO To Check"); +} + + + +} // namespace ns3 diff --git a/src/lte/model/lte-rlc-am.h b/src/lte/model/lte-rlc-am.h new file mode 100644 index 000000000..b0da5b0b9 --- /dev/null +++ b/src/lte/model/lte-rlc-am.h @@ -0,0 +1,169 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Manuel Requena + */ + +#ifndef LTE_RLC_AM_H +#define LTE_RLC_AM_H + +#include "ns3/lte-rlc.h" + +#include +#include + +namespace ns3 { + +/** + * LTE RLC Acknowledged Mode (AM), see 3GPP TS 36.322 + */ +class LteRlcAm : public LteRlc +{ +public: + LteRlcAm (); + virtual ~LteRlcAm (); + static TypeId GetTypeId (void); + + /** + * RLC SAP + */ + virtual void DoTransmitPdcpPdu (Ptr p); + + /** + * MAC SAP + */ + virtual void DoNotifyTxOpportunity (uint32_t bytes); + virtual void DoNotifyHarqDeliveryFailure (); + virtual void DoReceivePdu (Ptr p); + + void Start (); + +private: + /** + * This method will schedule a timeout at WaitReplyTimeout interval + * in the future, unless a timer is already running for the cache, + * in which case this method does nothing. + */ + void ExpireReorderingTimer (void); + void ExpirePollRetransmitTimer (void); + + bool IsInsideReceivingWindow (uint16_t seqNumber); +// +// void ReassembleOutsideWindow (void); +// void ReassembleSnLessThan (uint16_t seqNumber); +// + void ReassembleAndDeliver (Ptr packet); + +private: + std::vector < Ptr > m_txonBuffer; // Transmission buffer + std::vector < Ptr > m_txedBuffer; // Transmitted packets buffer + + struct RetxBuffer + { + Ptr m_pdu; + uint16_t m_retxCount; + }; + + std::vector < RetxBuffer > m_retxBuffer; // Retransmission buffer + + uint32_t m_txonBufferSize; + uint32_t m_retxBufferSize; + uint32_t m_txedBufferSize; + + bool m_statusPduRequested; + uint32_t m_statusPduBufferSize; + + struct PduBuffer + { + uint16_t m_seqNumber; + std::list < Ptr > m_byteSegments; + + bool m_pduComplete; + uint16_t m_totalSize; + uint16_t m_currSize; + }; + + std::map m_rxonBuffer; // Reception buffer + + Ptr m_controlPduBuffer; // Control PDU buffer (just one PDU) + + // SDU reassembly +// std::vector < Ptr > m_reasBuffer; // Reassembling buffer +// + std::list < Ptr > m_sdusBuffer; // List of SDUs in a packet (PDU) + + /** + * State variables. See section 7.1 in TS 36.322 + */ + // Transmitting side + uint16_t m_vtA; // VT(A) + uint16_t m_vtMs; // VT(MS) + uint16_t m_vtS; // VT(S) + uint16_t m_pollSn; // POLL_SN + + // Receiving side + uint16_t m_vrR; // VR(R) + uint16_t m_vrMr; // VR(MR) + uint16_t m_vrX; // VR(X) + uint16_t m_vrMs; // VR(MS) + uint16_t m_vrH; // VR(H) + + /** + * Counters. See section 7.1 in TS 36.322 + */ + uint32_t m_pduWithoutPoll; + uint32_t m_byteWithoutPoll; + + /** + * Constants. See section 7.2 in TS 36.322 + */ + uint16_t m_windowSize; + + /** + * Timers. See section 7.3 in TS 36.322 + */ + EventId m_pollRetransmitTimer; + EventId m_reorderingTimer; + EventId m_statusProhibitTimer; + + /** + * Configurable parameters. See section 7.4 in TS 36.322 + */ + uint16_t m_maxRetxThreshold; // TODO How these parameters are configured??? + uint16_t m_pollPdu; + uint16_t m_pollByte; + + /** + * SDU Reassembling state + */ + typedef enum { NONE = 0, + WAITING_S0_FULL = 1, + WAITING_SI_SF = 2 } ReassemblingState_t; + ReassemblingState_t m_reassemblingState; + Ptr m_keepS0; + + /** + * Expected Sequence Number + */ + uint16_t m_expectedSeqNumber; + +}; + + +} // namespace ns3 + +#endif // LTE_RLC_AM_H