diff --git a/src/wifi/model/eht/default-emlsr-manager.cc b/src/wifi/model/eht/default-emlsr-manager.cc index 93995f4a0..7e2218543 100644 --- a/src/wifi/model/eht/default-emlsr-manager.cc +++ b/src/wifi/model/eht/default-emlsr-manager.cc @@ -68,4 +68,18 @@ DefaultEmlsrManager::GetLinkToSendEmlNotification() return *m_assocLinkId; } +std::optional +DefaultEmlsrManager::ResendNotification(Ptr mpdu) +{ + NS_LOG_FUNCTION(this); + NS_ASSERT_MSG(m_assocLinkId, "No recorded link on which Assoc Response was received"); + return *m_assocLinkId; +} + +void +DefaultEmlsrManager::NotifyEmlsrModeChanged() +{ + NS_LOG_FUNCTION(this); +} + } // namespace ns3 diff --git a/src/wifi/model/eht/default-emlsr-manager.h b/src/wifi/model/eht/default-emlsr-manager.h index 043f5443b..3f0c12269 100644 --- a/src/wifi/model/eht/default-emlsr-manager.h +++ b/src/wifi/model/eht/default-emlsr-manager.h @@ -46,9 +46,11 @@ class DefaultEmlsrManager : public EmlsrManager protected: uint8_t GetLinkToSendEmlNotification() override; + std::optional ResendNotification(Ptr mpdu) override; private: void DoNotifyMgtFrameReceived(Ptr mpdu, uint8_t linkId) override; + void NotifyEmlsrModeChanged() override; std::optional m_assocLinkId; /**< ID of the link on which Association Response was received */ diff --git a/src/wifi/model/eht/emlsr-manager.cc b/src/wifi/model/eht/emlsr-manager.cc index b5ab3b2ec..8d4462b6b 100644 --- a/src/wifi/model/eht/emlsr-manager.cc +++ b/src/wifi/model/eht/emlsr-manager.cc @@ -79,7 +79,10 @@ EmlsrManager::DoDispose() { NS_LOG_FUNCTION(this); m_staMac->TraceDisconnectWithoutContext("AckedMpdu", MakeCallback(&EmlsrManager::TxOk, this)); + m_staMac->TraceDisconnectWithoutContext("DroppedMpdu", + MakeCallback(&EmlsrManager::TxDropped, this)); m_staMac = nullptr; + m_transitionTimeoutEvent.Cancel(); Object::DoDispose(); } @@ -96,6 +99,14 @@ EmlsrManager::SetWifiMac(Ptr mac) "EmlsrManager can only be installed on non-AP MLDs"); m_staMac->TraceConnectWithoutContext("AckedMpdu", MakeCallback(&EmlsrManager::TxOk, this)); + m_staMac->TraceConnectWithoutContext("DroppedMpdu", + MakeCallback(&EmlsrManager::TxDropped, this)); +} + +const std::set& +EmlsrManager::GetEmlsrLinks() const +{ + return m_emlsrLinks; } Ptr @@ -129,7 +140,10 @@ 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 (linkIds != m_emlsrLinks) + { + m_nextEmlsrLinks = linkIds; + } if (GetStaMac() && GetStaMac()->IsAssociated() && GetTransitionTimeout() && m_nextEmlsrLinks) { @@ -154,6 +168,23 @@ EmlsrManager::NotifyMgtFrameReceived(Ptr mpdu, uint8_t linkId) // set of EMLSR links have been configured, hence enable EMLSR mode on those links SendEmlOperatingModeNotification(); } + + if (hdr.IsAction() && hdr.GetAddr2() == m_staMac->GetBssid(linkId)) + { + // this is an action frame sent by an AP of the AP MLD we are associated with + auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket()); + if (category == WifiActionHeader::PROTECTED_EHT && + action.protectedEhtAction == + WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION) + { + if (m_transitionTimeoutEvent.IsRunning()) + { + // no need to wait until the expiration of the transition timeout + m_transitionTimeoutEvent.PeekEventImpl()->Invoke(); + m_transitionTimeoutEvent.Cancel(); + } + } + } } void @@ -234,6 +265,77 @@ EmlsrManager::TxOk(Ptr mpdu) m_lastAdvPaddingDelay = mle->GetEmlsrPaddingDelay(); m_lastAdvTransitionDelay = mle->GetEmlsrTransitionDelay(); } + + if (hdr.IsMgt() && hdr.IsAction()) + { + if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket()); + category == WifiActionHeader::PROTECTED_EHT && + action.protectedEhtAction == + WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION) + { + // the EML Operating Mode Notification frame that we sent has been acknowledged. + // Start the transition timeout to wait until the request can be made effective + NS_ASSERT_MSG(m_emlsrTransitionTimeout, "No transition timeout received from AP"); + m_transitionTimeoutEvent = Simulator::Schedule(*m_emlsrTransitionTimeout, + &EmlsrManager::ChangeEmlsrMode, + this); + } + } +} + +void +EmlsrManager::TxDropped(WifiMacDropReason reason, Ptr mpdu) +{ + NS_LOG_FUNCTION(this << reason << *mpdu); + + const auto& hdr = mpdu->GetHeader(); + + if (hdr.IsMgt() && hdr.IsAction()) + { + auto pkt = mpdu->GetPacket()->Copy(); + if (auto [category, action] = WifiActionHeader::Remove(pkt); + category == WifiActionHeader::PROTECTED_EHT && + action.protectedEhtAction == + WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION) + { + // the EML Operating Mode Notification frame has been dropped. Ask the subclass + // whether the frame needs to be resent + auto linkId = ResendNotification(mpdu); + if (linkId) + { + MgtEmlOperatingModeNotification frame; + pkt->RemoveHeader(frame); + GetEhtFem(*linkId)->SendEmlOperatingModeNotification(m_staMac->GetBssid(*linkId), + frame); + } + else + { + m_nextEmlsrLinks.reset(); + } + } + } +} + +void +EmlsrManager::ChangeEmlsrMode() +{ + NS_LOG_FUNCTION(this); + + // After the successful transmission of the EML Operating Mode Notification frame by the + // non-AP STA affiliated with the non-AP MLD, the non-AP MLD shall operate in the EMLSR mode + // and the other non-AP STAs operating on the corresponding EMLSR links shall transition to + // active mode after the transition delay indicated in the Transition Timeout subfield in the + // EML Capabilities subfield of the Basic Multi-Link element or immediately after receiving an + // EML Operating Mode Notification frame from one of the APs operating on the EMLSR links and + // affiliated with the AP MLD. (Sec. 35.3.17 of 802.11be D3.0) + NS_ASSERT_MSG(m_nextEmlsrLinks, "No set of EMLSR links stored"); + m_emlsrLinks.swap(*m_nextEmlsrLinks); + m_nextEmlsrLinks.reset(); + + // TODO Make other non-AP STAs operating on the corresponding EMLSR links transition to + // active mode or passive mode (depending on whether EMLSR mode has been enabled or disabled) + + NotifyEmlsrModeChanged(); } } // namespace ns3 diff --git a/src/wifi/model/eht/emlsr-manager.h b/src/wifi/model/eht/emlsr-manager.h index efb8760cc..24e0a2e92 100644 --- a/src/wifi/model/eht/emlsr-manager.h +++ b/src/wifi/model/eht/emlsr-manager.h @@ -25,6 +25,7 @@ #include "ns3/object.h" #include "ns3/sta-wifi-mac.h" +#include #include namespace ns3 @@ -78,6 +79,11 @@ class EmlsrManager : public Object */ void SetEmlsrLinks(const std::set& linkIds); + /** + * \return the set of links on which EMLSR mode is enabled + */ + const std::set& GetEmlsrLinks() const; + /** * Notify the reception of a management frame addressed to us. * @@ -105,6 +111,15 @@ class EmlsrManager : public Object */ virtual uint8_t GetLinkToSendEmlNotification() = 0; + /** + * A previous EML Operating Mode Notification frame was dropped. Ask the subclass whether + * the frame needs to be re-sent on the given link (if any). + * + * \param mpdu the dropped MPDU that includes the EML Operating Mode Notification frame + * \return the ID of the link over which to re-send the frame, if needed + */ + virtual std::optional ResendNotification(Ptr mpdu) = 0; + Time m_emlsrPaddingDelay; //!< EMLSR Padding delay Time m_emlsrTransitionDelay; //!< EMLSR Transition delay @@ -129,14 +144,36 @@ class EmlsrManager : public Object */ void TxOk(Ptr mpdu); + /** + * Notify that the given MPDU has been discarded for the given reason. + * + * \param reason the reason why the MPDU was dropped + * \param mpdu the dropped MPDU + */ + void TxDropped(WifiMacDropReason reason, Ptr mpdu); + + /** + * This method is called to make an EMLSR mode change effective after the transition + * delay has elapsed or a notification response has been received from the AP. + */ + void ChangeEmlsrMode(); + + /** + * Notify subclass that EMLSR mode changed. + */ + virtual void NotifyEmlsrModeChanged() = 0; + Ptr m_staMac; //!< the MAC of the managed non-AP MLD std::optional