diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.cc b/src/wifi/model/eht/eht-frame-exchange-manager.cc index 5a8f2f8c7..53e094d70 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.cc +++ b/src/wifi/model/eht/eht-frame-exchange-manager.cc @@ -1247,89 +1247,14 @@ EhtFrameExchangeManager::ReceiveMpdu(Ptr mpdu, if (trigger.IsMuRts() && m_staMac->IsEmlsrLink(m_linkId)) { // this is an initial Control frame - - /** - * It might happen that, while the aux PHY is receiving an ICF, the main PHY is - * completing a TXOP on another link or is returning to the primary link after a TXOP - * is completed on another link. In order to respond to the ICF, it is necessary that - * the main PHY has enough time to switch and be ready to operate on this link by the - * end of the ICF padding. - * - * TXOP end - * │ - * ┌───┐ another - * AP MLD │ACK│ link - * ───────────┬─────────┬┴───┴─────────────────────────────────────── - * EMLSR │ QoS │ │ main PHY - * client │ Data │ │ - * └─────────┘ │ - * ┌─────┬───┐ this - * AP MLD │ ICF │pad│ link - * ────────────────────┴─────┴───┴─────────────────────────────────── - * aux PHY - */ + if (DropReceivedIcf()) + { + return; + } auto emlsrManager = m_staMac->GetEmlsrManager(); NS_ASSERT(emlsrManager); - if (UsingOtherEmlsrLink()) - { - // we received an ICF on a link that is blocked because another EMLSR link is - // being used. Check if there is an ongoing DL TXOP on the other EMLSR link - auto apMldAddress = GetWifiRemoteStationManager()->GetMldAddress(m_bssid); - NS_ASSERT_MSG(apMldAddress, "MLD address not found for " << m_bssid); - - if (auto it = std::find_if( - m_staMac->GetLinkIds().cbegin(), - m_staMac->GetLinkIds().cend(), - /* lambda to find an EMLSR link on which there is an ongoing DL TXOP */ - [=, this](uint8_t linkId) { - auto ehtFem = StaticCast( - m_mac->GetFrameExchangeManager(linkId)); - return linkId != m_linkId && m_staMac->IsEmlsrLink(linkId) && - ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder && - m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress( - *ehtFem->m_txopHolder) == apMldAddress; - }); - it != m_staMac->GetLinkIds().cend()) - { - // AP is not expected to send ICFs on two links. If an ICF - // has been received on this link, it means that the DL TXOP - // on the other link terminated (e.g., the AP did not - // receive our response) - StaticCast(m_mac->GetFrameExchangeManager(*it)) - ->m_ongoingTxopEnd.Cancel(); - // we are going to start a TXOP on this link; unblock - // transmissions on this link, the other links will be - // blocked subsequently - m_staMac->UnblockTxOnLink({*it}, - WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK); - } - else - { - // We get here likely because transmission on the other EMLSR link - // started before the reception of the ICF ended. We drop this ICF and let the - // UL TXOP continue. - NS_LOG_DEBUG("Drop ICF because another EMLSR link is being used"); - return; - } - } - else if (auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId()); - mainPhy != m_phy) - { - const auto delay = mainPhy->GetChannelSwitchDelay(); - - if (mainPhy->GetState()->GetLastTime({WifiPhyState::TX, - // WifiPhyState::RX, comment out for now - WifiPhyState::SWITCHING, - WifiPhyState::SLEEP}) > - Simulator::Now() - delay) - { - NS_LOG_DEBUG("Drop ICF due to not enough time for the main PHY to switch link"); - return; - } - } - emlsrManager->NotifyIcfReceived(m_linkId); icfReceived = true; @@ -1363,6 +1288,102 @@ EhtFrameExchangeManager::ReceiveMpdu(Ptr mpdu, HeFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu); } +bool +EhtFrameExchangeManager::DropReceivedIcf() +{ + NS_LOG_FUNCTION(this); + + auto emlsrManager = m_staMac->GetEmlsrManager(); + NS_ASSERT(emlsrManager); + + if (UsingOtherEmlsrLink()) + { + // we received an ICF on a link that is blocked because another EMLSR link is + // being used. Check if there is an ongoing DL TXOP on the other EMLSR link + auto apMldAddress = GetWifiRemoteStationManager()->GetMldAddress(m_bssid); + NS_ASSERT_MSG(apMldAddress, "MLD address not found for " << m_bssid); + + if (auto it = std::find_if( + m_staMac->GetLinkIds().cbegin(), + m_staMac->GetLinkIds().cend(), + /* lambda to find an EMLSR link on which there is an ongoing DL TXOP */ + [=, this](uint8_t linkId) { + auto ehtFem = + StaticCast(m_mac->GetFrameExchangeManager(linkId)); + return linkId != m_linkId && m_staMac->IsEmlsrLink(linkId) && + ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder && + m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress( + *ehtFem->m_txopHolder) == apMldAddress; + }); + it != m_staMac->GetLinkIds().cend()) + { + // AP is not expected to send ICFs on two links. If an ICF + // has been received on this link, it means that the DL TXOP + // on the other link terminated (e.g., the AP did not + // receive our response) + StaticCast(m_mac->GetFrameExchangeManager(*it)) + ->m_ongoingTxopEnd.Cancel(); + // we are going to start a TXOP on this link; unblock + // transmissions on this link, the other links will be + // blocked subsequently + m_staMac->UnblockTxOnLink({m_linkId}, WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK); + } + else + { + // We get here likely because transmission on the other EMLSR link + // started before the reception of the ICF ended. We drop this ICF and let the + // UL TXOP continue. + NS_LOG_DEBUG("Drop ICF because another EMLSR link is being used"); + return true; + } + } + /** + * It might happen that, while the aux PHY is receiving an ICF, the main PHY is + * completing a TXOP on another link or is returning to the primary link after a TXOP + * is completed on another link. In order to respond to the ICF, it is necessary that + * the main PHY has enough time to switch and be ready to operate on this link by the + * end of the ICF padding. + * + * TXOP end + * │ + * ┌───┐ another + * AP MLD │ACK│ link + * ───────────┬─────────┬┴───┴─────────────────────────────────────── + * EMLSR │ QoS │ │ main PHY + * client │ Data │ │ + * └─────────┘ │ + * ┌─────┬───┐ this + * AP MLD │ ICF │pad│ link + * ────────────────────┴─────┴───┴─────────────────────────────────── + * aux PHY + */ + else if (auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId()); + mainPhy != m_phy) + { + const auto delay = mainPhy->GetChannelSwitchDelay(); + auto lastTime = mainPhy->GetState()->GetLastTime({WifiPhyState::TX}); + + if (auto lastSwitch = mainPhy->GetState()->GetLastTime({WifiPhyState::SWITCHING}); + lastSwitch > lastTime) + { + lastTime = lastSwitch; + } + if (auto lastSleep = mainPhy->GetState()->GetLastTime({WifiPhyState::SLEEP}); + lastSleep > lastTime) + { + lastTime = lastSleep; + } + // ignore RX state for now + + if (lastTime > Simulator::Now() - delay) + { + NS_LOG_DEBUG("Drop ICF due to not enough time for the main PHY to switch link"); + return true; + } + } + return false; +} + void EhtFrameExchangeManager::TxopEnd(const std::optional& txopHolder) { diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.h b/src/wifi/model/eht/eht-frame-exchange-manager.h index 05912a81b..19fef4ec5 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.h +++ b/src/wifi/model/eht/eht-frame-exchange-manager.h @@ -145,6 +145,12 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager void PsduRxError(Ptr psdu) override; private: + /** + * \return whether the received ICF must be dropped because we are unable to process it + * (e.g., another EMLSR link is being used or there is no time for main PHY switch) + */ + bool DropReceivedIcf(); + /** * Generate an in-device interference of the given power on the given link for the given * duration.