handle block ack and block ack request frames in MacLow
authorMirko Banchi <mk.banchi@gmail.com>
Wed, 03 Feb 2010 20:34:51 +0100
changeset 5957 dd5ece55acb9
parent 5956 e9918be47f78
child 5958 dd0accd82659
handle block ack and block ack request frames in MacLow
src/devices/wifi/mac-low.cc
src/devices/wifi/mac-low.h
--- a/src/devices/wifi/mac-low.cc	Wed Feb 03 20:34:51 2010 +0100
+++ b/src/devices/wifi/mac-low.cc	Wed Feb 03 20:34:51 2010 +0100
@@ -695,6 +695,51 @@
                                                  &MacLow::WaitSifsAfterEndTx, this);
         }
     } 
+  else if (hdr.IsBlockAck () && hdr.GetAddr1 () == m_self &&
+          (m_txParams.MustWaitBasicBlockAck () || m_txParams.MustWaitCompressedBlockAck ())) 
+          //m_blockAckTimeoutEvent.IsRunning ())
+    {
+      NS_LOG_DEBUG ("got block ack from "<<hdr.GetAddr2 ());
+      CtrlBAckResponseHeader blockAck;
+      packet->RemoveHeader (blockAck);
+      //m_blockAckTimeoutEvent.Cancel ();
+      m_listener->GotBlockAck (&blockAck, hdr.GetAddr2 ());
+    }
+  else if (hdr.IsBlockAckReq () && hdr.GetAddr1 () == m_self)
+    {
+      CtrlBAckRequestHeader blockAckReq;
+      packet->RemoveHeader (blockAckReq);
+      if (!blockAckReq.IsMultiTid ())
+        {
+          AgreementsI it = m_bAckAgreements.find (std::make_pair (hdr.GetAddr2 (), blockAckReq.GetTidInfo ()));
+          if (it != m_bAckAgreements.end ())
+            {
+              NS_ASSERT (m_sendAckEvent.IsExpired ());
+              if ((*it).second.first.IsImmediateBlockAck ())
+                {
+                  NS_LOG_DEBUG ("rx blockAckRequest/sendImmediateBlockAck from="<< hdr.GetAddr2 ());
+                  m_sendAckEvent = Simulator::Schedule (GetSifs (),
+                                                        &MacLow::SendBlockAckAfterBlockAckRequest, this,
+                                                        blockAckReq,
+                                                        hdr.GetAddr2 (), 
+                                                        hdr.GetDuration (),
+                                                        txMode);
+                }
+              else
+                {
+                  NS_FATAL_ERROR ("Delayed block ack not supported.");
+                }
+            }
+          else
+            {
+              NS_LOG_DEBUG ("There's not a valid agreement for this block ack request.");
+            }
+        }
+      else
+        {
+          NS_FATAL_ERROR ("Multi-tid block ack is not supported.");
+        }
+    }
   else if (hdr.IsCtl ()) 
     {
       NS_LOG_DEBUG ("rx drop " << hdr.GetTypeString ());
@@ -704,7 +749,41 @@
       WifiRemoteStation *station = GetStation (hdr.GetAddr2 ());
       station->ReportRxOk (rxSnr, txMode);
       
-      if (hdr.IsQosData () && hdr.IsQosNoAck ()) 
+      if (hdr.IsQosData () && StoreMpduIfNeeded (packet, hdr)) 
+        {
+          /* From section 9.10.4 in IEEE802.11:
+             Upon the receipt of a QoS data frame from the originator for which
+             the Block Ack agreement exists, the recipient shall buffer the MSDU
+             regardless of the value of the Ack Policy subfield within the 
+             QoS Control field of the QoS data frame. */
+          if (hdr.IsQosAck ())
+            {
+              AgreementsI it = m_bAckAgreements.find (std::make_pair (hdr.GetAddr2 (), hdr.GetQosTid ()));
+              RxCompleteBufferedPacketsWithSmallerSequence (it->second.first.GetStartingSequence (),
+                                                            hdr.GetAddr2 (), hdr.GetQosTid ());
+              RxCompleteBufferedPackets (hdr.GetAddr2 (), hdr.GetQosTid ());
+              NS_ASSERT (m_sendAckEvent.IsExpired ());
+              m_sendAckEvent = Simulator::Schedule (GetSifs (),
+                                                    &MacLow::SendAckAfterData, this,
+                                                    hdr.GetAddr2 (), 
+                                                    hdr.GetDuration (),
+                                                    txMode,
+                                                    rxSnr);
+            }
+          return;  
+        }
+      else if (hdr.IsQosData () && hdr.IsQosBlockAck ())
+        {
+          /* This happens if a packet with ack policy Block Ack is received and a block ack
+             agreement for that packet doesn't exist.
+
+             From section 11.5.3 in IEEE802.11e:
+             When a recipient does not have an active Block ack for a TID, but receives
+             data MPDUs with the Ack Policy subfield set to Block Ack, it shall discard
+             them [...]. */
+          return;
+        }
+      else if (hdr.IsQosData () && hdr.IsQosNoAck ()) 
         {
           NS_LOG_DEBUG ("rx unicast/noAck from="<<hdr.GetAddr2 ());
         } 
@@ -752,6 +831,27 @@
   ack.SetType (WIFI_MAC_CTL_ACK);
   return ack.GetSize () + 4;
 }
