diff --git a/src/devices/wifi/mac-low.cc b/src/devices/wifi/mac-low.cc new file mode 100644 index 000000000..db2cfe256 --- /dev/null +++ b/src/devices/wifi/mac-low.cc @@ -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 + */ + +#include + +#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 +# 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.ns3.inria.fr"); + return uid; +} +void +SnrTag::Print (std::ostream &os) const +{ + os << "snr="<Cancel (); + } +} + +/**************************************************************************** + * API methods below. + ****************************************************************************/ + +void +MacLow::SetInterface (Ptr 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="<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="<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="<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 diff --git a/src/devices/wifi/mac-low.h b/src/devices/wifi/mac-low.h new file mode 100644 index 000000000..7f9d3d76a --- /dev/null +++ b/src/devices/wifi/mac-low.h @@ -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 + */ +#ifndef MAC_LOW_H +#define MAC_LOW_H + +#include +#include + +#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 MacLowRxCallback; + + MacLow (); + ~MacLow (); + + void SetInterface (Ptr 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¶meters) 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 ¶ms) 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 m_interface; + WifiPhy *m_phy; + MacStations *m_stations; + MacParameters *m_parameters; + MacLowRxCallback m_rxCallback; + typedef std::vector::const_iterator NavListenersCI; + typedef std::vector 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 m_dropError; +}; + +}; // namespace ns3 + +#endif /* MAC_LOW_H */ diff --git a/src/devices/wifi/wifi-net-device.h b/src/devices/wifi/wifi-net-device.h index d3f3a0c9f..c0b21c22a 100644 --- a/src/devices/wifi/wifi-net-device.h +++ b/src/devices/wifi/wifi-net-device.h @@ -54,6 +54,7 @@ public: void ConnectTo (Ptr channel); + Mac48Address GetSelfAddress (void) const; virtual Mac48Address GetBssid (void) const = 0; virtual Ssid GetSsid (void) const = 0; diff --git a/src/devices/wifi/wscript b/src/devices/wifi/wscript index 3204f5715..73e18f237 100644 --- a/src/devices/wifi/wscript +++ b/src/devices/wifi/wscript @@ -16,6 +16,7 @@ def build(bld): '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 @@ def build(bld): 'wifi-channel.h', 'wifi-mode.h', 'ssid.h', + 'wifi-preamble.h', ]