From ec1d25cc6898f6ee5cafed4cc1aac3ceb0bae84f Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Wed, 22 Feb 2023 17:54:22 +0100 Subject: [PATCH] wifi: Update EMLSR info in remote station manager upon receiving EML Notification frame --- src/wifi/model/ap-wifi-mac.cc | 120 ++++++++++++++++++ src/wifi/model/ap-wifi-mac.h | 2 + src/wifi/model/wifi-remote-station-manager.cc | 24 ++++ src/wifi/model/wifi-remote-station-manager.h | 16 +++ 4 files changed, 162 insertions(+) diff --git a/src/wifi/model/ap-wifi-mac.cc b/src/wifi/model/ap-wifi-mac.cc index f8dc297c0..1e30707b1 100644 --- a/src/wifi/model/ap-wifi-mac.cc +++ b/src/wifi/model/ap-wifi-mac.cc @@ -1468,6 +1468,25 @@ ApWifiMac::TxOk(Ptr mpdu) } } } + else if (hdr.IsAction()) + { + if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket()); + category == WifiActionHeader::PROTECTED_EHT && + action.protectedEhtAction == + WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION) + { + // the EMLSR client acknowledged the EML Operating Mode Notification frame; + // we can stop the timer and enforce the configuration deriving from the + // EML Notification frame sent by the EMLSR client + if (auto eventIt = m_transitionTimeoutEvents.find(hdr.GetAddr1()); + eventIt != m_transitionTimeoutEvents.cend() && eventIt->second.IsRunning()) + { + // no need to wait until the expiration of the transition timeout + eventIt->second.PeekEventImpl()->Invoke(); + eventIt->second.Cancel(); + } + } + } } void @@ -2043,6 +2062,107 @@ ApWifiMac::ReceiveEmlNotification(MgtEmlOperatingModeNotification& frame, return; } + if (frame.m_emlControl.emlsrParamUpdateCtrl) + { + NS_ASSERT(frame.m_emlsrParamUpdate); + auto emlCapabilities = + GetWifiRemoteStationManager(linkId)->GetStationEmlCapabilities(sender); + NS_ASSERT_MSG(emlCapabilities, "EML Capabilities not stored for STA " << sender); + + // update values stored in remote station manager + emlCapabilities->emlsrPaddingDelay = frame.m_emlsrParamUpdate->paddingDelay; + emlCapabilities->emlsrTransitionDelay = frame.m_emlsrParamUpdate->transitionDelay; + } + + auto mldAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(sender); + NS_ASSERT_MSG(mldAddress, "No MLD address stored for STA " << sender); + auto emlsrLinks = + frame.m_emlControl.emlsrMode == 1 ? frame.GetLinkBitmap() : std::list{}; + + // The AP MLD has to consider the changes carried by the received EML Notification frame + // as effective at the same time as the non-AP MLD. Therefore, we need to start a time + // when the transmission of the Ack following the received EML Notification frame is + // completed. For this purpose, we connect a callback to the PHY TX begin trace to catch + // the Ack transmitted after the EML Notification frame. + CallbackBase cb = Callback( + [=](WifiConstPsduMap psduMap, WifiTxVector txVector, double /* txPowerW */) { + NS_ASSERT_MSG(psduMap.size() == 1 && psduMap.begin()->second->GetNMpdus() == 1 && + psduMap.begin()->second->GetHeader(0).IsAck(), + "Expected a Normal Ack after EML Notification frame"); + + auto ackDuration = + WifiPhy::CalculateTxDuration(psduMap, txVector, GetLink(linkId).phy->GetPhyBand()); + + TimeValue transitionTimeout; + ehtConfiguration->GetAttribute("TransitionTimeout", transitionTimeout); + + m_transitionTimeoutEvents[sender] = + Simulator::Schedule(ackDuration + transitionTimeout.Get(), [=]() { + for (uint8_t id = 0; id < GetNLinks(); id++) + { + auto linkAddress = + GetWifiRemoteStationManager(id)->GetAffiliatedStaAddress(*mldAddress); + if (!linkAddress) + { + // this link has not been setup by the non-AP MLD + continue; + } + + if (!emlsrLinks.empty()) + { + // the non-AP MLD is enabling EMLSR mode + /** + * 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) + */ + auto enabled = std::find(emlsrLinks.cbegin(), emlsrLinks.cend(), id) != + emlsrLinks.cend(); + if (enabled) + { + StaSwitchingToActiveModeOrDeassociated(*linkAddress, id); + } + GetWifiRemoteStationManager(id)->SetEmlsrEnabled(*linkAddress, enabled); + } + else + { + // the non-AP MLD is disabling EMLSR mode + /** + * 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 disable the EMLSR mode and the other non-AP + * STAs operating on the corresponding EMLSR links shall transition to + * power save 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) + */ + if (id != linkId && + GetWifiRemoteStationManager(id)->GetEmlsrEnabled(*linkAddress)) + { + StaSwitchingToPsMode(*linkAddress, id); + } + GetWifiRemoteStationManager(id)->SetEmlsrEnabled(*linkAddress, false); + } + } + }); + }); + + // connect the callback to the PHY TX begin trace to catch the Ack and disconnect + // after its transmission begins + auto phy = GetLink(linkId).phy; + phy->TraceConnectWithoutContext("PhyTxPsduBegin", cb); + Simulator::Schedule(phy->GetSifs() + NanoSeconds(1), + [=]() { phy->TraceDisconnectWithoutContext("PhyTxPsduBegin", cb); }); + // An AP MLD with dot11EHTEMLSROptionActivated equal to true sets the EMLSR Mode subfield // to the value obtained from the EMLSR Mode subfield of the received EML Operating Mode // Notification frame. (Sec. 9.6.35.8 of 802.11be D3.0) diff --git a/src/wifi/model/ap-wifi-mac.h b/src/wifi/model/ap-wifi-mac.h index 4dde49756..580748a2b 100644 --- a/src/wifi/model/ap-wifi-mac.h +++ b/src/wifi/model/ap-wifi-mac.h @@ -527,6 +527,8 @@ class ApWifiMac : public WifiMac bool m_enableNonErpProtection; //!< Flag whether protection mechanism is used or not when //!< non-ERP STAs are present within the BSS Time m_bsrLifetime; //!< Lifetime of Buffer Status Reports + /// transition timeout events running for EMLSR clients + std::map m_transitionTimeoutEvents; /// store value and timestamp for each Buffer Status Report struct BsrType diff --git a/src/wifi/model/wifi-remote-station-manager.cc b/src/wifi/model/wifi-remote-station-manager.cc index fbe57f4bd..73b39fe92 100644 --- a/src/wifi/model/wifi-remote-station-manager.cc +++ b/src/wifi/model/wifi-remote-station-manager.cc @@ -1389,6 +1389,7 @@ WifiRemoteStationManager::LookupState(Mac48Address address) const state->m_heCapabilities = nullptr; state->m_ehtCapabilities = nullptr; state->m_emlCapabilities = nullptr; + state->m_emlsrEnabled = false; state->m_channelWidth = m_wifiPhy->GetChannelWidth(); state->m_guardInterval = GetGuardInterval(); state->m_ness = 0; @@ -1432,6 +1433,13 @@ WifiRemoteStationManager::SetQosSupport(Mac48Address from, bool qosSupported) LookupState(from)->m_qosSupported = qosSupported; } +void +WifiRemoteStationManager::SetEmlsrEnabled(const Mac48Address& from, bool emlsrEnabled) +{ + NS_LOG_FUNCTION(this << from << emlsrEnabled); + LookupState(from)->m_emlsrEnabled = emlsrEnabled; +} + void WifiRemoteStationManager::AddStationHtCapabilities(Mac48Address from, HtCapabilities htCapabilities) { @@ -1976,6 +1984,12 @@ WifiRemoteStationManager::GetEmlsrSupported(const WifiRemoteStation* station) co return emlCapabilities && emlCapabilities->emlsrSupport == 1; } +bool +WifiRemoteStationManager::GetEmlsrEnabled(const WifiRemoteStation* station) const +{ + return station->m_state->m_emlsrEnabled; +} + uint8_t WifiRemoteStationManager::GetNMcsSupported(const WifiRemoteStation* station) const { @@ -2084,6 +2098,16 @@ WifiRemoteStationManager::GetEmlsrSupported(const Mac48Address& address) const return emlCapabilities && emlCapabilities->emlsrSupport == 1; } +bool +WifiRemoteStationManager::GetEmlsrEnabled(const Mac48Address& address) const +{ + if (auto stateIt = m_states.find(address); stateIt != m_states.cend()) + { + return stateIt->second->m_emlsrEnabled; + } + return false; +} + void WifiRemoteStationManager::SetDefaultTxPowerLevel(uint8_t txPower) { diff --git a/src/wifi/model/wifi-remote-station-manager.h b/src/wifi/model/wifi-remote-station-manager.h index aa4e24cc5..cec23c5d5 100644 --- a/src/wifi/model/wifi-remote-station-manager.h +++ b/src/wifi/model/wifi-remote-station-manager.h @@ -116,6 +116,7 @@ struct WifiRemoteStationState Ptr m_ehtCapabilities; //!< remote station EHT capabilities /// remote station EML capabilities std::shared_ptr m_emlCapabilities; + bool m_emlsrEnabled; //!< whether EMLSR mode is enabled on this link uint16_t m_channelWidth; //!< Channel width (in MHz) supported by the remote station uint16_t m_guardInterval; //!< HE Guard interval duration (in nanoseconds) supported by the @@ -235,6 +236,11 @@ class WifiRemoteStationManager : public Object * \param qosSupported whether the station supports QoS */ void SetQosSupport(Mac48Address from, bool qosSupported); + /** + * \param from the address of the station being recorded + * \param emlsrEnabled whether EMLSR mode is enabled for the station on this link + */ + void SetEmlsrEnabled(const Mac48Address& from, bool emlsrEnabled); /** * Records HT capabilities of the remote station. * @@ -633,6 +639,11 @@ class WifiRemoteStationManager : public Object * \return whether the non-AP MLD supports EMLSR */ bool GetEmlsrSupported(const Mac48Address& address) const; + /** + * \param address the (MLD or link) address of the non-AP MLD + * \return whether EMLSR mode is enabled for the non-AP MLD on this link + */ + bool GetEmlsrEnabled(const Mac48Address& address) const; /** * Return a mode for non-unicast packets. @@ -1141,6 +1152,11 @@ class WifiRemoteStationManager : public Object * \return whether the non-AP MLD supports EMLSR */ bool GetEmlsrSupported(const WifiRemoteStation* station) const; + /** + * \param station the station of a non-AP MLD + * \return whether EMLSR mode is enabled for the non-AP MLD on this link + */ + bool GetEmlsrEnabled(const WifiRemoteStation* station) const; /** * Return the WifiMode supported by the specified station at the specified index. *