A Discrete-Event Network Simulator
API
lte-rlc-um.cc
Go to the documentation of this file.
1 /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * Author: Manuel Requena <manuel.requena@cttc.es>
19  */
20 
21 #include "ns3/simulator.h"
22 #include "ns3/log.h"
23 
24 #include "ns3/lte-rlc-header.h"
25 #include "ns3/lte-rlc-um.h"
26 #include "ns3/lte-rlc-sdu-status-tag.h"
27 #include "ns3/lte-rlc-tag.h"
28 
29 namespace ns3 {
30 
31 NS_LOG_COMPONENT_DEFINE ("LteRlcUm");
32 
34 
36  : m_maxTxBufferSize (10 * 1024),
37  m_txBufferSize (0),
38  m_sequenceNumber (0),
39  m_vrUr (0),
40  m_vrUx (0),
41  m_vrUh (0),
42  m_windowSize (512),
43  m_expectedSeqNumber (0)
44 {
45  NS_LOG_FUNCTION (this);
47 }
48 
50 {
51  NS_LOG_FUNCTION (this);
52 }
53 
54 TypeId
56 {
57  static TypeId tid = TypeId ("ns3::LteRlcUm")
58  .SetParent<LteRlc> ()
59  .SetGroupName("Lte")
60  .AddConstructor<LteRlcUm> ()
61  .AddAttribute ("MaxTxBufferSize",
62  "Maximum Size of the Transmission Buffer (in Bytes)",
63  UintegerValue (10 * 1024),
65  MakeUintegerChecker<uint32_t> ())
66  .AddAttribute ("ReorderingTimer",
67  "Value of the t-Reordering timer (See section 7.3 of 3GPP TS 36.322)",
68  TimeValue (MilliSeconds (100)),
70  MakeTimeChecker ())
71  ;
72  return tid;
73 }
74 
75 void
77 {
78  NS_LOG_FUNCTION (this);
80  m_rbsTimer.Cancel ();
81 
83 }
84 
89 void
91 {
92  NS_LOG_FUNCTION (this << m_rnti << (uint32_t) m_lcid << p->GetSize ());
93 
95  {
99  p->AddPacketTag (tag);
100 
101  NS_LOG_LOGIC ("Tx Buffer: New packet added");
102  m_txBuffer.push_back (TxPdu (p, Simulator::Now ()));
103  m_txBufferSize += p->GetSize ();
104  NS_LOG_LOGIC ("NumOfBuffers = " << m_txBuffer.size() );
105  NS_LOG_LOGIC ("txBufferSize = " << m_txBufferSize);
106  }
107  else
108  {
109  // Discard full RLC SDU
110  NS_LOG_LOGIC ("TxBuffer is full. RLC SDU discarded");
111  NS_LOG_LOGIC ("MaxTxBufferSize = " << m_maxTxBufferSize);
112  NS_LOG_LOGIC ("txBufferSize = " << m_txBufferSize);
113  NS_LOG_LOGIC ("packet size = " << p->GetSize ());
114  m_txDropTrace (p);
115  }
116 
119  m_rbsTimer.Cancel ();
120 }
121 
122 
127 void
129 {
130  NS_LOG_FUNCTION (this << m_rnti << (uint32_t) m_lcid << txOpParams.bytes);
131 
132  if (txOpParams.bytes <= 2)
133  {
134  // Stingy MAC: Header fix part is 2 bytes, we need more bytes for the data
135  NS_LOG_LOGIC ("TX opportunity too small = " << txOpParams.bytes);
136  return;
137  }
138 
139  Ptr<Packet> packet = Create<Packet> ();
140  LteRlcHeader rlcHeader;
141 
142  // Build Data field
143  uint32_t nextSegmentSize = txOpParams.bytes - 2;
144  uint32_t nextSegmentId = 1;
145  uint32_t dataFieldTotalSize = 0;
146  uint32_t dataFieldAddedSize = 0;
147  std::vector < Ptr<Packet> > dataField;
148 
149  // Remove the first packet from the transmission buffer.
150  // If only a segment of the packet is taken, then the remaining is given back later
151  if ( m_txBuffer.size () == 0 )
152  {
153  NS_LOG_LOGIC ("No data pending");
154  return;
155  }
156 
157  Ptr<Packet> firstSegment = m_txBuffer.begin ()->m_pdu->Copy ();
158  Time firstSegmentTime = m_txBuffer.begin ()->m_waitingSince;
159 
160  NS_LOG_LOGIC ("SDUs in TxBuffer = " << m_txBuffer.size ());
161  NS_LOG_LOGIC ("First SDU buffer = " << firstSegment);
162  NS_LOG_LOGIC ("First SDU size = " << firstSegment->GetSize ());
163  NS_LOG_LOGIC ("Next segment size = " << nextSegmentSize);
164  NS_LOG_LOGIC ("Remove SDU from TxBuffer");
165  m_txBufferSize -= firstSegment->GetSize ();
166  NS_LOG_LOGIC ("txBufferSize = " << m_txBufferSize );
167  m_txBuffer.erase (m_txBuffer.begin ());
168 
169  while ( firstSegment && (firstSegment->GetSize () > 0) && (nextSegmentSize > 0) )
170  {
171  NS_LOG_LOGIC ("WHILE ( firstSegment && firstSegment->GetSize > 0 && nextSegmentSize > 0 )");
172  NS_LOG_LOGIC (" firstSegment size = " << firstSegment->GetSize ());
173  NS_LOG_LOGIC (" nextSegmentSize = " << nextSegmentSize);
174  if ( (firstSegment->GetSize () > nextSegmentSize) ||
175  // Segment larger than 2047 octets can only be mapped to the end of the Data field
176  (firstSegment->GetSize () > 2047)
177  )
178  {
179  // Take the minimum size, due to the 2047-bytes 3GPP exception
180  // This exception is due to the length of the LI field (just 11 bits)
181  uint32_t currSegmentSize = std::min (firstSegment->GetSize (), nextSegmentSize);
182 
183  NS_LOG_LOGIC (" IF ( firstSegment > nextSegmentSize ||");
184  NS_LOG_LOGIC (" firstSegment > 2047 )");
185 
186  // Segment txBuffer.FirstBuffer and
187  // Give back the remaining segment to the transmission buffer
188  Ptr<Packet> newSegment = firstSegment->CreateFragment (0, currSegmentSize);
189  NS_LOG_LOGIC (" newSegment size = " << newSegment->GetSize ());
190 
191  // Status tag of the new and remaining segments
192  // Note: This is the only place where a PDU is segmented and
193  // therefore its status can change
194  LteRlcSduStatusTag oldTag, newTag;
195  firstSegment->RemovePacketTag (oldTag);
196  newSegment->RemovePacketTag (newTag);
197  if (oldTag.GetStatus () == LteRlcSduStatusTag::FULL_SDU)
198  {
199  newTag.SetStatus (LteRlcSduStatusTag::FIRST_SEGMENT);
200  oldTag.SetStatus (LteRlcSduStatusTag::LAST_SEGMENT);
201  }
202  else if (oldTag.GetStatus () == LteRlcSduStatusTag::LAST_SEGMENT)
203  {
204  newTag.SetStatus (LteRlcSduStatusTag::MIDDLE_SEGMENT);
205  //oldTag.SetStatus (LteRlcSduStatusTag::LAST_SEGMENT);
206  }
207 
208  // Give back the remaining segment to the transmission buffer
209  firstSegment->RemoveAtStart (currSegmentSize);
210  NS_LOG_LOGIC (" firstSegment size (after RemoveAtStart) = " << firstSegment->GetSize ());
211  if (firstSegment->GetSize () > 0)
212  {
213  firstSegment->AddPacketTag (oldTag);
214 
215  m_txBuffer.insert (m_txBuffer.begin (), TxPdu (firstSegment, firstSegmentTime));
216  m_txBufferSize += m_txBuffer.begin()->m_pdu->GetSize ();
217 
218  NS_LOG_LOGIC (" TX buffer: Give back the remaining segment");
219  NS_LOG_LOGIC (" TX buffers = " << m_txBuffer.size ());
220  NS_LOG_LOGIC (" Front buffer size = " << m_txBuffer.begin()->m_pdu->GetSize ());
221  NS_LOG_LOGIC (" txBufferSize = " << m_txBufferSize );
222  }
223  else
224  {
225  // Whole segment was taken, so adjust tag
226  if (newTag.GetStatus () == LteRlcSduStatusTag::FIRST_SEGMENT)
227  {
228  newTag.SetStatus (LteRlcSduStatusTag::FULL_SDU);
229  }
230  else if (newTag.GetStatus () == LteRlcSduStatusTag::MIDDLE_SEGMENT)
231  {
232  newTag.SetStatus (LteRlcSduStatusTag::LAST_SEGMENT);
233  }
234  }
235  // Segment is completely taken or
236  // the remaining segment is given back to the transmission buffer
237  firstSegment = 0;
238 
239  // Put status tag once it has been adjusted
240  newSegment->AddPacketTag (newTag);
241 
242  // Add Segment to Data field
243  dataFieldAddedSize = newSegment->GetSize ();
244  dataFieldTotalSize += dataFieldAddedSize;
245  dataField.push_back (newSegment);
246  newSegment = 0;
247 
248  // ExtensionBit (Next_Segment - 1) = 0
249  rlcHeader.PushExtensionBit (LteRlcHeader::DATA_FIELD_FOLLOWS);
250 
251  // no LengthIndicator for the last one
252 
253  nextSegmentSize -= dataFieldAddedSize;
254  nextSegmentId++;
255 
256  // nextSegmentSize MUST be zero (only if segment is smaller or equal to 2047)
257 
258  // (NO more segments) → exit
259  // break;
260  }
261  else if ( (nextSegmentSize - firstSegment->GetSize () <= 2) || (m_txBuffer.size () == 0) )
262  {
263  NS_LOG_LOGIC (" IF nextSegmentSize - firstSegment->GetSize () <= 2 || txBuffer.size == 0");
264  // Add txBuffer.FirstBuffer to DataField
265  dataFieldAddedSize = firstSegment->GetSize ();
266  dataFieldTotalSize += dataFieldAddedSize;
267  dataField.push_back (firstSegment);
268  firstSegment = 0;
269 
270  // ExtensionBit (Next_Segment - 1) = 0
271  rlcHeader.PushExtensionBit (LteRlcHeader::DATA_FIELD_FOLLOWS);
272 
273  // no LengthIndicator for the last one
274 
275  nextSegmentSize -= dataFieldAddedSize;
276  nextSegmentId++;
277 
278  NS_LOG_LOGIC (" SDUs in TxBuffer = " << m_txBuffer.size ());
279  if (m_txBuffer.size () > 0)
280  {
281  NS_LOG_LOGIC (" First SDU buffer = " << m_txBuffer.begin()->m_pdu);
282  NS_LOG_LOGIC (" First SDU size = " << m_txBuffer.begin()->m_pdu->GetSize ());
283  }
284  NS_LOG_LOGIC (" Next segment size = " << nextSegmentSize);
285 
286  // nextSegmentSize <= 2 (only if txBuffer is not empty)
287 
288  // (NO more segments) → exit
289  // break;
290  }
291  else // (firstSegment->GetSize () < m_nextSegmentSize) && (m_txBuffer.size () > 0)
292  {
293  NS_LOG_LOGIC (" IF firstSegment < NextSegmentSize && txBuffer.size > 0");
294  // Add txBuffer.FirstBuffer to DataField
295  dataFieldAddedSize = firstSegment->GetSize ();
296  dataFieldTotalSize += dataFieldAddedSize;
297  dataField.push_back (firstSegment);
298 
299  // ExtensionBit (Next_Segment - 1) = 1
300  rlcHeader.PushExtensionBit (LteRlcHeader::E_LI_FIELDS_FOLLOWS);
301 
302  // LengthIndicator (Next_Segment) = txBuffer.FirstBuffer.length()
303  rlcHeader.PushLengthIndicator (firstSegment->GetSize ());
304 
305  nextSegmentSize -= ((nextSegmentId % 2) ? (2) : (1)) + dataFieldAddedSize;
306  nextSegmentId++;
307 
308  NS_LOG_LOGIC (" SDUs in TxBuffer = " << m_txBuffer.size ());
309  if (m_txBuffer.size () > 0)
310  {
311  NS_LOG_LOGIC (" First SDU buffer = " << m_txBuffer.begin()->m_pdu);
312  NS_LOG_LOGIC (" First SDU size = " << m_txBuffer.begin()->m_pdu->GetSize ());
313  }
314  NS_LOG_LOGIC (" Next segment size = " << nextSegmentSize);
315  NS_LOG_LOGIC (" Remove SDU from TxBuffer");
316 
317  // (more segments)
318  firstSegment = m_txBuffer.begin ()->m_pdu->Copy ();
319  firstSegmentTime = m_txBuffer.begin ()->m_waitingSince;
320  m_txBufferSize -= firstSegment->GetSize ();
321  m_txBuffer.erase (m_txBuffer.begin ());
322  NS_LOG_LOGIC (" txBufferSize = " << m_txBufferSize );
323  }
324 
325  }
326 
327  // Build RLC header
328  rlcHeader.SetSequenceNumber (m_sequenceNumber++);
329 
330  // Build RLC PDU with DataField and Header
331  std::vector< Ptr<Packet> >::iterator it;
332  it = dataField.begin ();
333 
334  uint8_t framingInfo = 0;
335 
336  // FIRST SEGMENT
337  LteRlcSduStatusTag tag;
338  NS_ASSERT_MSG ((*it)->PeekPacketTag (tag), "LteRlcSduStatusTag is missing");
339  (*it)->PeekPacketTag (tag);
340  if ( (tag.GetStatus () == LteRlcSduStatusTag::FULL_SDU) ||
342  {
343  framingInfo |= LteRlcHeader::FIRST_BYTE;
344  }
345  else
346  {
347  framingInfo |= LteRlcHeader::NO_FIRST_BYTE;
348  }
349 
350  while (it < dataField.end ())
351  {
352  NS_LOG_LOGIC ("Adding SDU/segment to packet, length = " << (*it)->GetSize ());
353 
354  NS_ASSERT_MSG ((*it)->PeekPacketTag (tag), "LteRlcSduStatusTag is missing");
355  (*it)->RemovePacketTag (tag);
356  if (packet->GetSize () > 0)
357  {
358  packet->AddAtEnd (*it);
359  }
360  else
361  {
362  packet = (*it);
363  }
364  it++;
365  }
366 
367  // LAST SEGMENT (Note: There could be only one and be the first one)
368  it--;
369  if ( (tag.GetStatus () == LteRlcSduStatusTag::FULL_SDU) ||
371  {
372  framingInfo |= LteRlcHeader::LAST_BYTE;
373  }
374  else
375  {
376  framingInfo |= LteRlcHeader::NO_LAST_BYTE;
377  }
378 
379  rlcHeader.SetFramingInfo (framingInfo);
380 
381  NS_LOG_LOGIC ("RLC header: " << rlcHeader);
382  packet->AddHeader (rlcHeader);
383 
384  // Sender timestamp
385  RlcTag rlcTag (Simulator::Now ());
386  packet->AddByteTag (rlcTag, 1, rlcHeader.GetSerializedSize ());
387  m_txPdu (m_rnti, m_lcid, packet->GetSize ());
388 
389  // Send RLC PDU to MAC layer
391  params.pdu = packet;
392  params.rnti = m_rnti;
393  params.lcid = m_lcid;
394  params.layer = txOpParams.layer;
395  params.harqProcessId = txOpParams.harqId;
396  params.componentCarrierId = txOpParams.componentCarrierId;
397 
398  m_macSapProvider->TransmitPdu (params);
399 
400  if (! m_txBuffer.empty ())
401  {
402  m_rbsTimer.Cancel ();
404  }
405 }
406 
407 void
409 {
410  NS_LOG_FUNCTION (this);
411 }
412 
413 void
415 {
416  NS_LOG_FUNCTION (this << m_rnti << (uint32_t) m_lcid << rxPduParams.p->GetSize ());
417 
418  // Receiver timestamp
419  RlcTag rlcTag;
420  Time delay;
421 
422  bool ret = rxPduParams.p->FindFirstMatchingByteTag (rlcTag);
423  NS_ASSERT_MSG (ret, "RlcTag is missing");
424 
425  delay = Simulator::Now() - rlcTag.GetSenderTimestamp ();
426  m_rxPdu (m_rnti, m_lcid, rxPduParams.p->GetSize (), delay.GetNanoSeconds ());
427 
428  // 5.1.2.2 Receive operations
429 
430  // Get RLC header parameters
431  LteRlcHeader rlcHeader;
432  rxPduParams.p->PeekHeader (rlcHeader);
433  NS_LOG_LOGIC ("RLC header: " << rlcHeader);
434  SequenceNumber10 seqNumber = rlcHeader.GetSequenceNumber ();
435 
436  // 5.1.2.2.1 General
437  // The receiving UM RLC entity shall maintain a reordering window according to state variable VR(UH) as follows:
438  // - a SN falls within the reordering window if (VR(UH) - UM_Window_Size) <= SN < VR(UH);
439  // - a SN falls outside of the reordering window otherwise.
440  // When receiving an UMD PDU from lower layer, the receiving UM RLC entity shall:
441  // - either discard the received UMD PDU or place it in the reception buffer (see sub clause 5.1.2.2.2);
442  // - if the received UMD PDU was placed in the reception buffer:
443  // - update state variables, reassemble and deliver RLC SDUs to upper layer and start/stop t-Reordering as needed (see sub clause 5.1.2.2.3);
444  // When t-Reordering expires, the receiving UM RLC entity shall:
445  // - update state variables, reassemble and deliver RLC SDUs to upper layer and start t-Reordering as needed (see sub clause 5.1.2.2.4).
446 
447  // 5.1.2.2.2 Actions when an UMD PDU is received from lower layer
448  // When an UMD PDU with SN = x is received from lower layer, the receiving UM RLC entity shall:
449  // - if VR(UR) < x < VR(UH) and the UMD PDU with SN = x has been received before; or
450  // - if (VR(UH) - UM_Window_Size) <= x < VR(UR):
451  // - discard the received UMD PDU;
452  // - else:
453  // - place the received UMD PDU in the reception buffer.
454 
455  NS_LOG_LOGIC ("VR(UR) = " << m_vrUr);
456  NS_LOG_LOGIC ("VR(UX) = " << m_vrUx);
457  NS_LOG_LOGIC ("VR(UH) = " << m_vrUh);
458  NS_LOG_LOGIC ("SN = " << seqNumber);
459 
462  seqNumber.SetModulusBase (m_vrUh - m_windowSize);
463 
464  if ( ( (m_vrUr < seqNumber) && (seqNumber < m_vrUh) && (m_rxBuffer.count (seqNumber.GetValue ()) > 0) ) ||
465  ( ((m_vrUh - m_windowSize) <= seqNumber) && (seqNumber < m_vrUr) )
466  )
467  {
468  NS_LOG_LOGIC ("PDU discarded");
469  rxPduParams.p = 0;
470  return;
471  }
472  else
473  {
474  NS_LOG_LOGIC ("Place PDU in the reception buffer");
475  m_rxBuffer[seqNumber.GetValue ()] = rxPduParams.p;
476  }
477 
478 
479  // 5.1.2.2.3 Actions when an UMD PDU is placed in the reception buffer
480  // When an UMD PDU with SN = x is placed in the reception buffer, the receiving UM RLC entity shall:
481 
482  // - if x falls outside of the reordering window:
483  // - update VR(UH) to x + 1;
484  // - reassemble RLC SDUs from any UMD PDUs with SN that falls outside of the reordering window, remove
485  // RLC headers when doing so and deliver the reassembled RLC SDUs to upper layer in ascending order of the
486  // RLC SN if not delivered before;
487  // - if VR(UR) falls outside of the reordering window:
488  // - set VR(UR) to (VR(UH) - UM_Window_Size);
489 
490  if ( ! IsInsideReorderingWindow (seqNumber))
491  {
492  NS_LOG_LOGIC ("SN is outside the reordering window");
493 
494  m_vrUh = seqNumber + 1;
495  NS_LOG_LOGIC ("New VR(UH) = " << m_vrUh);
496 
498 
500  {
502  NS_LOG_LOGIC ("VR(UR) is outside the reordering window");
503  NS_LOG_LOGIC ("New VR(UR) = " << m_vrUr);
504  }
505  }
506 
507  // - if the reception buffer contains an UMD PDU with SN = VR(UR):
508  // - update VR(UR) to the SN of the first UMD PDU with SN > current VR(UR) that has not been received;
509  // - reassemble RLC SDUs from any UMD PDUs with SN < updated VR(UR), remove RLC headers when doing
510  // so and deliver the reassembled RLC SDUs to upper layer in ascending order of the RLC SN if not delivered
511  // before;
512 
513  if ( m_rxBuffer.count (m_vrUr.GetValue ()) > 0 )
514  {
515  NS_LOG_LOGIC ("Reception buffer contains SN = " << m_vrUr);
516 
517  std::map <uint16_t, Ptr<Packet> >::iterator it;
518  uint16_t newVrUr;
519  SequenceNumber10 oldVrUr = m_vrUr;
520 
521  it = m_rxBuffer.find (m_vrUr.GetValue ());
522  newVrUr = (it->first) + 1;
523  while ( m_rxBuffer.count (newVrUr) > 0 )
524  {
525  newVrUr++;
526  }
527  m_vrUr = newVrUr;
528  NS_LOG_LOGIC ("New VR(UR) = " << m_vrUr);
529 
530  ReassembleSnInterval (oldVrUr, m_vrUr);
531  }
532 
533  // m_vrUh can change previously, set new modulus base
534  // for the t-Reordering timer-related comparisons
538 
539  // - if t-Reordering is running:
540  // - if VR(UX) <= VR(UR); or
541  // - if VR(UX) falls outside of the reordering window and VR(UX) is not equal to VR(UH)::
542  // - stop and reset t-Reordering;
543  if ( m_reorderingTimer.IsRunning () )
544  {
545  NS_LOG_LOGIC ("Reordering timer is running");
546 
547  if ( (m_vrUx <= m_vrUr) ||
548  ((! IsInsideReorderingWindow (m_vrUx)) && (m_vrUx != m_vrUh)) )
549  {
550  NS_LOG_LOGIC ("Stop reordering timer");
552  }
553  }
554 
555  // - if t-Reordering is not running (includes the case when t-Reordering is stopped due to actions above):
556  // - if VR(UH) > VR(UR):
557  // - start t-Reordering;
558  // - set VR(UX) to VR(UH).
559  if ( ! m_reorderingTimer.IsRunning () )
560  {
561  NS_LOG_LOGIC ("Reordering timer is not running");
562 
563  if ( m_vrUh > m_vrUr )
564  {
565  NS_LOG_LOGIC ("VR(UH) > VR(UR)");
566  NS_LOG_LOGIC ("Start reordering timer");
569  m_vrUx = m_vrUh;
570  NS_LOG_LOGIC ("New VR(UX) = " << m_vrUx);
571  }
572  }
573 
574 }
575 
576 
577 bool
579 {
580  NS_LOG_FUNCTION (this << seqNumber);
581  NS_LOG_LOGIC ("Reordering Window: " <<
582  m_vrUh << " - " << m_windowSize << " <= " << seqNumber << " < " << m_vrUh);
583 
585  seqNumber.SetModulusBase (m_vrUh - m_windowSize);
586 
587  if ( ((m_vrUh - m_windowSize) <= seqNumber) && (seqNumber < m_vrUh))
588  {
589  NS_LOG_LOGIC (seqNumber << " is INSIDE the reordering window");
590  return true;
591  }
592  else
593  {
594  NS_LOG_LOGIC (seqNumber << " is OUTSIDE the reordering window");
595  return false;
596  }
597 }
598 
599 
600 void
602 {
603  LteRlcHeader rlcHeader;
604  packet->RemoveHeader (rlcHeader);
605  uint8_t framingInfo = rlcHeader.GetFramingInfo ();
606  SequenceNumber10 currSeqNumber = rlcHeader.GetSequenceNumber ();
607  bool expectedSnLost;
608 
609  if ( currSeqNumber != m_expectedSeqNumber )
610  {
611  expectedSnLost = true;
612  NS_LOG_LOGIC ("There are losses. Expected SN = " << m_expectedSeqNumber << ". Current SN = " << currSeqNumber);
613  m_expectedSeqNumber = currSeqNumber + 1;
614  }
615  else
616  {
617  expectedSnLost = false;
618  NS_LOG_LOGIC ("No losses. Expected SN = " << m_expectedSeqNumber << ". Current SN = " << currSeqNumber);
620  }
621 
622  // Build list of SDUs
623  uint8_t extensionBit;
624  uint16_t lengthIndicator;
625  do
626  {
627  extensionBit = rlcHeader.PopExtensionBit ();
628  NS_LOG_LOGIC ("E = " << (uint16_t)extensionBit);
629 
630  if ( extensionBit == 0 )
631  {
632  m_sdusBuffer.push_back (packet);
633  }
634  else // extensionBit == 1
635  {
636  lengthIndicator = rlcHeader.PopLengthIndicator ();
637  NS_LOG_LOGIC ("LI = " << lengthIndicator);
638 
639  // Check if there is enough data in the packet
640  if ( lengthIndicator >= packet->GetSize () )
641  {
642  NS_LOG_LOGIC ("INTERNAL ERROR: Not enough data in the packet (" << packet->GetSize () << "). Needed LI=" << lengthIndicator);
643  }
644 
645  // Split packet in two fragments
646  Ptr<Packet> data_field = packet->CreateFragment (0, lengthIndicator);
647  packet->RemoveAtStart (lengthIndicator);
648 
649  m_sdusBuffer.push_back (data_field);
650  }
651  }
652  while ( extensionBit == 1 );
653 
654  std::list < Ptr<Packet> >::iterator it;
655 
656  // Current reassembling state
657  if (m_reassemblingState == WAITING_S0_FULL) NS_LOG_LOGIC ("Reassembling State = 'WAITING_S0_FULL'");
658  else if (m_reassemblingState == WAITING_SI_SF) NS_LOG_LOGIC ("Reassembling State = 'WAITING_SI_SF'");
659  else NS_LOG_LOGIC ("Reassembling State = Unknown state");
660 
661  // Received framing Info
662  NS_LOG_LOGIC ("Framing Info = " << (uint16_t)framingInfo);
663 
664  // Reassemble the list of SDUs (when there is no losses)
665  if (!expectedSnLost)
666  {
667  switch (m_reassemblingState)
668  {
669  case WAITING_S0_FULL:
670  switch (framingInfo)
671  {
674 
678  for ( it = m_sdusBuffer.begin () ; it != m_sdusBuffer.end () ; it++ )
679  {
681  }
682  m_sdusBuffer.clear ();
683  break;
684 
687 
691  while ( m_sdusBuffer.size () > 1 )
692  {
694  m_sdusBuffer.pop_front ();
695  }
696 
700  m_keepS0 = m_sdusBuffer.front ();
701  m_sdusBuffer.pop_front ();
702  break;
703 
706 
710  m_sdusBuffer.pop_front ();
711 
715  while ( ! m_sdusBuffer.empty () )
716  {
718  m_sdusBuffer.pop_front ();
719  }
720  break;
721 
723  if ( m_sdusBuffer.size () == 1 )
724  {
726  }
727  else
728  {
730  }
731 
735  m_sdusBuffer.pop_front ();
736 
737  if ( m_sdusBuffer.size () > 0 )
738  {
742  while ( m_sdusBuffer.size () > 1 )
743  {
745  m_sdusBuffer.pop_front ();
746  }
747 
751  m_keepS0 = m_sdusBuffer.front ();
752  m_sdusBuffer.pop_front ();
753  }
754  break;
755 
756  default:
760  NS_LOG_LOGIC ("INTERNAL ERROR: Transition not possible. FI = " << (uint32_t) framingInfo);
761  break;
762  }
763  break;
764 
765  case WAITING_SI_SF:
766  switch (framingInfo)
767  {
770 
774  m_keepS0->AddAtEnd (m_sdusBuffer.front ());
775  m_sdusBuffer.pop_front ();
777 
781  while ( ! m_sdusBuffer.empty () )
782  {
784  m_sdusBuffer.pop_front ();
785  }
786  break;
787 
790 
794  if ( m_sdusBuffer.size () == 1 )
795  {
796  m_keepS0->AddAtEnd (m_sdusBuffer.front ());
797  m_sdusBuffer.pop_front ();
798  }
799  else // m_sdusBuffer.size () > 1
800  {
804  m_keepS0->AddAtEnd (m_sdusBuffer.front ());
805  m_sdusBuffer.pop_front ();
807 
811  while ( m_sdusBuffer.size () > 1 )
812  {
814  m_sdusBuffer.pop_front ();
815  }
816 
820  m_keepS0 = m_sdusBuffer.front ();
821  m_sdusBuffer.pop_front ();
822  }
823  break;
824 
827  default:
831  NS_LOG_LOGIC ("INTERNAL ERROR: Transition not possible. FI = " << (uint32_t) framingInfo);
832  break;
833  }
834  break;
835 
836  default:
837  NS_LOG_LOGIC ("INTERNAL ERROR: Wrong reassembling state = " << (uint32_t) m_reassemblingState);
838  break;
839  }
840  }
841  else // Reassemble the list of SDUs (when there are losses, i.e. the received SN is not the expected one)
842  {
843  switch (m_reassemblingState)
844  {
845  case WAITING_S0_FULL:
846  switch (framingInfo)
847  {
850 
854  for ( it = m_sdusBuffer.begin () ; it != m_sdusBuffer.end () ; it++ )
855  {
857  }
858  m_sdusBuffer.clear ();
859  break;
860 
863 
867  while ( m_sdusBuffer.size () > 1 )
868  {
870  m_sdusBuffer.pop_front ();
871  }
872 
876  m_keepS0 = m_sdusBuffer.front ();
877  m_sdusBuffer.pop_front ();
878  break;
879 
882 
886  m_sdusBuffer.pop_front ();
887 
891  while ( ! m_sdusBuffer.empty () )
892  {
894  m_sdusBuffer.pop_front ();
895  }
896  break;
897 
899  if ( m_sdusBuffer.size () == 1 )
900  {
902  }
903  else
904  {
906  }
907 
911  m_sdusBuffer.pop_front ();
912 
913  if ( m_sdusBuffer.size () > 0 )
914  {
918  while ( m_sdusBuffer.size () > 1 )
919  {
921  m_sdusBuffer.pop_front ();
922  }
923 
927  m_keepS0 = m_sdusBuffer.front ();
928  m_sdusBuffer.pop_front ();
929  }
930  break;
931 
932  default:
936  NS_LOG_LOGIC ("INTERNAL ERROR: Transition not possible. FI = " << (uint32_t) framingInfo);
937  break;
938  }
939  break;
940 
941  case WAITING_SI_SF:
942  switch (framingInfo)
943  {
946 
950  m_keepS0 = 0;
951 
955  while ( ! m_sdusBuffer.empty () )
956  {
958  m_sdusBuffer.pop_front ();
959  }
960  break;
961 
964 
968  m_keepS0 = 0;
969 
973  while ( m_sdusBuffer.size () > 1 )
974  {
976  m_sdusBuffer.pop_front ();
977  }
978 
982  m_keepS0 = m_sdusBuffer.front ();
983  m_sdusBuffer.pop_front ();
984 
985  break;
986 
989 
993  m_keepS0 = 0;
994 
998  m_sdusBuffer.pop_front ();
999 
1003  while ( ! m_sdusBuffer.empty () )
1004  {
1006  m_sdusBuffer.pop_front ();
1007  }
1008  break;
1009 
1011  if ( m_sdusBuffer.size () == 1 )
1012  {
1014  }
1015  else
1016  {
1018  }
1019 
1023  m_keepS0 = 0;
1024 
1028  m_sdusBuffer.pop_front ();
1029 
1030  if ( m_sdusBuffer.size () > 0 )
1031  {
1035  while ( m_sdusBuffer.size () > 1 )
1036  {
1038  m_sdusBuffer.pop_front ();
1039  }
1040 
1044  m_keepS0 = m_sdusBuffer.front ();
1045  m_sdusBuffer.pop_front ();
1046  }
1047  break;
1048 
1049  default:
1053  NS_LOG_LOGIC ("INTERNAL ERROR: Transition not possible. FI = " << (uint32_t) framingInfo);
1054  break;
1055  }
1056  break;
1057 
1058  default:
1059  NS_LOG_LOGIC ("INTERNAL ERROR: Wrong reassembling state = " << (uint32_t) m_reassemblingState);
1060  break;
1061  }
1062  }
1063 
1064 }
1065 
1066 
1067 void
1069 {
1070  NS_LOG_LOGIC ("Reassemble Outside Window");
1071 
1072  std::map <uint16_t, Ptr<Packet> >::iterator it;
1073  it = m_rxBuffer.begin ();
1074 
1075  while ( (it != m_rxBuffer.end ()) && ! IsInsideReorderingWindow (SequenceNumber10 (it->first)) )
1076  {
1077  NS_LOG_LOGIC ("SN = " << it->first);
1078 
1079  // Reassemble RLC SDUs and deliver the PDCP PDU to upper layer
1080  ReassembleAndDeliver (it->second);
1081 
1082  std::map <uint16_t, Ptr<Packet> >::iterator it_tmp = it;
1083  ++it;
1084  m_rxBuffer.erase (it_tmp);
1085  }
1086 
1087  if (it != m_rxBuffer.end ())
1088  {
1089  NS_LOG_LOGIC ("(SN = " << it->first << ") is inside the reordering window");
1090  }
1091 }
1092 
1093 void
1095 {
1096  NS_LOG_LOGIC ("Reassemble SN between " << lowSeqNumber << " and " << highSeqNumber);
1097 
1098  std::map <uint16_t, Ptr<Packet> >::iterator it;
1099 
1100  SequenceNumber10 reassembleSn = lowSeqNumber;
1101  NS_LOG_LOGIC ("reassembleSN = " << reassembleSn);
1102  NS_LOG_LOGIC ("highSeqNumber = " << highSeqNumber);
1103  while (reassembleSn < highSeqNumber)
1104  {
1105  NS_LOG_LOGIC ("reassembleSn < highSeqNumber");
1106  it = m_rxBuffer.find (reassembleSn.GetValue ());
1107  NS_LOG_LOGIC ("it->first = " << it->first);
1108  NS_LOG_LOGIC ("it->second = " << it->second);
1109  if (it != m_rxBuffer.end () )
1110  {
1111  NS_LOG_LOGIC ("SN = " << it->first);
1112 
1113  // Reassemble RLC SDUs and deliver the PDCP PDU to upper layer
1114  ReassembleAndDeliver (it->second);
1115 
1116  m_rxBuffer.erase (it);
1117  }
1118 
1119  reassembleSn++;
1120  }
1121 }
1122 
1123 
1124 void
1126 {
1127  Time holDelay (0);
1128  uint32_t queueSize = 0;
1129 
1130  if (! m_txBuffer.empty ())
1131  {
1132  holDelay = Simulator::Now () - m_txBuffer.front().m_waitingSince;
1133 
1134  queueSize = m_txBufferSize + 2 * m_txBuffer.size (); // Data in tx queue + estimated headers size
1135  }
1136 
1138  r.rnti = m_rnti;
1139  r.lcid = m_lcid;
1140  r.txQueueSize = queueSize;
1141  r.txQueueHolDelay = holDelay.GetMilliSeconds () ;
1142  r.retxQueueSize = 0;
1143  r.retxQueueHolDelay = 0;
1144  r.statusPduSize = 0;
1145 
1146  NS_LOG_LOGIC ("Send ReportBufferStatus = " << r.txQueueSize << ", " << r.txQueueHolDelay );
1148 }
1149 
1150 
1151 void
1153 {
1154  NS_LOG_FUNCTION (this << m_rnti << (uint32_t) m_lcid);
1155  NS_LOG_LOGIC ("Reordering timer has expired");
1156 
1157  // 5.1.2.2.4 Actions when t-Reordering expires
1158  // When t-Reordering expires, the receiving UM RLC entity shall:
1159  // - update VR(UR) to the SN of the first UMD PDU with SN >= VR(UX) that has not been received;
1160  // - reassemble RLC SDUs from any UMD PDUs with SN < updated VR(UR), remove RLC headers when doing so
1161  // and deliver the reassembled RLC SDUs to upper layer in ascending order of the RLC SN if not delivered before;
1162  // - if VR(UH) > VR(UR):
1163  // - start t-Reordering;
1164  // - set VR(UX) to VR(UH).
1165 
1166  std::map <uint16_t, Ptr<Packet> >::iterator it;
1167  SequenceNumber10 newVrUr = m_vrUx;
1168 
1169  while ( (it = m_rxBuffer.find (newVrUr.GetValue ())) != m_rxBuffer.end () )
1170  {
1171  newVrUr++;
1172  }
1173  SequenceNumber10 oldVrUr = m_vrUr;
1174  m_vrUr = newVrUr;
1175  NS_LOG_LOGIC ("New VR(UR) = " << m_vrUr);
1176 
1177  ReassembleSnInterval (oldVrUr, m_vrUr);
1178 
1179  if ( m_vrUh > m_vrUr)
1180  {
1181  NS_LOG_LOGIC ("Start reordering timer");
1184  m_vrUx = m_vrUh;
1185  NS_LOG_LOGIC ("New VR(UX) = " << m_vrUx);
1186  }
1187 }
1188 
1189 
1190 void
1192 {
1193  NS_LOG_LOGIC ("RBS Timer expires");
1194 
1195  if (! m_txBuffer.empty ())
1196  {
1199  }
1200 }
1201 
1202 } // namespace ns3
uint32_t m_maxTxBufferSize
maximum transmit buffer status
Definition: lte-rlc-um.h:98
bool FindFirstMatchingByteTag(Tag &tag) const
Finds the first tag matching the parameter Tag type.
Definition: packet.cc:939
uint32_t RemoveHeader(Header &header)
Deserialize and remove the header from the internal buffer.
Definition: packet.cc:280
TracedCallback< uint16_t, uint8_t, uint32_t > m_txPdu
Used to inform of a PDU delivery to the MAC SAP provider.
Definition: lte-rlc.h:174
static EventId Schedule(Time const &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:557
virtual uint32_t GetSerializedSize() const
Definition: lte-rlc-tag.cc:60
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:103
Store an incoming (from layer above us) PDU, waiting to transmit it.
Definition: lte-rlc-um.h:103
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by "...
Parameters for LteMacSapUser::NotifyTxOpportunity.
Definition: lte-mac-sap.h:103
LTE RLC Unacknowledged Mode (UM), see 3GPP TS 36.322.
Definition: lte-rlc-um.h:35
uint16_t GetValue() const
Extracts the numeric value of the sequence number.
virtual ~LteRlcUm()
Definition: lte-rlc-um.cc:49
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:45
uint32_t GetSize(void) const
Returns the the size in bytes of the packet (including the zero-filled initial payload).
Definition: packet.h:852
#define min(a, b)
Definition: 80211b.c:42
void ReassembleAndDeliver(Ptr< Packet > packet)
Reassemble and deliver function.
Definition: lte-rlc-um.cc:601
void DoReportBufferStatus()
Report buffer status.
Definition: lte-rlc-um.cc:1125
Tag to calculate the per-PDU delay from eNb RLC to UE RLC.
Definition: lte-rlc-tag.h:36
Ptr< Packet > CreateFragment(uint32_t start, uint32_t length) const
Create a new packet which contains a fragment of the original packet.
Definition: packet.cc:227
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1297
Time m_reorderingTimerValue
Timers.
Definition: lte-rlc-um.h:144
uint32_t retxQueueSize
the current size of the RLC retransmission queue in bytes
Definition: lte-mac-sap.h:73
virtual void DoTransmitPdcpPdu(Ptr< Packet > p)
RLC SAP.
Definition: lte-rlc-um.cc:90
std::vector< TxPdu > m_txBuffer
Transmission buffer.
Definition: lte-rlc-um.h:121
uint8_t PopExtensionBit(void)
Pop extension bit.
uint16_t m_rnti
RNTI.
Definition: lte-rlc.h:168
uint8_t m_lcid
LCID.
Definition: lte-rlc.h:169
virtual void DoNotifyHarqDeliveryFailure()
Notify HARQ delivery failure.
Definition: lte-rlc-um.cc:408
SequenceNumber10 m_vrUr
VR(UR)
Definition: lte-rlc-um.h:132
uint16_t rnti
the C-RNTI identifying the UE
Definition: lte-mac-sap.h:69
TracedCallback< Ptr< const Packet > > m_txDropTrace
The trace source fired when the RLC drops a packet before transmission.
Definition: lte-rlc.h:183
SequenceNumber10 m_sequenceNumber
State variables.
Definition: lte-rlc-um.h:130
void ReassembleSnInterval(SequenceNumber10 lowSeqNumber, SequenceNumber10 highSeqNumber)
Reassemble SN interval function.
Definition: lte-rlc-um.cc:1094
void SetModulusBase(SequenceNumber10 modulusBase)
Set modulus base.
The packet header for the Radio Link Control (RLC) protocol packets.
Parameters for LteMacSapProvider::ReportBufferStatus.
Definition: lte-mac-sap.h:67
virtual void DoNotifyTxOpportunity(LteMacSapUser::TxOpportunityParameters txOpParams)
MAC SAP.
Definition: lte-rlc-um.cc:128
SequenceNumber10 m_vrUh
VR(UH)
Definition: lte-rlc-um.h:134
uint16_t txQueueHolDelay
the Head Of Line delay of the transmission queue
Definition: lte-mac-sap.h:72
void AddAtEnd(Ptr< const Packet > packet)
Concatenate the input packet at the end of the current packet.
Definition: packet.cc:335
uint16_t PopLengthIndicator(void)
Pop length indicator.
uint8_t GetStatus(void) const
Get status function.
LteRlcSapUser * m_rlcSapUser
RLC SAP user.
Definition: lte-rlc.h:144
SequenceNumber10 m_expectedSeqNumber
Expected Sequence Number.
Definition: lte-rlc-um.h:160
void RemoveAtStart(uint32_t size)
Remove size bytes from the start of the current packet.
Definition: packet.cc:362
AttributeValue implementation for Time.
Definition: nstime.h:1353
static TypeId GetTypeId(void)
Get the type ID.
Definition: lte-rlc-um.cc:55
Hold an unsigned integer type.
Definition: uinteger.h:44
ReassemblingState_t m_reassemblingState
reassembling state
Definition: lte-rlc-um.h:154
uint8_t componentCarrierId
the component carrier id
Definition: lte-mac-sap.h:132
virtual void ReceivePdcpPdu(Ptr< Packet > p)=0
Called by the RLC entity to notify the PDCP entity of the reception of a new PDCP PDU...
virtual void DoDispose()
Destructor implementation.
Definition: lte-rlc.cc:125
uint32_t PeekHeader(Header &header) const
Deserialize but does not remove the header from the internal buffer.
Definition: packet.cc:290
LteMacSapProvider * m_macSapProvider
MAC SAP provider.
Definition: lte-rlc.h:166
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:289
Ptr< Packet > m_keepS0
keep S0
Definition: lte-rlc-um.h:155
Every class exported by the ns3 library is enclosed in the ns3 namespace.
int64_t GetNanoSeconds(void) const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:392
SequenceNumber10 m_vrUx
VR(UX)
Definition: lte-rlc-um.h:133
uint16_t m_windowSize
Constants.
Definition: lte-rlc-um.h:139
void ExpireReorderingTimer(void)
Expire reordering timer.
Definition: lte-rlc-um.cc:1152
SequenceNumber10 GetSequenceNumber() const
Get sequence number.
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method...
Definition: nstime.h:1354
uint8_t lcid
the logical channel id corresponding to the sending RLC instance
Definition: lte-mac-sap.h:70
uint32_t txQueueSize
the current size of the RLC transmission queue
Definition: lte-mac-sap.h:71
static Time Now(void)
Return the current simulation virtual time.
Definition: simulator.cc:195
std::map< uint16_t, Ptr< Packet > > m_rxBuffer
Reception buffer.
Definition: lte-rlc-um.h:122
EventId m_reorderingTimer
reordering timer
Definition: lte-rlc-um.h:145
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:88
uint32_t bytes
the number of bytes to transmit
Definition: lte-mac-sap.h:129
uint16_t statusPduSize
the current size of the pending STATUS RLC PDU message in bytes
Definition: lte-mac-sap.h:75
std::list< Ptr< Packet > > m_sdusBuffer
List of SDUs in a packet.
Definition: lte-rlc-um.h:125
uint8_t layer
the layer of transmission (MIMO)
Definition: lte-mac-sap.h:130
void AddPacketTag(const Tag &tag) const
Add a packet tag.
Definition: packet.cc:956
This class implements a tag that carries the status of a RLC SDU for the fragmentation process Status...
int64_t GetMilliSeconds(void) const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:384
bool RemovePacketTag(Tag &tag)
Remove a packet tag.
Definition: packet.cc:963
void ReassembleOutsideWindow(void)
Reassemble outside window.
Definition: lte-rlc-um.cc:1068
bool IsInsideReorderingWindow(SequenceNumber10 seqNumber)
Is inside reordering window function.
Definition: lte-rlc-um.cc:578
bool IsRunning(void) const
This method is syntactic sugar for !IsExpired().
Definition: event-id.cc:71
void SetStatus(uint8_t status)
Set status function.
virtual void DoDispose()
Destructor implementation.
Definition: lte-rlc-um.cc:76
uint16_t retxQueueHolDelay
the Head Of Line delay of the retransmission queue
Definition: lte-mac-sap.h:74
void Cancel(void)
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition: event-id.cc:53
virtual void ReportBufferStatus(ReportBufferStatusParameters params)=0
Report the RLC buffer status to the MAC.
uint32_t m_txBufferSize
transmit buffer size
Definition: lte-rlc-um.h:99
Parameters for LteMacSapUser::ReceivePdu.
Definition: lte-mac-sap.h:156
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:533
virtual void DoReceivePdu(LteMacSapUser::ReceivePduParameters rxPduParams)
Receive PDU function.
Definition: lte-rlc-um.cc:414
void ExpireRbsTimer(void)
Expire RBS timer.
Definition: lte-rlc-um.cc:1191
SequenceNumber10 class.
TracedCallback< uint16_t, uint8_t, uint32_t, uint64_t > m_rxPdu
Used to inform of a PDU reception from the MAC SAP user.
Definition: lte-rlc.h:178
EventId m_rbsTimer
RBS timer.
Definition: lte-rlc-um.h:146
uint8_t GetFramingInfo() const
Get framing info.
virtual void TransmitPdu(TransmitPduParameters params)=0
send an RLC PDU to the MAC for transmission.
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method...
Definition: uinteger.h:45
This abstract base class defines the API to interact with the Radio Link Control (LTE_RLC) in LTE...
Definition: lte-rlc.h:50
a unique identifier for an interface.
Definition: type-id.h:58
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:923
void AddByteTag(const Tag &tag) const
Tag each byte included in this packet with a new byte tag.
Definition: packet.cc:912
void AddHeader(const Header &header)
Add header to this packet.
Definition: packet.cc:256
Ptr< Packet > p
the RLC PDU to be received
Definition: lte-mac-sap.h:175
Parameters for LteMacSapProvider::TransmitPdu.
Definition: lte-mac-sap.h:45