diff --git a/src/wifi/model/eht/default-emlsr-manager.cc b/src/wifi/model/eht/default-emlsr-manager.cc index 9251c031e..93995f4a0 100644 --- a/src/wifi/model/eht/default-emlsr-manager.cc +++ b/src/wifi/model/eht/default-emlsr-manager.cc @@ -20,6 +20,7 @@ #include "default-emlsr-manager.h" #include "ns3/log.h" +#include "ns3/wifi-mpdu.h" namespace ns3 { @@ -48,4 +49,23 @@ DefaultEmlsrManager::~DefaultEmlsrManager() NS_LOG_FUNCTION_NOARGS(); } +void +DefaultEmlsrManager::DoNotifyMgtFrameReceived(Ptr mpdu, uint8_t linkId) +{ + NS_LOG_FUNCTION(this << *mpdu << linkId); + + if (mpdu->GetHeader().IsAssocResp() && GetStaMac()->IsAssociated() && GetTransitionTimeout()) + { + m_assocLinkId = linkId; + } +} + +uint8_t +DefaultEmlsrManager::GetLinkToSendEmlNotification() +{ + NS_LOG_FUNCTION(this); + NS_ASSERT_MSG(m_assocLinkId, "No recorded link on which Assoc Response was received"); + return *m_assocLinkId; +} + } // namespace ns3 diff --git a/src/wifi/model/eht/default-emlsr-manager.h b/src/wifi/model/eht/default-emlsr-manager.h index 2f95b3cff..043f5443b 100644 --- a/src/wifi/model/eht/default-emlsr-manager.h +++ b/src/wifi/model/eht/default-emlsr-manager.h @@ -22,6 +22,8 @@ #include "emlsr-manager.h" +#include + namespace ns3 { @@ -41,6 +43,15 @@ class DefaultEmlsrManager : public EmlsrManager DefaultEmlsrManager(); ~DefaultEmlsrManager() override; + + protected: + uint8_t GetLinkToSendEmlNotification() override; + + private: + void DoNotifyMgtFrameReceived(Ptr mpdu, uint8_t linkId) override; + + std::optional m_assocLinkId; /**< ID of the link on which Association Response + was received */ }; } // namespace ns3 diff --git a/src/wifi/model/eht/emlsr-manager.cc b/src/wifi/model/eht/emlsr-manager.cc index 463a57e93..b5ab3b2ec 100644 --- a/src/wifi/model/eht/emlsr-manager.cc +++ b/src/wifi/model/eht/emlsr-manager.cc @@ -23,7 +23,10 @@ #include "eht-frame-exchange-manager.h" #include "ns3/abort.h" +#include "ns3/assert.h" +#include "ns3/attribute-container.h" #include "ns3/log.h" +#include "ns3/wifi-mpdu.h" namespace ns3 { @@ -50,7 +53,14 @@ EmlsrManager::GetTypeId() "Possible values are 0 us, 16 us, 32 us, 64 us, 128 us or 256 us.", TimeValue(MicroSeconds(0)), MakeTimeAccessor(&EmlsrManager::m_emlsrTransitionDelay), - MakeTimeChecker(MicroSeconds(0), MicroSeconds(256))); + MakeTimeChecker(MicroSeconds(0), MicroSeconds(256))) + .AddAttribute( + "EmlsrLinkSet", + "IDs of the links on which EMLSR mode will be enabled. An empty set " + "indicates to disable EMLSR.", + AttributeContainerValue(), + MakeAttributeContainerAccessor(&EmlsrManager::SetEmlsrLinks), + MakeAttributeContainerChecker(MakeUintegerChecker())); return tid; } @@ -68,6 +78,7 @@ void EmlsrManager::DoDispose() { NS_LOG_FUNCTION(this); + m_staMac->TraceDisconnectWithoutContext("AckedMpdu", MakeCallback(&EmlsrManager::TxOk, this)); m_staMac = nullptr; Object::DoDispose(); } @@ -83,6 +94,8 @@ EmlsrManager::SetWifiMac(Ptr mac) NS_ABORT_MSG_IF(m_staMac->GetNLinks() <= 1, "EmlsrManager can only be installed on MLDs"); NS_ABORT_MSG_IF(m_staMac->GetTypeOfStation() != STA, "EmlsrManager can only be installed on non-AP MLDs"); + + m_staMac->TraceConnectWithoutContext("AckedMpdu", MakeCallback(&EmlsrManager::TxOk, this)); } Ptr @@ -110,4 +123,117 @@ EmlsrManager::GetTransitionTimeout() const return m_emlsrTransitionTimeout; } +void +EmlsrManager::SetEmlsrLinks(const std::set& linkIds) +{ + NS_LOG_FUNCTION(this); + NS_ABORT_MSG_IF(linkIds.size() == 1, "Cannot enable EMLSR mode on a single link"); + + m_nextEmlsrLinks = linkIds; + + if (GetStaMac() && GetStaMac()->IsAssociated() && GetTransitionTimeout() && m_nextEmlsrLinks) + { + // Request to enable EMLSR mode on the given links, provided that they have been setup + SendEmlOperatingModeNotification(); + } +} + +void +EmlsrManager::NotifyMgtFrameReceived(Ptr mpdu, uint8_t linkId) +{ + NS_LOG_FUNCTION(this << *mpdu << linkId); + + const auto& hdr = mpdu->GetHeader(); + + DoNotifyMgtFrameReceived(mpdu, linkId); + + if (hdr.IsAssocResp() && GetStaMac()->IsAssociated() && GetTransitionTimeout() && + m_nextEmlsrLinks && !m_nextEmlsrLinks->empty()) + { + // we just completed ML setup with an AP MLD that supports EMLSR and a non-empty + // set of EMLSR links have been configured, hence enable EMLSR mode on those links + SendEmlOperatingModeNotification(); + } +} + +void +EmlsrManager::SendEmlOperatingModeNotification() +{ + NS_LOG_FUNCTION(this); + + NS_ABORT_MSG_IF(!m_emlsrTransitionTimeout, + "AP did not advertise a Transition Timeout, cannot send EML notification"); + NS_ASSERT_MSG(m_nextEmlsrLinks, "Need to set EMLSR links before calling this method"); + + MgtEmlOperatingModeNotification frame; + + // Add the EMLSR Parameter Update field if needed + if (m_lastAdvPaddingDelay != m_emlsrPaddingDelay || + m_lastAdvTransitionDelay != m_emlsrTransitionDelay) + { + m_lastAdvPaddingDelay = m_emlsrPaddingDelay; + m_lastAdvTransitionDelay = m_emlsrTransitionDelay; + frame.m_emlControl.emlsrParamUpdateCtrl = 1; + frame.m_emlsrParamUpdate = MgtEmlOperatingModeNotification::EmlsrParamUpdate{}; + frame.m_emlsrParamUpdate->paddingDelay = + CommonInfoBasicMle::EncodeEmlsrPaddingDelay(m_lastAdvPaddingDelay); + frame.m_emlsrParamUpdate->transitionDelay = + CommonInfoBasicMle::EncodeEmlsrTransitionDelay(m_lastAdvTransitionDelay); + } + + // We must verify that the links included in the given EMLSR link set (if any) have been setup. + auto setupLinkIds = m_staMac->GetSetupLinkIds(); + + for (auto emlsrLinkIt = m_nextEmlsrLinks->begin(); emlsrLinkIt != m_nextEmlsrLinks->end();) + { + if (auto setupLinkIt = setupLinkIds.find(*emlsrLinkIt); setupLinkIt != setupLinkIds.cend()) + { + setupLinkIds.erase(setupLinkIt); + auto apLinkId = m_staMac->GetApLinkId(*emlsrLinkIt); + NS_ASSERT(apLinkId); + frame.SetLinkIdInBitmap(*apLinkId); + emlsrLinkIt++; + } + else + { + NS_LOG_DEBUG("Link ID " << +(*emlsrLinkIt) << " has not been setup"); + emlsrLinkIt = m_nextEmlsrLinks->erase(emlsrLinkIt); + } + } + + // EMLSR Mode is enabled if and only if the set of EMLSR links is not empty + frame.m_emlControl.emlsrMode = m_nextEmlsrLinks->empty() ? 0 : 1; + + // TODO if this is a single radio non-AP MLD and not all setup links are in the EMLSR link + // set, we have to put setup links that are not included in the given EMLSR link set (i.e., + // those remaining in setupLinkIds, if m_nextEmlsrLinks is not empty) in the sleep mode: + // For the EMLSR mode enabled in a single radio non-AP MLD, the STA(s) affiliated with + // the non-AP MLD that operates on the enabled link(s) that corresponds to the bit + // position(s) of the EMLSR Link Bitmap subfield set to 0 shall be in doze state if a + // non-AP STA affiliated with the non-AP MLD that operates on one of the EMLSR links is + // in awake state. (Sec. 35.3.17 of 802.11be D3.0) + + auto linkId = GetLinkToSendEmlNotification(); + GetEhtFem(linkId)->SendEmlOperatingModeNotification(m_staMac->GetBssid(linkId), frame); +} + +void +EmlsrManager::TxOk(Ptr mpdu) +{ + NS_LOG_FUNCTION(this << *mpdu); + + const auto& hdr = mpdu->GetHeader(); + + if (hdr.IsAssocReq()) + { + // store padding delay and transition delay advertised in AssocReq + MgtAssocRequestHeader assocReq; + mpdu->GetPacket()->PeekHeader(assocReq); + auto& mle = assocReq.Get(); + NS_ASSERT_MSG(mle, "AssocReq should contain a Multi-Link Element"); + m_lastAdvPaddingDelay = mle->GetEmlsrPaddingDelay(); + m_lastAdvTransitionDelay = mle->GetEmlsrTransitionDelay(); + } +} + } // namespace ns3 diff --git a/src/wifi/model/eht/emlsr-manager.h b/src/wifi/model/eht/emlsr-manager.h index 2ab5cc04a..efb8760cc 100644 --- a/src/wifi/model/eht/emlsr-manager.h +++ b/src/wifi/model/eht/emlsr-manager.h @@ -20,13 +20,18 @@ #ifndef EMLSR_MANAGER_H #define EMLSR_MANAGER_H +#include "ns3/mac48-address.h" +#include "ns3/mgt-headers.h" #include "ns3/object.h" #include "ns3/sta-wifi-mac.h" +#include + namespace ns3 { class EhtFrameExchangeManager; +class WifiMpdu; /** * \ingroup wifi @@ -64,6 +69,23 @@ class EmlsrManager : public Object */ std::optional