+uint32_t
+MacLow::GetBlockAckSize (enum BlockAckType type) const
+{
+  WifiMacHeader hdr;
+  hdr.SetType (WIFI_MAC_CTL_BACKRESP);
+  CtrlBAckResponseHeader blockAck;
+  if (type == BASIC_BLOCK_ACK)
+    {
+      blockAck.SetType (BASIC_BLOCK_ACK);
+    }
+  else if (type == COMPRESSED_BLOCK_ACK)
+    {
+      blockAck.SetType (COMPRESSED_BLOCK_ACK);
+    }
+  else if (type == MULTI_TID_BLOCK_ACK)
+    {
+      //Not implemented
+      NS_ASSERT (false);  
+    }
+  return hdr.GetSize () + blockAck.GetSerializedSize () + 4; 
+}
 uint32_t 
 MacLow::GetRtsSize (void) const
 {
@@ -766,6 +866,12 @@
   return m_phy->CalculateTxDuration (GetAckSize (), ackMode, WIFI_PREAMBLE_LONG);
 }
 Time
+MacLow::GetBlockAckDuration (Mac48Address to, WifiMode blockAckReqTxMode, enum BlockAckType type) const
+{
+  //WifiMode blockAckMode = GetBlockAckTxModeForBlockAckReq (to, blockAckReqTxMode);
+  return m_phy->CalculateTxDuration (GetBlockAckSize (type), blockAckReqTxMode, WIFI_PREAMBLE_LONG);
+}
+Time
 MacLow::GetCtsDuration (Mac48Address to, WifiMode rtsTxMode) const
 {
   WifiMode ctsMode = GetCtsTxModeForRts (to, rtsTxMode);
@@ -1144,7 +1250,17 @@
     } 
   else 
     {
-      if (m_txParams.MustWaitAck ()) 
+      if (m_txParams.MustWaitBasicBlockAck ())
+        {
+          duration += GetSifs ();
+          duration += GetBlockAckDuration (m_currentHdr.GetAddr1 (), dataTxMode, BASIC_BLOCK_ACK);
+        }
+      else if (m_txParams.MustWaitCompressedBlockAck ())
+        {
+          duration += GetSifs ();
+          duration += GetBlockAckDuration (m_currentHdr.GetAddr1 (), dataTxMode, COMPRESSED_BLOCK_ACK);
+        }
+      else if (m_txParams.MustWaitAck ()) 
         {
           duration += GetSifs ();
           duration += GetAckDuration (m_currentHdr.GetAddr1 (), dataTxMode);
@@ -1435,4 +1551,193 @@
     }
 }
 
