diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.cc b/src/wifi/model/eht/eht-frame-exchange-manager.cc index 59ae744ed..3e1a17bbb 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.cc +++ b/src/wifi/model/eht/eht-frame-exchange-manager.cc @@ -120,9 +120,115 @@ EhtFrameExchangeManager::ForwardPsduDown(Ptr psdu, WifiTxVector& txVector.SetSigBMode(sigBMode); } + if (!m_apMac) + { + HeFrameExchangeManager::ForwardPsduDown(psdu, txVector); + return; + } + + auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand()); + + // check if the EMLSR clients shall switch back to listening operation at the end of this PPDU + for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();) + { + auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt); + + if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) && + GetEmlsrSwitchToListening(psdu, aid, *clientIt)) + { + EmlsrSwitchToListening(*clientIt, txDuration); + // this client is no longer involved in the current TXOP + clientIt = m_protectedStas.erase(clientIt); + } + else + { + clientIt++; + } + } + HeFrameExchangeManager::ForwardPsduDown(psdu, txVector); } +void +EhtFrameExchangeManager::ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector) +{ + NS_LOG_FUNCTION(this << psduMap << txVector); + + if (!m_apMac) + { + HeFrameExchangeManager::ForwardPsduMapDown(psduMap, txVector); + return; + } + + auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand()); + + // check if the EMLSR clients shall switch back to listening operation at the end of this PPDU + for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();) + { + auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt); + + if (auto psduMapIt = psduMap.find(aid); + GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) && + (psduMapIt == psduMap.cend() || + GetEmlsrSwitchToListening(psduMapIt->second, aid, *clientIt))) + { + EmlsrSwitchToListening(*clientIt, txDuration); + // this client is no longer involved in the current TXOP + clientIt = m_protectedStas.erase(clientIt); + } + else + { + clientIt++; + } + } + + HeFrameExchangeManager::ForwardPsduMapDown(psduMap, txVector); +} + +void +EhtFrameExchangeManager::EmlsrSwitchToListening(const Mac48Address& address, const Time& delay) +{ + NS_LOG_FUNCTION(this << address << delay.As(Time::US)); + + // this EMLSR client switches back to listening operation a transition delay + // after the given delay + auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address); + NS_ASSERT(mldAddress); + auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address); + NS_ASSERT(emlCapabilities); + + for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++) + { + if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress)) + { + Simulator::Schedule(delay, [=]() { + if (linkId != m_linkId) + { + // the reason for blocking the other EMLSR links has changed now + m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK, + *mldAddress, + {linkId}); + } + + // block DL transmissions on this link until transition delay elapses + m_mac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY, + *mldAddress, + {linkId}); + }); + + // unblock all EMLSR links when the transition delay elapses + Simulator::Schedule(delay + CommonInfoBasicMle::DecodeEmlsrTransitionDelay( + emlCapabilities->emlsrTransitionDelay), + [=]() { + m_mac->UnblockUnicastTxOnLinks( + WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY, + *mldAddress, + {linkId}); + }); + } + } +} + void EhtFrameExchangeManager::SendEmlOperatingModeNotification( const Mac48Address& dest, @@ -240,4 +346,70 @@ EhtFrameExchangeManager::SendMuRts(const WifiTxParameters& txParams) HeFrameExchangeManager::SendMuRts(txParams); } +bool +EhtFrameExchangeManager::GetEmlsrSwitchToListening(Ptr psdu, + uint16_t aid, + const Mac48Address& address) const +{ + NS_LOG_FUNCTION(this << psdu << aid << address); + + // Sec. 35.3.17 of 802.11be D3.0: + // The non-AP MLD shall be switched back to the listening operation on the EMLSR links after + // the EMLSR transition delay time if [...] the non-AP STA affiliated with the non-AP MLD + // does not detect [...] any of the following frames: + // - an individually addressed frame with the RA equal to the MAC address of the non-AP STA + // affiliated with the non-AP MLD + if (psdu->GetAddr1() == address) + { + return false; + } + + // - a Trigger frame that has one of the User Info fields addressed to the non-AP STA + // affiliated with the non-AP MLD + for (const auto& mpdu : *PeekPointer(psdu)) + { + if (mpdu->GetHeader().IsTrigger()) + { + CtrlTriggerHeader trigger; + mpdu->GetPacket()->PeekHeader(trigger); + if (trigger.FindUserInfoWithAid(aid) != trigger.end()) + { + return false; + } + } + } + + // - a CTS-to-self frame with the RA equal to the MAC address of the AP affiliated with + // the AP MLD + if (psdu->GetHeader(0).IsCts()) + { + if (m_apMac && psdu->GetAddr1() == m_self) + { + return false; + } + if (m_staMac && psdu->GetAddr1() == m_bssid) + { + return false; + } + } + + // - a Multi-STA BlockAck frame that has one of the Per AID TID Info fields addressed to + // the non-AP STA affiliated with the non-AP MLD + if (psdu->GetHeader(0).IsBlockAck()) + { + CtrlBAckResponseHeader blockAck; + psdu->GetPayload(0)->PeekHeader(blockAck); + if (blockAck.IsMultiSta() && !blockAck.FindPerAidTidInfoWithAid(aid).empty()) + { + return false; + } + } + + // - a NDP Announcement frame that has one of the STA Info fields addressed to the non-AP + // STA affiliated with the non-AP MLD and a sounding NDP + // TODO NDP Announcement frame not supported yet + + return true; +} + } // namespace ns3 diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.h b/src/wifi/model/eht/eht-frame-exchange-manager.h index e8dd2403a..9a8baa760 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.h +++ b/src/wifi/model/eht/eht-frame-exchange-manager.h @@ -66,9 +66,33 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager */ std::optional GetMostRecentRssi(const Mac48Address& address) const override; + /** + * \param psdu the given PSDU + * \param aid the AID of an EMLSR client + * \param address the link MAC address of an EMLSR client + * \return whether the EMLSR client having the given AID and MAC address shall switch back to + * the listening operation when receiving the given PSDU + */ + bool GetEmlsrSwitchToListening(Ptr psdu, + uint16_t aid, + const Mac48Address& address) const; + protected: void ForwardPsduDown(Ptr psdu, WifiTxVector& txVector) override; + void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector) override; void SendMuRts(const WifiTxParameters& txParams) override; + + /** + * This method is intended to be called when an AP MLD detects that an EMLSR client previously + * involved in the current TXOP will start waiting for the transition delay interval (to switch + * back to listening operation) after the given delay. + * This method blocks the transmissions on all the EMLSR links of the given EMLSR client until + * the transition delay advertised by the EMLSR client expires. + * + * \param address the link MAC address of the given EMLSR client + * \param delay the given delay + */ + void EmlsrSwitchToListening(const Mac48Address& address, const Time& delay); }; } // namespace ns3 diff --git a/src/wifi/model/he/he-frame-exchange-manager.h b/src/wifi/model/he/he-frame-exchange-manager.h index 438ca5ddd..fc29f37be 100644 --- a/src/wifi/model/he/he-frame-exchange-manager.h +++ b/src/wifi/model/he/he-frame-exchange-manager.h @@ -256,7 +256,7 @@ class HeFrameExchangeManager : public VhtFrameExchangeManager * \param psduMap the map of PSDUs to transmit * \param txVector the TXVECTOR used to transmit the MU PPDU */ - void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector); + virtual void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector); /** * Take the necessary actions after that some BlockAck frames are missing diff --git a/src/wifi/model/wifi-mac-queue-scheduler.h b/src/wifi/model/wifi-mac-queue-scheduler.h index 27f7ba68c..7d30ace04 100644 --- a/src/wifi/model/wifi-mac-queue-scheduler.h +++ b/src/wifi/model/wifi-mac-queue-scheduler.h @@ -44,6 +44,7 @@ enum class WifiQueueBlockedReason : uint8_t WAITING_ADDBA_RESP = 0, POWER_SAVE_MODE, USING_OTHER_EMLSR_LINK, + WAITING_EMLSR_TRANSITION_DELAY, REASONS_COUNT }; @@ -65,6 +66,8 @@ operator<<(std::ostream& os, WifiQueueBlockedReason reason) return (os << "POWER_SAVE_MODE"); case WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK: return (os << "USING_OTHER_EMLSR_LINK"); + case WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY: + return (os << "WAITING_EMLSR_TRANSITION_DELAY"); case WifiQueueBlockedReason::REASONS_COUNT: return (os << "REASONS_COUNT"); default: