add MacLow to build
authorMathieu Lacage <mathieu.lacage@sophia.inria.fr>
Tue, 09 Oct 2007 11:36:13 +0200
changeset 1922 dd18bc29fc3c
parent 1921 4aa07179e71b
child 1923 a8caecf6637f
add MacLow to build
src/devices/wifi/mac-low.cc
src/devices/wifi/mac-low.h
src/devices/wifi/wifi-net-device.h
src/devices/wifi/wscript
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/wifi/mac-low.cc	Tue Oct 09 11:36:13 2007 +0200
@@ -0,0 +1,1008 @@
+/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2005,2006 INRIA
+ * All rights reserved.
+ *
+ * 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: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
+ */
+
+#include <cassert>
+
+#include "ns3/packet.h"
+#include "ns3/simulator.h"
+#include "ns3/tag.h"
+
+#include "mac-low.h"
+#include "wifi-phy.h"
+#include "wifi-mac-trailer.h"
+#include "wifi-net-device.h"
+#include "mac-stations.h"
+#include "mac-parameters.h"
+
+#define noMAC_LOW_TRACE 1
+
+#ifdef MAC_LOW_TRACE
+#  include <iostream>
+#  define TRACE(x) \
+  std::cout << "MAC LOW " << x << std::endl;
+#else /* MAC_LOW_TRACE */
+# define TRACE(x)
+#endif /* MAC_LOW_TRACE */
+
+namespace ns3 {
+
+class SnrTag : public Tag
+{
+public:
+  SnrTag ();
+  SnrTag (const SnrTag &o);
+  ~SnrTag ();
+  static uint32_t GetUid (void);
+  void Print (std::ostream &os) const;
+  uint32_t GetSerializedSize (void) const;
+  void Serialize (Buffer::Iterator i) const;
+  uint32_t Deserialize (Buffer::Iterator i);
+
+  void Set (double snr);
+  double Get (void) const;
+private:
+  double m_snr;
+};
+
+SnrTag::SnrTag ()
+  : m_snr (0.0)
+{}
+SnrTag::SnrTag (const SnrTag &o)
+  : m_snr (o.m_snr)
+{}
+SnrTag::~SnrTag ()
+{}
+uint32_t 
+SnrTag::GetUid (void)
+{
+  static uint32_t uid = AllocateUid<SnrTag> ("SnrTag.ns3.inria.fr");
+  return uid;
+}
+void 
+SnrTag::Print (std::ostream &os) const
+{
+  os << "snr="<<m_snr;
+}
+uint32_t 
+SnrTag::GetSerializedSize (void) const
+{
+  return 0;
+}
+void 
+SnrTag::Serialize (Buffer::Iterator i) const
+{
+  // would need to serialize double to platform-independent format.
+}
+uint32_t Deserialize (Buffer::Iterator i)
+{
+  // would need to deserialize double from platform-independent format.
+  return 0;
+}
+void 
+SnrTag::Set (double snr)
+{
+  m_snr = snr;
+}
+double 
+SnrTag::Get (void) const
+{
+  return m_snr;
+}
+
+
+MacLowTransmissionListener::MacLowTransmissionListener ()
+{}
+MacLowTransmissionListener::~MacLowTransmissionListener ()
+{}
+MacLowNavListener::MacLowNavListener ()
+{}
+MacLowNavListener::~MacLowNavListener ()
+{}
+
+MacLowTransmissionParameters::MacLowTransmissionParameters ()
+  : m_nextSize (0),
+    m_waitAck (ACK_NONE),
+    m_sendRts (false),
+    m_overrideDurationId (Seconds (0))
+{}
+void 
+MacLowTransmissionParameters::EnableNextData (uint32_t size)
+{
+  m_nextSize = size;
+}
+void 
+MacLowTransmissionParameters::DisableNextData (void)
+{
+  m_nextSize = 0;
+}
+void 
+MacLowTransmissionParameters::EnableOverrideDurationId (Time durationId)
+{
+  m_overrideDurationId = durationId;
+}
+void 
+MacLowTransmissionParameters::DisableOverrideDurationId (void)
+{
+  m_overrideDurationId = Seconds (0);
+}
+void 
+MacLowTransmissionParameters::EnableSuperFastAck (void)
+{
+  m_waitAck = ACK_SUPER_FAST;
+}
+void 
+MacLowTransmissionParameters::EnableFastAck (void)
+{
+  m_waitAck = ACK_FAST;
+}
+void 
+MacLowTransmissionParameters::EnableAck (void)
+{
+  m_waitAck = ACK_NORMAL;
+}
+void 
+MacLowTransmissionParameters::DisableAck (void)
+{
+  m_waitAck = ACK_NONE;
+}
+void 
+MacLowTransmissionParameters::EnableRts (void)
+{
+  m_sendRts = true;
+}
+void 
+MacLowTransmissionParameters::DisableRts (void)
+{
+  m_sendRts = false;
+}
+bool 
+MacLowTransmissionParameters::MustWaitAck (void) const
+{
+  return (m_waitAck != ACK_NONE)?true:false;
+}
+bool 
+MacLowTransmissionParameters::MustWaitNormalAck (void) const
+{
+  return (m_waitAck == ACK_NORMAL)?true:false;
+}
+bool 
+MacLowTransmissionParameters::MustWaitFastAck (void) const
+{
+  return (m_waitAck == ACK_FAST)?true:false;
+}
+bool 
+MacLowTransmissionParameters::MustWaitSuperFastAck (void) const
+{
+  return (m_waitAck == ACK_SUPER_FAST)?true:false;
+}
+bool 
+MacLowTransmissionParameters::MustSendRts (void) const
+{
+  return m_sendRts;
+}
+bool 
+MacLowTransmissionParameters::HasDurationId (void) const
+{
+  return (m_overrideDurationId != Seconds (0))?true:false;
+}
+Time
+MacLowTransmissionParameters::GetDurationId (void) const
+{
+  assert (m_overrideDurationId != Seconds (0));
+  return m_overrideDurationId;
+}
+bool 
+MacLowTransmissionParameters::HasNextPacket (void) const
+{
+  return (m_nextSize != 0)?true:false;
+}
+uint32_t 
+MacLowTransmissionParameters::GetNextPacketSize (void) const
+{
+  assert (HasNextPacket ());
+  return m_nextSize;
+}
+
+
+
+MacLow::MacLow ()
+  : m_normalAckTimeoutEvent (),
+    m_fastAckTimeoutEvent (),
+    m_superFastAckTimeoutEvent (),
+    m_fastAckFailedTimeoutEvent (),
+    m_ctsTimeoutEvent (),
+    m_sendCtsEvent (),
+    m_sendAckEvent (),
+    m_sendDataEvent (),
+    m_waitSifsEvent (),
+    m_hasCurrent (false)
+{
+  m_lastNavDuration = Seconds (0);
+  m_lastNavStart = Seconds (0);
+}
+
+MacLow::~MacLow ()
+{
+  CancelAllEvents ();
+}
+
+void
+MacLow::CancelAllEvents (void)
+{
+  bool oneRunning = false;
+  if (m_normalAckTimeoutEvent.IsRunning ()) 
+    {
+      m_normalAckTimeoutEvent.Cancel ();
+      oneRunning = true;
+    }
+  if (m_fastAckTimeoutEvent.IsRunning ()) 
+    {
+      m_fastAckTimeoutEvent.Cancel ();
+      oneRunning = true;
+    }
+  if (m_superFastAckTimeoutEvent.IsRunning ()) 
+    {
+      m_superFastAckTimeoutEvent.Cancel ();
+      oneRunning = true;
+    }
+  if (m_fastAckFailedTimeoutEvent.IsRunning ()) 
+    {
+      m_fastAckFailedTimeoutEvent.Cancel ();
+      oneRunning = true;
+    }
+  if (m_ctsTimeoutEvent.IsRunning ()) 
+    {
+      m_ctsTimeoutEvent.Cancel ();
+      oneRunning = true;
+    }
+  if (m_sendCtsEvent.IsRunning ()) 
+    {
+      m_sendCtsEvent.Cancel ();
+      oneRunning = true;
+    }
+  if (m_sendAckEvent.IsRunning ()) 
+    {
+      m_sendAckEvent.Cancel ();
+      oneRunning = true;
+    }
+  if (m_sendDataEvent.IsRunning ()) 
+    {
+      m_sendDataEvent.Cancel ();
+      oneRunning = true;
+    }
+  if (m_waitSifsEvent.IsRunning ()) 
+    {
+      m_waitSifsEvent.Cancel ();
+      oneRunning = true;
+    }
+  if (oneRunning && m_listener != 0) 
+    {
+      m_listener->Cancel ();
+    }
+}
+
+/****************************************************************************
+ *  API methods below.
+ ****************************************************************************/
+
+void
+MacLow::SetInterface (Ptr<WifiNetDevice> interface)
+{
+  m_interface = interface;
+}
+void
+MacLow::SetPhy (WifiPhy *phy)
+{
+  m_phy = phy;
+}
+void 
+MacLow::SetParameters (MacParameters *parameters)
+{
+  m_parameters = parameters;
+}
+void 
+MacLow::SetStations (MacStations *stations)
+{
+  m_stations = stations;
+}
+void 
+MacLow::SetRxCallback (MacLowRxCallback callback)
+{
+  m_rxCallback = callback;
+}
+void 
+MacLow::RegisterNavListener (MacLowNavListener *listener)
+{
+  m_navListeners.push_back (listener);
+}
+
+
+void 
+MacLow::StartTransmission (Packet packet, 
+                           WifiMacHeader const*hdr, 
+                           MacLowTransmissionParameters parameters,
+                           MacLowTransmissionListener *listener)
+{
+  /* m_currentPacket is not NULL because someone started
+   * a transmission and was interrupted before one of:
+   *   - ctsTimeout
+   *   - sendDataAfterCTS
+   * expired. This means that one of these timers is still
+   * running. They are all cancelled below anyway by the 
+   * call to CancelAllEvents (because of at least one
+   * of these two timer) which will trigger a call to the
+   * previous listener's cancel method.
+   *
+   * This typically happens because the high-priority 
+   * QapScheduler has taken access to the channel from
+   * one of the Edca of the QAP.
+   */
+  if (m_hasCurrent) 
+    {
+      m_hasCurrent = false;
+    }
+  m_currentPacket = packet;
+  m_currentHdr = *hdr;
+  CancelAllEvents ();
+  m_listener = listener;
+  m_txParams = parameters;
+
+  //assert (m_phy->IsStateIdle ());
+
+  TRACE ("startTx size="<< GetCurrentSize () << ", to=" << m_currentHdr.GetAddr1());
+
+  if (m_txParams.MustSendRts ()) 
+    {
+      SendRtsForPacket ();
+    } 
+  else 
+    {
+      SendDataPacket ();
+    }
+
+  /* When this method completes, we have taken ownership of the medium. */
+  assert (m_phy->IsStateTx ());  
+}
+
+void
+MacLow::ReceiveError (Packet const packet, double rxSnr)
+{
+  TRACE ("rx failed ");
+  m_dropError (packet);
+  if (m_txParams.MustWaitFastAck ()) 
+    {
+      assert (m_fastAckFailedTimeoutEvent.IsExpired ());
+      m_fastAckFailedTimeoutEvent = Simulator::Schedule (GetSifs (), 
+                                                         &MacLow::FastAckFailedTimeout, this);
+    }
+  return;
+}
+
+void 
+MacLow::ReceiveOk (Packet const packet, double rxSnr, WifiMode txMode, WifiMode headerMode)
+{
+  /* A packet is received from the PHY.
+   * When we have handled this packet,
+   * we handle any packet present in the
+   * packet queue.
+   */
+  WifiMacHeader hdr;
+  Packet p = packet;
+  p.RemoveHeader (hdr);
+  
+  bool isPrevNavZero = IsNavZero (Simulator::Now ());
+  TRACE ("duration/id=" << hdr.GetDuration ());
+  NotifyNav (Simulator::Now (), &hdr);
+  if (hdr.IsRts ()) 
+    {
+      /* XXX see section 9.9.2.2.1 802.11e/D12.1 */
+      if (isPrevNavZero &&
+          hdr.GetAddr1 () == m_interface->GetSelfAddress ()) 
+        {
+          TRACE ("rx RTS from=" << hdr.GetAddr2 () << ", schedule CTS");
+          assert (m_sendCtsEvent.IsExpired ());
+          MacStation *station = m_stations->Lookup (hdr.GetAddr2 ());
+          station->ReportRxOk (rxSnr, txMode);
+          m_sendCtsEvent = Simulator::Schedule (GetSifs (),
+                                                &MacLow::SendCtsAfterRts, this,
+                                                hdr.GetAddr2 (), 
+                                                //MicroSeconds (hdr.GetDurationUs ()),
+                                                //Time (),
+                                                Seconds (0),
+                                                GetCtsTxModeForRts (hdr.GetAddr2 (), txMode),
+                                                rxSnr);
+        } 
+      else 
+        {
+          TRACE ("rx RTS from=" << hdr.GetAddr2 () << ", cannot schedule CTS");
+        }
+    } 
+  else if (hdr.IsCts () &&
+           hdr.GetAddr1 () == m_interface->GetSelfAddress () &&
+           m_ctsTimeoutEvent.IsRunning () &&
+           m_hasCurrent) 
+    {
+      TRACE ("receive cts from="<<m_currentHdr.GetAddr1 ());
+      SnrTag tag;
+      packet.PeekTag (tag);
+      MacStation *station = GetStation (m_currentHdr.GetAddr1 ());
+      station->ReportRxOk (rxSnr, txMode);
+      station->ReportRtsOk (rxSnr, txMode, tag.Get ());
+      
+      m_ctsTimeoutEvent.Cancel ();
+      m_listener->GotCts (rxSnr, txMode);
+      assert (m_sendDataEvent.IsExpired ());
+      m_sendDataEvent = Simulator::Schedule (GetSifs (), 
+                                             &MacLow::SendDataAfterCts, this, 
+                                             hdr.GetAddr1 (),
+                                             MicroSeconds (hdr.GetDurationUs ()),
+                                             txMode);
+    } 
+  else if (hdr.IsAck () &&
+           hdr.GetAddr1 () == m_interface->GetSelfAddress () &&
+           (m_normalAckTimeoutEvent.IsRunning () || 
+            m_fastAckTimeoutEvent.IsRunning () ||
+            m_superFastAckTimeoutEvent.IsRunning ()) &&
+           m_txParams.MustWaitAck ()) 
+    {
+      TRACE ("receive ack from="<<m_currentHdr.GetAddr1 ());
+      SnrTag tag;
+      packet.PeekTag (tag);
+      MacStation *station = GetStation (m_currentHdr.GetAddr1 ());
+      station->ReportRxOk (rxSnr, txMode);
+      station->ReportDataOk (rxSnr, txMode, tag.Get ());
+      bool gotAck = false;
+      if (m_txParams.MustWaitNormalAck () &&
+          m_normalAckTimeoutEvent.IsRunning ()) 
+        {
+          m_normalAckTimeoutEvent.Cancel ();
+          gotAck = true;
+        }
+      if (m_txParams.MustWaitFastAck () &&
+          m_fastAckTimeoutEvent.IsRunning ()) 
+        {
+          m_fastAckTimeoutEvent.Cancel ();
+          gotAck = true;
+        }
+      if (gotAck) 
+        {
+          m_listener->GotAck (rxSnr, txMode);
+        }
+    if (m_txParams.HasNextPacket ()) 
+      {
+        m_waitSifsEvent = Simulator::Schedule (GetSifs (), 
+                                               &MacLow::WaitSifsAfterEndTx, this);
+      }
+    } 
+  else if (hdr.IsCtl ()) 
+    {
+      TRACE ("rx drop " << hdr.GetTypeString ());
+    } 
+  else if (hdr.GetAddr1 () == m_interface->GetSelfAddress ()) 
+    {
+      MacStation *station = GetStation (hdr.GetAddr2 ());
+      station->ReportRxOk (rxSnr, txMode);
+      
+      if (hdr.IsQosData () && hdr.IsQosNoAck ()) 
+        {
+          TRACE ("rx unicast/noAck from="<<hdr.GetAddr2 ());
+        } 
+      else if (hdr.IsData () || hdr.IsMgt ()) 
+        {
+          TRACE ("rx unicast/sendAck from=" << hdr.GetAddr2 ());
+          assert (m_sendAckEvent.IsExpired ());
+          m_sendAckEvent = Simulator::Schedule (GetSifs (),
+                                                &MacLow::SendAckAfterData, this,
+                                                hdr.GetAddr2 (), 
+                                                MicroSeconds (hdr.GetDurationUs ()),
+                                                GetAckTxModeForData (hdr.GetAddr2 (), txMode),
+                                                rxSnr);
+        }
+      goto rxPacket;
+    } 
+  else if (hdr.GetAddr1 ().IsBroadcast ()) 
+    {
+      if (hdr.IsData () || hdr.IsMgt ()) 
+        {
+          TRACE ("rx broadcast from=" << hdr.GetAddr2 ());
+          goto rxPacket;
+        } 
+      else 
+        {
+          // DROP.
+        }
+    } 
+  else 
+    {
+      //TRACE_VERBOSE ("rx not-for-me from %d", GetSource (packet));
+    }
+  return;
+ rxPacket:
+  WifiMacTrailer fcs;
+  p.RemoveTrailer (fcs);
+  m_rxCallback (p, &hdr);
+  return;
+}
+
+uint32_t 
+MacLow::GetAckSize (void) const
+{
+  WifiMacHeader ack;
+  ack.SetType (WIFI_MAC_CTL_ACK);
+  return ack.GetSize ();
+}
+uint32_t 
+MacLow::GetRtsSize (void) const
+{
+  WifiMacHeader rts;
+  rts.SetType (WIFI_MAC_CTL_RTS);
+  return rts.GetSize ();
+}
+uint32_t 
+MacLow::GetCtsSize (void) const
+{
+  WifiMacHeader cts;
+  cts.SetType (WIFI_MAC_CTL_CTS);
+  return cts.GetSize ();
+}
+Time
+MacLow::GetSifs (void) const
+{
+  return m_parameters->GetSifs ();
+}
+Time
+MacLow::GetPifs (void) const
+{
+  return m_parameters->GetPifs ();
+}
+Time
+MacLow::GetAckTimeout (void) const
+{
+  return m_parameters->GetAckTimeout ();
+}
+Time
+MacLow::GetCtsTimeout (void) const
+{
+  return m_parameters->GetCtsTimeout ();
+}
+uint32_t 
+MacLow::GetCurrentSize (void) const
+{
+  WifiMacTrailer fcs;
+  return m_currentPacket.GetSize () + m_currentHdr.GetSize () + fcs.GetSerializedSize ();
+}
+
+WifiMode
+MacLow::GetRtsTxMode (Mac48Address to) const
+{
+  return GetStation (to)->GetRtsMode ();
+}
+WifiMode
+MacLow::GetDataTxMode (Mac48Address to, uint32_t size) const
+{
+  return GetStation (to)->GetDataMode (size);
+}
+
+WifiMode
+MacLow::GetCtsTxModeForRts (Mac48Address to, WifiMode rtsTxMode) const
+{
+  return GetStation (to)->GetCtsMode (rtsTxMode);
+}
+WifiMode
+MacLow::GetAckTxModeForData (Mac48Address to, WifiMode dataTxMode) const
+{
+  return GetStation (to)->GetAckMode (dataTxMode);
+}
+
+
+Time
+MacLow::CalculateOverallTxTime (uint32_t dataSize, Mac48Address to, 
+                                MacLowTransmissionParameters const& params) const
+{
+  Time txTime = Seconds (0);
+  WifiMode rtsMode = GetRtsTxMode (to);
+  WifiMode dataMode = GetDataTxMode (to, dataSize);
+  if (params.MustSendRts ()) 
+    {
+      txTime += m_phy->CalculateTxDuration (GetRtsSize (), rtsMode, WIFI_PREAMBLE_LONG);
+      WifiMode ctsMode = GetCtsTxModeForRts (m_currentHdr.GetAddr1 (), rtsMode);
+      txTime += m_phy->CalculateTxDuration (GetCtsSize (), ctsMode, WIFI_PREAMBLE_LONG);
+      txTime += GetSifs () * Scalar (2);
+    }
+  txTime += m_phy->CalculateTxDuration (dataSize, dataMode, WIFI_PREAMBLE_LONG);
+  if (params.MustWaitAck ()) 
+    {
+      WifiMode ackMode = GetAckTxModeForData (m_currentHdr.GetAddr1 (), dataMode);
+      txTime += GetSifs ();
+      txTime += m_phy->CalculateTxDuration (GetAckSize (), ackMode, WIFI_PREAMBLE_LONG);
+    }
+  return txTime;
+}
+
+Time
+MacLow::CalculateTransmissionTime (uint32_t dataSize, Mac48Address to, 
+                                   MacLowTransmissionParameters const& params) const
+{
+  Time txTime = CalculateOverallTxTime (dataSize, to, params);
+  if (params.HasNextPacket ()) 
+    {
+      WifiMode dataMode = GetDataTxMode (to, dataSize );
+      txTime += GetSifs ();
+      txTime += m_phy->CalculateTxDuration (params.GetNextPacketSize (), dataMode, WIFI_PREAMBLE_LONG);
+    }
+  return txTime;
+}
+
+
+void
+MacLow::NotifyNav (Time at, WifiMacHeader const *hdr)
+{
+  /* XXX
+   * We might need to do something special for the
+   * subtle case of RTS/CTS. I don't know what.
+   *
+   * See section 9.9.2.2.1, 802.11e/D12.1
+   */
+  assert (m_lastNavStart < at);
+  Time oldNavStart = m_lastNavStart;
+  Time oldNavEnd = oldNavStart + m_lastNavDuration;
+  Time newNavStart = at;
+  Time duration = MicroSeconds (hdr->GetDurationUs ());
+
+  if (hdr->IsCfpoll () &&
+      hdr->GetAddr2 () == m_interface->GetBssid ()) 
+    {
+      m_lastNavStart = newNavStart;
+      m_lastNavDuration = duration;
+      for (NavListenersCI i = m_navListeners.begin (); i != m_navListeners.end (); i++) 
+        {
+          // XXX !!!!!!!
+          (*i)->NavReset (newNavStart, duration);
+        }
+      return;
+    }
+
+  if (oldNavEnd > newNavStart) 
+    {
+      Time newNavEnd = newNavStart + duration;
+      /* The two NAVs overlap */
+      if (newNavEnd > oldNavEnd) 
+        {
+          for (NavListenersCI i = m_navListeners.begin (); i != m_navListeners.end (); i++) 
+            {
+              (*i)->NavContinue (newNavStart, duration);
+            }
+        }
+    } 
+  else 
+    {
+      m_lastNavStart = newNavStart;
+      m_lastNavDuration = duration;
+      for (NavListenersCI i = m_navListeners.begin (); i != m_navListeners.end (); i++) 
+        {
+          (*i)->NavStart (newNavStart, duration);
+        }
+    }
+}
+
+void
+MacLow::ForwardDown (Packet const packet, WifiMacHeader const* hdr, 
+                     WifiMode txMode)
+{
+  m_phy->SendPacket (packet, txMode, WIFI_PREAMBLE_LONG, 0);
+  /* Note that it is really important to notify the NAV 
+   * thing _after_ forwarding the packet to the PHY.
+   */
+  Time txDuration = m_phy->CalculateTxDuration (packet.GetSize (), txMode, WIFI_PREAMBLE_LONG);
+  NotifyNav (Simulator::Now ()+txDuration, hdr);
+}
+
+void
+MacLow::CtsTimeout (void)
+{
+  MacStation *station = GetStation (m_currentHdr.GetAddr1 ());
+  station->ReportRtsFailed ();
+  m_hasCurrent = false;
+  m_listener->MissedCts ();
+  m_listener = 0;
+}
+void
+MacLow::NormalAckTimeout (void)
+{
+  MacStation *station = GetStation (m_currentHdr.GetAddr1 ());
+  station->ReportDataFailed ();
+  m_listener->MissedAck ();
+  m_listener = 0;
+}
+void
+MacLow::FastAckTimeout (void)
+{
+  MacStation *station = GetStation (m_currentHdr.GetAddr1 ());
+  station->ReportDataFailed ();
+  if (m_phy->IsStateIdle ()) 
+    {
+      TRACE ("fast Ack idle missed");
+      m_listener->MissedAck ();
+    }
+  m_listener = 0;
+}
+void
+MacLow::SuperFastAckTimeout ()
+{
+  MacStation *station = GetStation (m_currentHdr.GetAddr1 ());
+  station->ReportDataFailed ();
+  if (m_phy->IsStateIdle ()) 
+    {
+      TRACE ("super fast Ack failed");
+      m_listener->MissedAck ();
+    } 
+  else 
+    {
+      TRACE ("super fast Ack ok");
+      m_listener->GotAck (0.0, WifiMode ());
+    }
+  m_listener = 0;
+}
+
+void
+MacLow::SendRtsForPacket (void)
+{
+  /* send an RTS for this packet. */
+  WifiMacHeader rts;
+  rts.SetType (WIFI_MAC_CTL_RTS);
+  rts.SetDsNotFrom ();
+  rts.SetDsNotTo ();
+  rts.SetAddr1 (m_currentHdr.GetAddr1 ());
+  rts.SetAddr2 (m_interface->GetSelfAddress ());
+  WifiMode rtsTxMode = GetRtsTxMode (m_currentHdr.GetAddr1 ());
+  Time duration = Seconds (0);
+  if (m_txParams.HasDurationId ()) 
+    {
+      duration += m_txParams.GetDurationId ();
+    } 
+  else 
+    {
+      WifiMode dataTxMode = GetDataTxMode (m_currentHdr.GetAddr1 (), GetCurrentSize ());
+      WifiMode ackTxMode = GetAckTxModeForData (m_currentHdr.GetAddr1 (), dataTxMode);
+      WifiMode ctsTxMode = GetCtsTxModeForRts (m_currentHdr.GetAddr1 (), rtsTxMode);
+      duration += GetSifs ();
+      duration += m_phy->CalculateTxDuration (GetCtsSize (), ctsTxMode, WIFI_PREAMBLE_LONG);
+      duration += GetSifs ();
+      duration += m_phy->CalculateTxDuration (GetCurrentSize (), dataTxMode, WIFI_PREAMBLE_LONG);
+      duration += GetSifs ();
+      duration += m_phy->CalculateTxDuration (GetAckSize (), ackTxMode, WIFI_PREAMBLE_LONG);
+    }
+  rts.SetDurationUs (duration.GetMicroSeconds ());
+
+  TRACE ("tx RTS to="<< rts.GetAddr1 () << ", mode=" << (uint32_t)rtsTxMode);
+
+  Time txDuration = m_phy->CalculateTxDuration (GetRtsSize (), rtsTxMode, WIFI_PREAMBLE_LONG);
+  Time timerDelay = txDuration + GetCtsTimeout ();
+
+  assert (m_ctsTimeoutEvent.IsExpired ());
+  m_ctsTimeoutEvent = Simulator::Schedule (timerDelay, &MacLow::CtsTimeout, this);
+
+  Packet packet;
+  packet.AddHeader (rts);
+  WifiMacTrailer fcs;
+  packet.AddTrailer (fcs);
+
+  ForwardDown (packet, &rts, rtsTxMode);
+}
+
+void
+MacLow::StartDataTxTimers (void)
+{
+  WifiMode dataTxMode = GetDataTxMode (m_currentHdr.GetAddr1 (), GetCurrentSize ());
+  Time txDuration = m_phy->CalculateTxDuration (GetCurrentSize (), dataTxMode, WIFI_PREAMBLE_LONG);
+  if (m_txParams.MustWaitNormalAck ()) 
+    {
+      Time timerDelay = txDuration + GetAckTimeout ();
+      assert (m_normalAckTimeoutEvent.IsExpired ());
+      m_normalAckTimeoutEvent = Simulator::Schedule (timerDelay, &MacLow::NormalAckTimeout, this);
+    } 
+  else if (m_txParams.MustWaitFastAck ()) 
+    {
+      Time timerDelay = txDuration + GetPifs ();
+      assert (m_fastAckTimeoutEvent.IsExpired ());
+      m_fastAckTimeoutEvent = Simulator::Schedule (timerDelay, &MacLow::FastAckTimeout, this);
+    } 
+  else if (m_txParams.MustWaitSuperFastAck ()) 
+    {
+      Time timerDelay = txDuration + GetPifs ();
+      assert (m_superFastAckTimeoutEvent.IsExpired ());
+      m_superFastAckTimeoutEvent = Simulator::Schedule (timerDelay, 
+                                                        &MacLow::SuperFastAckTimeout, this);
+    } 
+  else if (m_txParams.HasNextPacket ()) 
+    {
+      Time delay = txDuration + GetSifs ();
+      assert (m_waitSifsEvent.IsExpired ());
+      m_waitSifsEvent = Simulator::Schedule (delay, &MacLow::WaitSifsAfterEndTx, this);
+    } 
+  else 
+    {
+      // since we do not expect any timer to be triggered.
+      m_listener = 0;
+    }
+}
+
+void
+MacLow::SendDataPacket (void)
+{
+  /* send this packet directly. No RTS is needed. */
+  StartDataTxTimers ();
+
+  WifiMode dataTxMode = GetDataTxMode (m_currentHdr.GetAddr1 (), GetCurrentSize ());
+  TRACE ("tx "<< m_currentHdr.GetTypeString () << 
+         ", to=" << m_currentHdr.GetAddr1 () <<
+         ", mode=" << dataTxMode.GetPhyRate ());
+  Time duration = Seconds (0);
+  if (m_txParams.HasDurationId ()) 
+    {
+      duration += m_txParams.GetDurationId ();
+    } 
+  else 
+    {
+      WifiMode ackTxMode = GetAckTxModeForData (m_currentHdr.GetAddr1 (), 
+                                                dataTxMode);
+      if (m_txParams.MustWaitAck ()) 
+        {
+          duration += GetSifs ();
+          duration += m_phy->CalculateTxDuration (GetAckSize (), ackTxMode, WIFI_PREAMBLE_LONG);
+        }
+      if (m_txParams.HasNextPacket ()) 
+        {
+          duration += GetSifs ();
+          duration += m_phy->CalculateTxDuration (m_txParams.GetNextPacketSize (), 
+                                                  dataTxMode, WIFI_PREAMBLE_LONG);
+          if (m_txParams.MustWaitAck ()) 
+            {
+              duration += GetSifs ();
+              duration += m_phy->CalculateTxDuration (GetAckSize (), ackTxMode, WIFI_PREAMBLE_LONG);
+            }
+        }
+    }
+  m_currentHdr.SetDurationUs (duration.GetMicroSeconds ());
+
+  m_currentPacket.AddHeader (m_currentHdr);
+  WifiMacTrailer fcs;
+  m_currentPacket.AddTrailer (fcs);
+
+  ForwardDown (m_currentPacket, &m_currentHdr, dataTxMode);
+  m_hasCurrent = false;
+}
+
+bool 
+MacLow::IsNavZero (Time now)
+{
+  if (m_lastNavStart + m_lastNavDuration > now) 
+    {
+      return false;
+    } 
+  else 
+    {
+      return true;
+    }
+}
+
+MacStation *
+MacLow::GetStation (Mac48Address ad) const
+{
+  return m_stations->Lookup (ad);
+}
+
+void
+MacLow::SendCtsAfterRts (Mac48Address source, Time duration, WifiMode txMode, double rtsSnr)
+{
+  /* send a CTS when you receive a RTS 
+   * right after SIFS.
+   */
+  TRACE ("tx CTS to=" << source << ", mode=" << (uint32_t)txMode);
+  WifiMacHeader cts;
+  cts.SetType (WIFI_MAC_CTL_CTS);
+  cts.SetDsNotFrom ();
+  cts.SetDsNotTo ();
+  cts.SetAddr1 (source);
+  duration -= m_phy->CalculateTxDuration (GetCtsSize (), txMode, WIFI_PREAMBLE_LONG);
+  duration -= GetSifs ();
+  cts.SetDurationUs (duration.GetMicroSeconds ());
+
+  Packet packet;
+  packet.AddHeader (cts);
+  WifiMacTrailer fcs;
+  packet.AddTrailer (fcs);
+
+  struct SnrTag tag;
+  tag.Set (rtsSnr);
+  packet.AddTag (tag);
+
+  ForwardDown (packet, &cts, txMode);
+}
+
+void
+MacLow::SendDataAfterCts (Mac48Address source, Time duration, WifiMode txMode)
+{
+  /* send the third step in a 
+   * RTS/CTS/DATA/ACK hanshake 
+   */
+  assert (m_hasCurrent);
+  WifiMode dataTxMode = GetDataTxMode (m_currentHdr.GetAddr1 (), GetCurrentSize ());
+
+  TRACE ("tx " << m_currentHdr.GetTypeString () << " to=" << m_currentHdr.GetAddr2 () <<
+         ", mode=" << dataTxMode.GetPhyRate () << ", seq=0x"<< m_currentHdr.GetSequenceControl ());
+
+  StartDataTxTimers ();
+  Time txDuration = m_phy->CalculateTxDuration (GetCurrentSize (), dataTxMode, WIFI_PREAMBLE_LONG);
+  duration -= txDuration;
+  duration -= GetSifs ();
+  m_currentHdr.SetDurationUs (duration.GetMicroSeconds ());
+
+  m_currentPacket.AddHeader (m_currentHdr);
+  WifiMacTrailer fcs;
+  m_currentPacket.AddTrailer (fcs);
+
+  ForwardDown (m_currentPacket, &m_currentHdr, dataTxMode);
+  m_hasCurrent = false;
+}
+
+void 
+MacLow::WaitSifsAfterEndTx (void)
+{
+  m_listener->StartNext ();
+}
+
+void
+MacLow::FastAckFailedTimeout (void)
+{
+  m_listener->MissedAck ();
+  TRACE ("fast Ack busy but missed");
+}
+
+void
+MacLow::SendAckAfterData (Mac48Address source, Time duration, WifiMode txMode, double dataSnr)
+{
+  /* send an ACK when you receive 
+   * a packet after SIFS. 
+   */
+  TRACE ("tx ACK to=" << source << ", mode=" << txMode.GetPhyRate ());
+  WifiMacHeader ack;
+  ack.SetType (WIFI_MAC_CTL_ACK);
+  ack.SetDsNotFrom ();
+  ack.SetDsNotTo ();
+  ack.SetAddr1 (source);
+  duration -= m_phy->CalculateTxDuration (GetAckSize (), txMode, WIFI_PREAMBLE_LONG);
+  duration -= GetSifs ();
+  ack.SetDurationUs (duration.GetMicroSeconds ());
+
+  Packet packet;
+  packet.AddHeader (ack);
+  WifiMacTrailer fcs;
+  packet.AddTrailer (fcs);
+
+  struct SnrTag tag;
+  tag.Set (dataSnr);
+  packet.AddTag (tag);
+
+  ForwardDown (packet, &ack, txMode);
+}
+
+} // namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/wifi/mac-low.h	Tue Oct 09 11:36:13 2007 +0200
@@ -0,0 +1,249 @@
+/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2005, 2006 INRIA
+ * All rights reserved.
+ *
+ * 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: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
+ */
+#ifndef MAC_LOW_H
+#define MAC_LOW_H
+
+#include <vector>
+#include <stdint.h>
+
+#include "wifi-mac-header.h"
+#include "wifi-mode.h"
+#include "ns3/mac48-address.h"
+#include "ns3/callback.h"
+#include "ns3/callback-trace-source.h"
+#include "ns3/event-id.h"
+#include "ns3/packet.h"
+#include "ns3/nstime.h"
+
+namespace ns3 {
+
+class WifiNetDevice;
+class WifiPhy;
+class PacketLogger;
+class MacStations;
+class MacStation;
+class MacParameters;
+
+class MacLowTransmissionListener {
+public:
+  MacLowTransmissionListener ();
+  virtual ~MacLowTransmissionListener ();
+
+  virtual void GotCts (double snr, WifiMode txMode) = 0;
+  virtual void MissedCts (void) = 0;
+  /* Do not rely on the gotAck method to be
+   * given valid parameters when SuperFastAck is
+   * enabled.
+   */
+  virtual void GotAck (double snr, WifiMode txMode) = 0;
+  virtual void MissedAck (void) = 0;
+  virtual void StartNext (void) = 0;
+
+  /* Invoked if this transmission was canceled 
+   * one way or another. When this method is invoked,
+   * you can assume that the packet has not been passed
+   * down the stack to the PHY. You are responsible
+   * for freeing the packet if you want to.
+   */
+  virtual void Cancel (void) = 0;
+};
+
+
+class MacLowNavListener {
+public:
+  MacLowNavListener ();
+  virtual ~MacLowNavListener ();
+  virtual void NavStart (Time now, Time duration) = 0;
+  virtual void NavContinue (Time now, Time duration) = 0;
+  virtual void NavReset (Time now, Time duration) = 0;
+};
+
+class MacLowTransmissionParameters {
+public:
+  MacLowTransmissionParameters ();
+    
+  /* If ACK is enabled, we wait ACKTimeout for an ACK.
+   */
+  void EnableAck (void);
+  /* If FastAck is enabled, we:
+   *   - wait PIFS after end-of-tx. If idle, report
+   *     FastAckMissed.
+   *   - if busy at end-of-tx+PIFS, wait end-of-rx
+   *   - if Ack ok at end-of-rx, report FastAck ok.
+   *   - if Ack not ok at end-of-rx, report FastAckMissed
+   *     at end-of-rx+SIFS.
+   * This is really complicated but it is needed for
+   * proper HCCA support.
+   */
+  void EnableFastAck (void);
+  /* If SuperFastAck is enabled, we:
+   *   - if busy at end-of-tx+PIFS, report gotAck
+   *   - if idle at end-of-tx+PIFS, report missedAck
+   */
+  void EnableSuperFastAck (void);
+  /* If RTS is enabled, we wait CTSTimeout for a CTS.
+   * Otherwise, no RTS is sent.
+   */
+  void EnableRts (void);
+  /* If NextData is enabled, we add the transmission duration
+   * of the nextData to the durationId and we notify the
+   * transmissionListener at the end of the current
+   * transmission + SIFS.
+   */
+  void EnableNextData (uint32_t size);
+  
+  /* If we enable this, we ignore all other durationId 
+   * calculation and simply force the packet's durationId
+   * field to this value.
+   */
+  void EnableOverrideDurationId (Time durationId);
+  
+  void DisableAck (void);
+  void DisableRts (void);
+  void DisableNextData (void);
+  void DisableOverrideDurationId (void);
+
+  bool MustWaitAck (void) const;
+  bool MustWaitNormalAck (void) const;
+  bool MustWaitFastAck (void) const;
+  bool MustWaitSuperFastAck (void) const;
+  bool MustSendRts (void) const;
+  bool HasDurationId (void) const;
+  Time GetDurationId (void) const;
+  bool HasNextPacket (void) const;
+  uint32_t GetNextPacketSize (void) const;
+
+private:
+  uint32_t m_nextSize;
+  enum {
+    ACK_NONE,
+    ACK_NORMAL,
+    ACK_FAST,
+    ACK_SUPER_FAST
+  } m_waitAck;
+  bool m_sendRts;
+  Time m_overrideDurationId;
+};
+
+
+class MacLow {
+public:
+  typedef Callback<void, Packet , WifiMacHeader const*> MacLowRxCallback;
+
+  MacLow ();
+  ~MacLow ();
+
+  void SetInterface (Ptr<WifiNetDevice> interface);
+  void SetPhy (WifiPhy *phy);
+  void SetStations (MacStations *stations);
+  void SetParameters (MacParameters *parameters);
+  void SetRxCallback (MacLowRxCallback callback);
+  void RegisterNavListener (MacLowNavListener *listener);
+
+  /* This transmission time includes the time required for
+   * the next packet transmission if one was selected.
+   */
+  Time CalculateTransmissionTime (uint32_t payloadSize,
+                                  Mac48Address to,
+                                  MacLowTransmissionParameters const&parameters) const;
+
+  /* start the transmission of the currently-stored data. */
+  void StartTransmission (Packet packet, 
+                          WifiMacHeader const*hdr, 
+                          MacLowTransmissionParameters parameters,
+                          MacLowTransmissionListener *listener);
+
+  void ReceiveOk (Packet const packet, double rxSnr, WifiMode txMode, WifiMode headerMode);
+  void ReceiveError (Packet const packet, double rxSnr);
+private:
+  void CancelAllEvents (void);
+  uint32_t GetAckSize (void) const;
+  uint32_t GetRtsSize (void) const;
+  uint32_t GetCtsSize (void) const;
+  Time GetSifs (void) const;
+  Time GetPifs (void) const;
+  Time GetAckTimeout (void) const;
+  Time GetCtsTimeout (void) const;
+  uint32_t GetCurrentSize (void) const;
+  Time NowUs (void) const;
+  MacStation *GetStation (Mac48Address to) const;
+  void ForwardDown (Packet const packet, WifiMacHeader const *hdr, 
+                    WifiMode txMode);
+  Time CalculateOverallTxTime (uint32_t size,
+                               Mac48Address to,
+                               MacLowTransmissionParameters const &params) const;
+  WifiMode GetRtsTxMode (Mac48Address to) const;
+  WifiMode GetDataTxMode (Mac48Address to, uint32_t size) const;
+  WifiMode GetCtsTxModeForRts (Mac48Address to, WifiMode rtsTxMode) const;
+  WifiMode GetAckTxModeForData (Mac48Address to, WifiMode dataTxMode) const;
+  void NotifyNav (Time at, WifiMacHeader const*hdr);
+  bool IsNavZero (Time at);
+  void MaybeCancelPrevious (void);
+  
+  void NormalAckTimeout (void);
+  void FastAckTimeout (void);
+  void SuperFastAckTimeout (void);
+  void FastAckFailedTimeout (void);
+  void CtsTimeout (void);
+  void SendCtsAfterRts (Mac48Address source, Time duration, WifiMode txMode, double rtsSnr);
+  void SendAckAfterData (Mac48Address source, Time duration, WifiMode txMode, double rtsSnr);
+  void SendDataAfterCts (Mac48Address source, Time duration, WifiMode txMode);
+  void WaitSifsAfterEndTx (void);
+
+  void SendRtsForPacket (void);
+  void SendDataPacket (void);
+  void SendCurrentTxPacket (void);
+  void StartDataTxTimers (void);
+
+  Ptr<WifiNetDevice> m_interface;
+  WifiPhy *m_phy;
+  MacStations *m_stations;
+  MacParameters *m_parameters;
+  MacLowRxCallback m_rxCallback;
+  typedef std::vector<MacLowNavListener *>::const_iterator NavListenersCI;
+  typedef std::vector<MacLowNavListener *> NavListeners;
+  NavListeners m_navListeners;
+
+  EventId m_normalAckTimeoutEvent;
+  EventId m_fastAckTimeoutEvent;
+  EventId m_superFastAckTimeoutEvent;
+  EventId m_fastAckFailedTimeoutEvent;
+  EventId m_ctsTimeoutEvent;
+  EventId m_sendCtsEvent;
+  EventId m_sendAckEvent;
+  EventId m_sendDataEvent;
+  EventId m_waitSifsEvent;
+
+  Packet m_currentPacket;
+  bool m_hasCurrent;
+  WifiMacHeader m_currentHdr;
+  MacLowTransmissionParameters m_txParams;
+  MacLowTransmissionListener *m_listener;
+
+  Time m_lastNavStart;
+  Time m_lastNavDuration;
+
+  CallbackTraceSource<Packet> m_dropError;
+};
+
+}; // namespace ns3
+
+#endif /* MAC_LOW_H */
--- a/src/devices/wifi/wifi-net-device.h	Tue Oct 09 11:07:18 2007 +0200
+++ b/src/devices/wifi/wifi-net-device.h	Tue Oct 09 11:36:13 2007 +0200
@@ -54,6 +54,7 @@
 
   void ConnectTo (Ptr<WifiChannel> channel);
 
+  Mac48Address GetSelfAddress (void) const;
   virtual Mac48Address GetBssid (void) const = 0;
   virtual Ssid GetSsid (void) const = 0;
 
--- a/src/devices/wifi/wscript	Tue Oct 09 11:07:18 2007 +0200
+++ b/src/devices/wifi/wscript	Tue Oct 09 11:36:13 2007 +0200
@@ -16,6 +16,7 @@
         'wifi-mac-header.cc',
         'wifi-mac-trailer.cc',
         'mac-parameters.cc',
+        'mac-low.cc',
         ]
     headers = bld.create_obj('ns3header')
     headers.source = [
@@ -24,4 +25,5 @@
         'wifi-channel.h',
         'wifi-mode.h',
         'ssid.h',
+        'wifi-preamble.h',
         ]