+void
+MacLow::SendBlockAckResponse (const CtrlBAckResponseHeader* blockAck, Mac48Address originator, bool immediate,
+                              Time duration, WifiMode blockAckReqTxMode)
+{
+  Ptr<Packet> packet = Create<Packet> ();
+  packet->AddHeader (*blockAck);
+
+  WifiMacHeader hdr;
+  hdr.SetType (WIFI_MAC_CTL_BACKRESP);
+  hdr.SetAddr1 (originator);
+  hdr.SetAddr2 (GetAddress ());
+  hdr.SetDsNotFrom ();
+  hdr.SetDsNotTo ();
+  hdr.SetNoRetry ();
+  hdr.SetNoMoreFragments ();
+
+  m_currentPacket = packet;
+  m_currentHdr = hdr;
+  if (immediate)
+    {
+      m_txParams.DisableAck ();
+      duration -= GetSifs ();
+      /* from section 9.6 in IEEE802.11e:
+         The BlockAck control frame shall be sent at the same rate and modulation class as
+         the BlockAckReq frame if it is sent in response to a BlockAckReq frame.*/
+      if (blockAck->IsBasic ())
+        {
+          duration -= GetBlockAckDuration (originator, blockAckReqTxMode, BASIC_BLOCK_ACK);
+        }
+      else if (blockAck->IsCompressed ())
+        {
+          duration -= GetBlockAckDuration (originator, blockAckReqTxMode, COMPRESSED_BLOCK_ACK);
+        }
+      else if (blockAck->IsMultiTid ())
+        {
+          NS_FATAL_ERROR ("Multi-tid block ack is not supported.");
+        }
+    }
+  else
+    {
+      m_txParams.EnableAck ();
+      duration += GetSifs ();
+      duration += GetAckDuration (originator, blockAckReqTxMode);
+    }
+  m_txParams.DisableNextData ();
+
+  StartDataTxTimers ();
+
+  NS_ASSERT (duration >= MicroSeconds (0));
+  hdr.SetDuration (duration);
+  //here should be present a control about immediate or delayed block ack
+  //for now we assume immediate
+  packet->AddHeader (hdr);
+  WifiMacTrailer fcs;
+  packet->AddTrailer (fcs);
+  ForwardDown (packet, &hdr, blockAckReqTxMode);
+  m_currentPacket = 0;
+}
+
+void
+MacLow::SendBlockAckAfterBlockAckRequest (const CtrlBAckRequestHeader reqHdr, Mac48Address originator,
+                                          Time duration, WifiMode blockAckReqTxMode)
+{
+  NS_LOG_FUNCTION (this);
+  CtrlBAckResponseHeader blockAck;
+  uint8_t tid;
+  bool immediate = false;
+  if (!reqHdr.IsMultiTid ())
+    {
+      blockAck.SetStartingSequence (reqHdr.GetStartingSequence ());
+      blockAck.SetTidInfo (reqHdr.GetTidInfo ());
+      
+      tid = reqHdr.GetTidInfo ();
+      AgreementsI it;
+      it = m_bAckAgreements.find (std::make_pair (originator, tid));
+      if (it != m_bAckAgreements.end ())
+        {
+          immediate = (*it).second.first.IsImmediateBlockAck ();
+          uint16_t startingSeqCtrl = reqHdr.GetStartingSequenceControl ();
+
+          /* All packets with smaller sequence than starting sequence control must be passed up to Wifimac 
+           * See 9.10.3 in IEEE8022.11e standard.
+           */
+          RxCompleteBufferedPacketsWithSmallerSequence ((startingSeqCtrl>>4)&0xfff0, originator, tid);
+
+          std::list<BufferedPacket>::iterator i = (*it).second.second.begin ();
+
+          /* For more details about next operations see section 9.10.4 of IEEE802.11e standard */
+          if (reqHdr.IsBasic ())
+            {
+              blockAck.SetType (BASIC_BLOCK_ACK);
+              uint16_t guard = startingSeqCtrl;
+              std::list<BufferedPacket>::iterator lastComplete = (*it).second.second.begin ();
+              for (; i != (*it).second.second.end () && guard == (*i).second.GetSequenceControl (); i++)
+                {
+                  blockAck.SetReceivedFragment ((*i).second.GetSequenceNumber (),
+                                                (*i).second.GetFragmentNumber ());
+                   /* Section 9.10.4 in IEEE802.11n: the recipient shall pass up to WifiMac the 
+                    * MSDUs and A-MSDUs starting with the starting sequence number 
+                    * sequentially until there is an incomplete MSDU or A-MSDU in the buffer */
+                  if (!(*i).second.IsMoreFragments ())
+                    {
+                      while (lastComplete != i)
+                        {
+                          m_rxCallback ((*lastComplete).first, &(*lastComplete).second);
+                          lastComplete++;
+                        }
+                      m_rxCallback ((*lastComplete).first, &(*lastComplete).second);
+                      lastComplete++;
+                    }
+                  guard = (*i).second.IsMoreFragments () ? (guard + 1) : (guard + 16) & 0xfff0;
+                }
+              (*it).second.first.SetStartingSequence ((guard>>4)&0x0fff);
+              /* All packets already forwarded to WifiMac must be removed from buffer: 
+                 [begin (), lastComplete) */
+              (*it).second.second.erase ((*it).second.second.begin (), lastComplete);
+              for (i = lastComplete; i != (*it).second.second.end (); i++)
+                { 
+                  blockAck.SetReceivedFragment ((*i).second.GetSequenceNumber (),
+                                                (*i).second.GetFragmentNumber ());  
+                }
+            }
+          else if (reqHdr.IsCompressed ())
+            {
+              blockAck.SetType (COMPRESSED_BLOCK_ACK);
+              uint16_t guard = startingSeqCtrl;
+              std::list<BufferedPacket>::iterator lastComplete = (*it).second.second.begin ();
+              for (; i != (*it).second.second.end () && guard == (*i).second.GetSequenceControl (); i++)
+                {
+                  if (!(*i).second.IsMoreFragments ())
+                    {
+                      blockAck.SetReceivedPacket ((*i).second.GetSequenceNumber ());
+                      while (lastComplete != i)
+                        {
+                          m_rxCallback ((*lastComplete).first, &(*lastComplete).second);
+                          lastComplete++;
+                        }
+                      m_rxCallback ((*lastComplete).first, &(*lastComplete).second);
+                      lastComplete++;
+                    }
+                  guard = (*i).second.IsMoreFragments () ? (guard + 1) : (guard + 16) & 0xfff0;
+                }
+              (*it).second.first.SetStartingSequence ((guard>>4)&0x0fff);
+              /* All packets already forwarded to WifiMac must be removed from buffer:
+                 [begin (), lastcomplete) */
+              (*it).second.second.erase ((*it).second.second.begin (), lastComplete);
+              i = lastComplete;
+              if (i != (*it).second.second.end ())
+                {
+                  guard = (*i).second.GetSequenceControl () & 0xfff0;
+                }
+              for (; i != (*it).second.second.end ();)
+                {
+                  for (; i != (*it).second.second.end () && guard == (*i).second.GetSequenceControl (); i++)
+                    {
+                      if (!(*i).second.IsMoreFragments ())
+                        {
+                          guard = (guard + 16) & 0xfff0;
+                          blockAck.SetReceivedPacket ((*i).second.GetSequenceNumber ());
+                        }
+                      else
+                        {
+                          guard += 1;
+                        }
+                    }
+                  while (i != (*it).second.second.end () && ((guard >> 4) & 0x0fff) == (*i).second.GetSequenceNumber ())
+                    {
+                      i++;
+                    }
+                  if (i != (*it).second.second.end ())
+                    {
+                      guard = (*i).second.GetSequenceControl () & 0xfff0;
+                    }
+                }
+            }
+        }
+      else
+        {
+          NS_LOG_DEBUG ("there's not a valid block ack agreement with "<<originator);
+        }
+    }
+  else
+    {
+      NS_FATAL_ERROR ("Multi-tid block ack is not supported.");
+    }
+
+  SendBlockAckResponse (&blockAck, originator, immediate, duration, blockAckReqTxMode);
+}
+
 } // namespace ns3
