1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013
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: Ghada Badawy <gbadawy@gmail.com>
19  *         Stefano Avallone <stavallo@unina.it>
20  */
21 
22 #include "ns3/log.h"
23 #include "ns3/packet.h"
24 #include "mpdu-aggregator.h"
25 #include "ampdu-subframe-header.h"
26 #include "wifi-phy.h"
27 #include "wifi-tx-vector.h"
28 #include "wifi-remote-station-manager.h"
29 #include "wifi-mac-queue-item.h"
30 #include "wifi-mac-queue.h"
31 #include "msdu-aggregator.h"
32 #include "wifi-net-device.h"
33 #include "ns3/ht-capabilities.h"
34 #include "ns3/vht-capabilities.h"
35 #include "ns3/he-capabilities.h"
36 #include "regular-wifi-mac.h"
37 #include "ctrl-headers.h"
38 #include "wifi-mac-trailer.h"
39 #include "wifi-tx-parameters.h"
40 
41 NS_LOG_COMPONENT_DEFINE ("MpduAggregator");
42 
43 namespace ns3 {
44 
45 NS_OBJECT_ENSURE_REGISTERED (MpduAggregator);
46 
47 TypeId
GetTypeId(void)48 MpduAggregator::GetTypeId (void)
49 {
50   static TypeId tid = TypeId ("ns3::MpduAggregator")
51     .SetParent<Object> ()
52     .SetGroupName ("Wifi")
53     .AddConstructor<MpduAggregator> ()
54   ;
55   return tid;
56 }
57 
MpduAggregator()58 MpduAggregator::MpduAggregator ()
59 {
60 }
61 
~MpduAggregator()62 MpduAggregator::~MpduAggregator ()
63 {
64 }
65 
66 void
DoDispose()67 MpduAggregator::DoDispose ()
68 {
69   m_mac = 0;
70   Object::DoDispose ();
71 }
72 
73 void
SetWifiMac(const Ptr<RegularWifiMac> mac)74 MpduAggregator::SetWifiMac (const Ptr<RegularWifiMac> mac)
75 {
76   NS_LOG_FUNCTION (this << mac);
77   m_mac = mac;
78 }
79 
80 void
Aggregate(Ptr<const WifiMacQueueItem> mpdu,Ptr<Packet> ampdu,bool isSingle)81 MpduAggregator::Aggregate (Ptr<const WifiMacQueueItem> mpdu, Ptr<Packet> ampdu, bool isSingle)
82 {
83   NS_LOG_FUNCTION (mpdu << ampdu << isSingle);
84   NS_ASSERT (ampdu);
85   // if isSingle is true, then ampdu must be empty
86   NS_ASSERT (!isSingle || ampdu->GetSize () == 0);
87 
88   // pad the previous A-MPDU subframe if the A-MPDU is not empty
89   if (ampdu->GetSize () > 0)
90     {
91       uint8_t padding = CalculatePadding (ampdu->GetSize ());
92 
93       if (padding)
94         {
95           Ptr<Packet> pad = Create<Packet> (padding);
96           ampdu->AddAtEnd (pad);
97         }
98     }
99 
100   // add MPDU header and trailer
101   Ptr<Packet> tmp = mpdu->GetPacket ()->Copy ();
102   tmp->AddHeader (mpdu->GetHeader ());
103   AddWifiMacTrailer (tmp);
104 
105   // add A-MPDU subframe header and MPDU to the A-MPDU
106   AmpduSubframeHeader hdr = GetAmpduSubframeHeader (static_cast<uint16_t> (tmp->GetSize ()), isSingle);
107 
108   tmp->AddHeader (hdr);
109   ampdu->AddAtEnd (tmp);
110 }
111 
112 uint32_t
GetSizeIfAggregated(uint32_t mpduSize,uint32_t ampduSize)113 MpduAggregator::GetSizeIfAggregated (uint32_t mpduSize, uint32_t ampduSize)
114 {
115   NS_LOG_FUNCTION (mpduSize << ampduSize);
116 
117   return ampduSize + CalculatePadding (ampduSize) + 4 + mpduSize;
118 }
119 
120 uint32_t
GetMaxAmpduSize(Mac48Address recipient,uint8_t tid,WifiModulationClass modulation) const121 MpduAggregator::GetMaxAmpduSize (Mac48Address recipient, uint8_t tid,
122                                  WifiModulationClass modulation) const
123 {
124   NS_LOG_FUNCTION (this << recipient << +tid << modulation);
125 
126   AcIndex ac = QosUtilsMapTidToAc (tid);
127 
128   // Find the A-MPDU max size configured on this device
129   uint32_t maxAmpduSize = m_mac->GetMaxAmpduSize (ac);
130 
131   if (maxAmpduSize == 0)
132     {
133       NS_LOG_DEBUG ("A-MPDU Aggregation is disabled on this station for AC " << ac);
134       return 0;
135     }
136 
137   Ptr<WifiRemoteStationManager> stationManager = m_mac->GetWifiRemoteStationManager ();
138   NS_ASSERT (stationManager);
139 
140   // Retrieve the Capabilities elements advertised by the recipient
141   Ptr<const HeCapabilities> heCapabilities = stationManager->GetStationHeCapabilities (recipient);
142   Ptr<const VhtCapabilities> vhtCapabilities = stationManager->GetStationVhtCapabilities (recipient);
143   Ptr<const HtCapabilities> htCapabilities = stationManager->GetStationHtCapabilities (recipient);
144 
145   // Determine the constraint imposed by the recipient based on the PPDU
146   // format used to transmit the A-MPDU
147   if (modulation == WIFI_MOD_CLASS_HE)
148     {
149       NS_ABORT_MSG_IF (!heCapabilities, "HE Capabilities element not received");
150 
151       maxAmpduSize = std::min (maxAmpduSize, heCapabilities->GetMaxAmpduLength ());
152     }
153   else if (modulation == WIFI_MOD_CLASS_VHT)
154     {
155       NS_ABORT_MSG_IF (!vhtCapabilities, "VHT Capabilities element not received");
156 
157       maxAmpduSize = std::min (maxAmpduSize, vhtCapabilities->GetMaxAmpduLength ());
158     }
159   else if (modulation == WIFI_MOD_CLASS_HT)
160     {
161       NS_ABORT_MSG_IF (!htCapabilities, "HT Capabilities element not received");
162 
163       maxAmpduSize = std::min (maxAmpduSize, htCapabilities->GetMaxAmpduLength ());
164     }
165   else  // non-HT PPDU
166     {
167       NS_LOG_DEBUG ("A-MPDU aggregation is not available for non-HT PHYs");
168 
169       maxAmpduSize = 0;
170     }
171 
172   return maxAmpduSize;
173 }
174 
175 uint8_t
CalculatePadding(uint32_t ampduSize)176 MpduAggregator::CalculatePadding (uint32_t ampduSize)
177 {
178   return (4 - (ampduSize % 4 )) % 4;
179 }
180 
181 AmpduSubframeHeader
GetAmpduSubframeHeader(uint16_t mpduSize,bool isSingle)182 MpduAggregator::GetAmpduSubframeHeader (uint16_t mpduSize, bool isSingle)
183 {
184   AmpduSubframeHeader hdr;
185   hdr.SetLength (mpduSize);
186   if (isSingle)
187     {
188       hdr.SetEof (1);
189     }
190   return hdr;
191 }
192 
193 std::vector<Ptr<WifiMacQueueItem>>
GetNextAmpdu(Ptr<WifiMacQueueItem> mpdu,WifiTxParameters & txParams,Time availableTime,WifiMacQueueItem::ConstIterator queueIt) const194 MpduAggregator::GetNextAmpdu (Ptr<WifiMacQueueItem> mpdu, WifiTxParameters& txParams,
195                               Time availableTime, WifiMacQueueItem::ConstIterator queueIt) const
196 {
197   NS_LOG_FUNCTION (this << *mpdu << &txParams << availableTime);
198 
199   std::vector<Ptr<WifiMacQueueItem>> mpduList;
200 
201   Mac48Address recipient = mpdu->GetHeader ().GetAddr1 ();
202   NS_ASSERT (mpdu->GetHeader ().IsQosData () && !recipient.IsBroadcast ());
203   uint8_t tid = mpdu->GetHeader ().GetQosTid ();
204 
205   Ptr<QosTxop> qosTxop = m_mac->GetQosTxop (tid);
206   NS_ASSERT (qosTxop != 0);
207 
208   //Have to make sure that the block ack agreement is established and A-MPDU is enabled
209   if (qosTxop->GetBaAgreementEstablished (recipient, tid)
210       && GetMaxAmpduSize (recipient, tid, txParams.m_txVector.GetModulationClass ()) > 0)
211     {
212       /* here is performed MPDU aggregation */
213       Ptr<WifiMacQueueItem> nextMpdu = mpdu;
214 
215       while (nextMpdu != 0)
216         {
217           // if we are here, nextMpdu can be aggregated to the A-MPDU.
218           NS_LOG_DEBUG ("Adding packet with sequence number " << nextMpdu->GetHeader ().GetSequenceNumber ()
219                         << " to A-MPDU, packet size = " << nextMpdu->GetSize ()
220                         << ", A-MPDU size = " << txParams.GetSize (recipient));
221 
222           mpduList.push_back (nextMpdu);
223 
224           // If allowed by the BA agreement, get the next MPDU
225           nextMpdu = 0;
226 
227           Ptr<const WifiMacQueueItem> peekedMpdu;
228           peekedMpdu = qosTxop->PeekNextMpdu (queueIt, tid, recipient);
229           if (peekedMpdu != 0)
230             {
231               // PeekNextMpdu() does not return an MPDU that is beyond the transmit window
232               NS_ASSERT (IsInWindow (peekedMpdu->GetHeader ().GetSequenceNumber (),
233                                      qosTxop->GetBaStartingSequence (recipient, tid),
234                                      qosTxop->GetBaBufferSize (recipient, tid)));
235 
236               // get the next MPDU to aggregate, provided that the constraints on size
237               // and duration limit are met. Note that the returned MPDU differs from
238               // the peeked MPDU if A-MSDU aggregation is enabled.
239               NS_LOG_DEBUG ("Trying to aggregate another MPDU");
240               nextMpdu = qosTxop->GetNextMpdu (peekedMpdu, txParams, availableTime, false, queueIt);
241             }
242         }
243 
244       if (mpduList.size () == 1)
245         {
246           // return an empty vector if it was not possible to aggregate at least two MPDUs
247           mpduList.clear ();
248         }
249     }
250 
251   return mpduList;
252 }
253 
254 } //namespace ns3
255