diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.cc b/src/wifi/model/eht/eht-frame-exchange-manager.cc index 5c099a04f..f4bf24d10 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.cc +++ b/src/wifi/model/eht/eht-frame-exchange-manager.cc @@ -58,6 +58,31 @@ EhtFrameExchangeManager::~EhtFrameExchangeManager() NS_LOG_FUNCTION_NOARGS(); } +void +EhtFrameExchangeManager::DoDispose() +{ + NS_LOG_FUNCTION(this); + m_responseFromEmlsrClients.Cancel(); + HeFrameExchangeManager::DoDispose(); +} + +void +EhtFrameExchangeManager::RxStartIndication(WifiTxVector txVector, Time psduDuration) +{ + NS_LOG_FUNCTION(this << txVector << psduDuration.As(Time::MS)); + + HeFrameExchangeManager::RxStartIndication(txVector, psduDuration); + + if (m_txTimer.IsRunning() && m_responseFromEmlsrClients.IsRunning()) + { + m_responseFromEmlsrClients.Cancel(); + m_responseFromEmlsrClients = + Simulator::Schedule(m_txTimer.GetDelayLeft(), + &EhtFrameExchangeManager::HandleMissingResponses, + this); + } +} + void EhtFrameExchangeManager::SetLinkId(uint8_t linkId) { @@ -143,17 +168,26 @@ EhtFrameExchangeManager::ForwardPsduDown(Ptr psdu, WifiTxVector& { auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt); - if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) && - GetEmlsrSwitchToListening(psdu, aid, *clientIt)) + if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt)) { - EmlsrSwitchToListening(*clientIt, txDuration); - // this client is no longer involved in the current TXOP - clientIt = m_protectedStas.erase(clientIt); - } - else - { - clientIt++; + if (GetEmlsrSwitchToListening(psdu, aid, *clientIt)) + { + EmlsrSwitchToListening(*clientIt, txDuration); + // this client is no longer involved in the current TXOP + clientIt = m_protectedStas.erase(clientIt); + continue; + } + if (!m_responseFromEmlsrClients.IsRunning() && m_txTimer.IsRunning() && + m_txTimer.GetStasExpectedToRespond().count(*clientIt) == 1) + { + // we expect a response from this EMLSR client + m_responseFromEmlsrClients = + Simulator::Schedule(m_txTimer.GetDelayLeft(), + &EhtFrameExchangeManager::HandleMissingResponses, + this); + } } + clientIt++; } HeFrameExchangeManager::ForwardPsduDown(psdu, txVector); @@ -177,19 +211,28 @@ EhtFrameExchangeManager::ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVect { auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt); - if (auto psduMapIt = psduMap.find(aid); - GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) && - (psduMapIt == psduMap.cend() || - GetEmlsrSwitchToListening(psduMapIt->second, aid, *clientIt))) + if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt)) { - EmlsrSwitchToListening(*clientIt, txDuration); - // this client is no longer involved in the current TXOP - clientIt = m_protectedStas.erase(clientIt); - } - else - { - clientIt++; + if (auto psduMapIt = psduMap.find(aid); + 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); + continue; + } + if (!m_responseFromEmlsrClients.IsRunning() && m_txTimer.IsRunning() && + m_txTimer.GetStasExpectedToRespond().count(*clientIt) == 1) + { + // we expect a response from this EMLSR client + m_responseFromEmlsrClients = + Simulator::Schedule(m_txTimer.GetDelayLeft(), + &EhtFrameExchangeManager::HandleMissingResponses, + this); + } } + clientIt++; } HeFrameExchangeManager::ForwardPsduMapDown(psduMap, txVector); @@ -502,4 +545,23 @@ EhtFrameExchangeManager::ReceiveMpdu(Ptr mpdu, HeFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu); } +void +EhtFrameExchangeManager::HandleMissingResponses() +{ + NS_LOG_FUNCTION(this); + + // The non-AP STA affiliated with the non-AP MLD that received the initial Control frame + // does not respond to the most recently received frame from the AP affiliated with the + // AP MLD that requires immediate response after a SIFS. (Sec. 35.3.17 of 802.11be D3.1) + for (const auto& address : m_txTimer.GetStasExpectedToRespond()) + { + NS_LOG_DEBUG(address << " did not respond"); + if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address)) + { + m_protectedStas.erase(address); + EmlsrSwitchToListening(address, Seconds(0)); + } + } +} + } // 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 71ec7e6d7..4096bfe62 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.h +++ b/src/wifi/model/eht/eht-frame-exchange-manager.h @@ -87,6 +87,8 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager void NotifySwitchingEmlsrLink(Ptr phy, uint8_t linkId, Time delay); protected: + void DoDispose() override; + void RxStartIndication(WifiTxVector txVector, Time psduDuration) override; void ForwardPsduDown(Ptr psdu, WifiTxVector& txVector) override; void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector) override; void SendMuRts(const WifiTxParameters& txParams) override; @@ -107,6 +109,15 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager * \param delay the given delay */ void EmlsrSwitchToListening(const Mac48Address& address, const Time& delay); + + private: + /** + * Handle missing responses from EMLSR clients that were expected to send a response. + */ + void HandleMissingResponses(); + + EventId m_responseFromEmlsrClients; ///< timer used by an AP MLD when expecting a response from + ///< an EMLSR client }; } // namespace ns3 diff --git a/src/wifi/test/wifi-emlsr-test.cc b/src/wifi/test/wifi-emlsr-test.cc index 84b7ca500..2c0f5a305 100644 --- a/src/wifi/test/wifi-emlsr-test.cc +++ b/src/wifi/test/wifi-emlsr-test.cc @@ -2368,20 +2368,20 @@ EmlsrDlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap, auto apPhy = m_apMac->GetWifiPhy(linkId); auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, apPhy->GetPhyBand()); - auto timeout = apPhy->GetSifs() + apPhy->GetSlot() + MicroSeconds(20); m_countBlockAck++; switch (m_countBlockAck) { case 4: - // at the end of the PPDU carrying this BlockAck, the EMLSR client sending this - // frame will start a timeout interval, after which it will start the transition to - // the listening mode (such transition lasting the transition delay) + // the PPDU carrying this BlockAck is corrupted, hence the AP MLD MAC receives the + // PHY-RXSTART indication but it does not receive any frame from the PHY. Therefore, + // at the end of the PPDU transmission, the AP MLD realizes that the EMLSR client has + // not responded and assumes that the EMLSR client has started the transition to the + // listening mode (such transition lasting the transition delay) - // immediately before the end of the PPDU plus timeout, this link is not blocked - // for the EMLSR client - Simulator::Schedule(txDuration + timeout - NanoSeconds(1), [=]() { + // at the end of the PPDU, this link is not blocked for the EMLSR client + Simulator::Schedule(txDuration, [=]() { WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *addr, 0); auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId); NS_TEST_EXPECT_MSG_EQ(mask.has_value(), @@ -2393,9 +2393,8 @@ EmlsrDlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap, "Expected EMLSR link " << +linkId << " of EMLSR client " << clientId << " to be unblocked"); }); - // immediately before the end of the PPDU plus timeout, the other links are blocked - // for the EMLSR client - Simulator::Schedule(txDuration + timeout - NanoSeconds(1), [=]() { + // at the end of the PPDU, the other links are blocked for the EMLSR client + Simulator::Schedule(txDuration, [=]() { for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++) { if (id == linkId) @@ -2423,9 +2422,8 @@ EmlsrDlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap, " for one reason only"); } }); - // immediately after the end of the PPDU plus timeout, all links are blocked for the EMLSR - // client - Simulator::Schedule(txDuration + timeout + MicroSeconds(1), [=]() { + // immediately after the end of the PPDU, all links are blocked for the EMLSR client + Simulator::Schedule(txDuration + MicroSeconds(1), [=]() { for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++) { WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *addr, 0); @@ -2449,51 +2447,45 @@ EmlsrDlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap, } }); // immediately before the transition delay, all links are still blocked for the EMLSR client - Simulator::Schedule( - txDuration + timeout + m_transitionDelay.at(clientId) - NanoSeconds(1), - [=]() { - for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++) - { - WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *addr, 0); - auto mask = - m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id); - NS_TEST_EXPECT_MSG_EQ(mask.has_value(), - true, - "Expected to find a mask for EMLSR link " - << +id << " of EMLSR client " << clientId); - auto reason = static_cast( - WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY); - NS_TEST_EXPECT_MSG_EQ(mask->test(reason), - true, - "Expected EMLSR link " << +id << " of EMLSR client " - << clientId << " to be blocked"); - NS_TEST_EXPECT_MSG_EQ(mask->count(), - 1, - "Expected EMLSR link " << +id << " of EMLSR client " - << clientId - << " to be blocked " - " for one reason only"); - } - }); + Simulator::Schedule(txDuration + m_transitionDelay.at(clientId), [=]() { + for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++) + { + WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *addr, 0); + auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id); + NS_TEST_EXPECT_MSG_EQ(mask.has_value(), + true, + "Expected to find a mask for EMLSR link " + << +id << " of EMLSR client " << clientId); + auto reason = static_cast( + WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY); + NS_TEST_EXPECT_MSG_EQ(mask->test(reason), + true, + "Expected EMLSR link " << +id << " of EMLSR client " + << clientId << " to be blocked"); + NS_TEST_EXPECT_MSG_EQ(mask->count(), + 1, + "Expected EMLSR link " << +id << " of EMLSR client " + << clientId + << " to be blocked " + " for one reason only"); + } + }); // immediately after the transition delay, all links are unblocked for the EMLSR client - Simulator::Schedule( - txDuration + timeout + m_transitionDelay.at(clientId) + MicroSeconds(1), - [=]() { - for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++) - { - WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *addr, 0); - auto mask = - m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id); - NS_TEST_EXPECT_MSG_EQ(mask.has_value(), - true, - "Expected to find a mask for EMLSR link " - << +id << " of EMLSR client " << clientId); - NS_TEST_EXPECT_MSG_EQ(mask->none(), - true, - "Expected EMLSR link " << +id << " of EMLSR client " - << clientId << " to be unblocked"); - } - }); + Simulator::Schedule(txDuration + m_transitionDelay.at(clientId) + MicroSeconds(1), [=]() { + for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++) + { + WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *addr, 0); + auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id); + NS_TEST_EXPECT_MSG_EQ(mask.has_value(), + true, + "Expected to find a mask for EMLSR link " + << +id << " of EMLSR client " << clientId); + NS_TEST_EXPECT_MSG_EQ(mask->none(), + true, + "Expected EMLSR link " << +id << " of EMLSR client " + << clientId << " to be unblocked"); + } + }); // corrupt this BlockAck so that the AP MLD sends a BlockAckReq later on auto uid = psduMap.cbegin()->second->GetPacket()->GetUid();