--- a/src/devices/wifi/mac-low.h	Wed Feb 03 20:34:51 2010 +0100
+++ b/src/devices/wifi/mac-low.h	Wed Feb 03 20:34:51 2010 +0100
@@ -440,6 +440,7 @@
 private:
   void CancelAllEvents (void);
   uint32_t GetAckSize (void) const;
+  uint32_t GetBlockAckSize (enum BlockAckType type) const;
   uint32_t GetRtsSize (void) const;
   uint32_t GetCtsSize (void) const;
   uint32_t GetSize (Ptr<const Packet> packet, const WifiMacHeader *hdr) const;
@@ -454,8 +455,10 @@
   WifiMode GetDataTxMode (Ptr<const Packet> packet, const WifiMacHeader *hdr) const;
   WifiMode GetCtsTxModeForRts (Mac48Address to, WifiMode rtsTxMode) const;
   WifiMode GetAckTxModeForData (Mac48Address to, WifiMode dataTxMode) const;
+
   Time GetCtsDuration (Mac48Address to, WifiMode rtsTxMode) const;
   Time GetAckDuration (Mac48Address to, WifiMode dataTxMode) const;
+  Time GetBlockAckDuration (Mac48Address to, WifiMode blockAckReqTxMode, enum BlockAckType type) const;
   void NotifyNav (const WifiMacHeader &hdr, WifiMode txMode, WifiPreamble preamble);
   void DoNavResetNow (Time duration);
   bool DoNavStartNow (Time duration);
@@ -511,6 +514,17 @@
    * circularly modulo 2^12.
    */
   bool StoreMpduIfNeeded (Ptr<Packet> packet, WifiMacHeader hdr);
+  /*
+   * Invoked after that a block ack request has been received. Looks for corresponding
+   * block ack agreement and creates block ack bitmap on a received packets basis.
+   */
+  void SendBlockAckAfterBlockAckRequest (const CtrlBAckRequestHeader reqHdr, Mac48Address originator,
+                                         Time duration, WifiMode blockAckReqTxMode);
+  /*
+   * This method creates block ack frame with header equals to <i>blockAck</i> and start its transmission.
+   */
+  void SendBlockAckResponse (const CtrlBAckResponseHeader* blockAck, Mac48Address originator, bool immediate,
+                             Time duration, WifiMode blockAckReqTxMode);
 
   void SetupPhyMacLowListener (Ptr<WifiPhy> phy);