diff --git a/CHANGES.md b/CHANGES.md index 652b67f18..93072f69c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,6 +20,7 @@ This file is a best-effort approach to solving this issue; we will do our best b * (wifi) Added a new `AssocType` attribute to `StaWifiMac` to configure the type of association performed by a device, provided that it is supported by the standard configured for the device. By using this attribute, it is possible for an EHT single-link device to perform ML setup with an AP MLD and for an EHT multi-link device to perform legacy association with an AP MLD. * (wifi) Added a new attribute `Per20CcaSensitivityThreshold` to `EhtConfiguration` for tuning the Per 20MHz CCA threshold when 802.11be is used. * (wifi) Added a new `MaxRadioBw` attribute to `WifiPhy` to configure the maximum width supported by the radio. +* (wifi) Added a new attribute (`KeepMainPhyAfterDlTxop`) to the `AdvancedEmlsrManager` to control whether, after the end of a DL TXOP carried out on an aux PHY link, the main PHY shall stay on that link (for a switch main PHY back delay) in the attempt to gain an UL TXOP. This attribute is applicable to the case in which aux PHYs are not TX capable and do not switch link. ### Changes to existing API diff --git a/src/wifi/doc/source/wifi-design.rst b/src/wifi/doc/source/wifi-design.rst index 455d97c95..26b0837d8 100644 --- a/src/wifi/doc/source/wifi-design.rst +++ b/src/wifi/doc/source/wifi-design.rst @@ -1414,6 +1414,14 @@ a reason to postpone the switch. The switch back to the preferred link is postpo A similar check is applied to possibly postpone the switch back to the preferred link when the SwitchMainPhyBack timer expires. +The Advanced EMLSR Manager provides the ``KeepMainPhyAfterDlTxop`` attribute to control the behavior +of the main PHY at the end of a DL TXOP carried out on an aux PHY link, in case aux PHYs are not TX +capable and do not switch link. If such attribute is set to false (default value), the main PHY +immediately switches back to the preferred link. If such attribute is set to true, it is checked +whether channel access on the aux PHY link is expected to be gained within a switch main PHY back +delay plus a channel switch delay: if it is, the main PHY stays on the aux PHY link and a switch +main PHY back timer is started; otherwise, the main PHY switches back to the preferred link. + The Advanced EMLSR Manager also connects a callback to the ``NSlotsLeftAlert`` trace source of the Channel Access Manager, which sends notifications when at most a configurable number of slots remain until the backoff of an AC expires. It must be noted that this notification is only sent if channel diff --git a/src/wifi/model/eht/advanced-emlsr-manager.cc b/src/wifi/model/eht/advanced-emlsr-manager.cc index d941f4937..6d51fdc4a 100644 --- a/src/wifi/model/eht/advanced-emlsr-manager.cc +++ b/src/wifi/model/eht/advanced-emlsr-manager.cc @@ -142,7 +142,15 @@ AdvancedEmlsrManager::GetTypeId() "expires, the main PHY is switched back to the preferred link.", TimeValue(MilliSeconds(5)), MakeTimeAccessor(&AdvancedEmlsrManager::m_switchMainPhyBackDelay), - MakeTimeChecker()); + MakeTimeChecker()) + .AddAttribute("KeepMainPhyAfterDlTxop", + "In case aux PHYs are not TX capable and do not switch link, after the " + "end of a DL TXOP carried out on an aux PHY link, the main PHY stays on " + "that link for a switch main PHY back delay, if this attribute is true, " + "or it returns to the preferred link, otherwise.", + BooleanValue(false), + MakeBooleanAccessor(&AdvancedEmlsrManager::m_keepMainPhyAfterDlTxop), + MakeBooleanChecker()); return tid; } @@ -310,7 +318,7 @@ AdvancedEmlsrManager::ReceivedMacHdr(Ptr phy, // another callback (FEM::ReceivedMacHdr) from the PhyRxMacHeaderEnd trace source of // the main PHY, thus invalidating the list of callbacks on which the for loop iterates. // Hence, schedule the call to NotifyTxopEnd to execute it outside such for loop. - Simulator::ScheduleNow(&AdvancedEmlsrManager::NotifyTxopEnd, this, *linkId, false, false); + Simulator::ScheduleNow(&AdvancedEmlsrManager::NotifyTxopEnd, this, *linkId, nullptr); } // if the MAC header has been received on the link on which the main PHY is operating (or on @@ -335,9 +343,9 @@ AdvancedEmlsrManager::ReceivedMacHdr(Ptr phy, } void -AdvancedEmlsrManager::DoNotifyTxopEnd(uint8_t linkId) +AdvancedEmlsrManager::DoNotifyTxopEnd(uint8_t linkId, Ptr edca) { - NS_LOG_FUNCTION(this << linkId); + NS_LOG_FUNCTION(this << linkId << edca); auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId); @@ -358,6 +366,30 @@ AdvancedEmlsrManager::DoNotifyTxopEnd(uint8_t linkId) // or // - SwitchAuxPhy is false and there is an aux PHY to reconnect + if (!m_auxPhyTxCapable && !m_switchAuxPhy && !edca && m_keepMainPhyAfterDlTxop) + { + // DL TXOP ended, check if the main PHY must be kept on this link to try to gain an UL TXOP + NS_ASSERT_MSG(!m_switchMainPhyBackEvent.IsPending(), + "Switch main PHY back timer should not be running at the end of a DL TXOP"); + NS_ASSERT_MSG(!mainPhy->IsStateSwitching(), + "Main PHY should not be switching at the end of a DL TXOP"); + + if (GetStaMac()->GetChannelAccessManager(linkId)->GetExpectedAccessWithin( + m_switchMainPhyBackDelay) == WifiExpectedAccessReason::ACCESS_EXPECTED) + { + NS_LOG_DEBUG("Keep main PHY on link " << +linkId << " to try to gain an UL TXOP"); + m_switchMainPhyBackEvent = + Simulator::Schedule(m_switchMainPhyBackDelay, + &AdvancedEmlsrManager::SwitchMainPhyBackDelayExpired, + this, + linkId, + std::nullopt); + // start checking PHY activity on the link the main PHY is operating + RegisterListener(GetStaMac()->GetWifiPhy(linkId)); + return; + } + } + std::shared_ptr traceInfo; if (const auto it = m_rtsStartingUlTxop.find(linkId); diff --git a/src/wifi/model/eht/advanced-emlsr-manager.h b/src/wifi/model/eht/advanced-emlsr-manager.h index c26701c76..f1f1478a2 100644 --- a/src/wifi/model/eht/advanced-emlsr-manager.h +++ b/src/wifi/model/eht/advanced-emlsr-manager.h @@ -139,7 +139,7 @@ class AdvancedEmlsrManager : public DefaultEmlsrManager void UnregisterListener(); private: - void DoNotifyTxopEnd(uint8_t linkId) override; + void DoNotifyTxopEnd(uint8_t linkId, Ptr edca) override; void DoNotifyIcfReceived(uint8_t linkId) override; void DoNotifyUlTxopStart(uint8_t linkId) override; @@ -154,6 +154,9 @@ class AdvancedEmlsrManager : public DefaultEmlsrManager Time m_switchMainPhyBackDelay; //!< duration of the timer started in case of non-TX capable aux //!< PHY when medium is sensed busy during the PIFS interval //!< preceding/following the main PHY switch end + bool m_keepMainPhyAfterDlTxop; //!< whether the main PHY must stay, for a switch main PHY back + //!< delay, on an aux PHY link after a DL TXOP, in case aux PHYs + //!< are not TX capable and do not switch EventId m_ccaLastPifs; //!< event scheduled in case of non-TX capable aux PHY to //!< determine whether TX can be started based on whether //!< the medium has been idle during the last PIFS interval diff --git a/src/wifi/model/eht/default-emlsr-manager.cc b/src/wifi/model/eht/default-emlsr-manager.cc index 8e29d603f..11634c609 100644 --- a/src/wifi/model/eht/default-emlsr-manager.cc +++ b/src/wifi/model/eht/default-emlsr-manager.cc @@ -213,9 +213,9 @@ DefaultEmlsrManager::DoNotifyUlTxopStart(uint8_t linkId) } void -DefaultEmlsrManager::DoNotifyTxopEnd(uint8_t linkId) +DefaultEmlsrManager::DoNotifyTxopEnd(uint8_t linkId, Ptr edca) { - NS_LOG_FUNCTION(this << linkId); + NS_LOG_FUNCTION(this << linkId << edca); if (m_switchAuxPhy) { diff --git a/src/wifi/model/eht/default-emlsr-manager.h b/src/wifi/model/eht/default-emlsr-manager.h index a4b87662e..d87d002a2 100644 --- a/src/wifi/model/eht/default-emlsr-manager.h +++ b/src/wifi/model/eht/default-emlsr-manager.h @@ -111,7 +111,7 @@ class DefaultEmlsrManager : public EmlsrManager Time duration) override; void DoNotifyIcfReceived(uint8_t linkId) override; void DoNotifyUlTxopStart(uint8_t linkId) override; - void DoNotifyTxopEnd(uint8_t linkId) override; + void DoNotifyTxopEnd(uint8_t linkId, Ptr edca) override; void DoNotifyProtectionCompleted(uint8_t linkId) override; }; diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.cc b/src/wifi/model/eht/eht-frame-exchange-manager.cc index 332a16d80..3cb66cf90 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.cc +++ b/src/wifi/model/eht/eht-frame-exchange-manager.cc @@ -1264,12 +1264,9 @@ EhtFrameExchangeManager::NotifyChannelReleased(Ptr txop) // Notify the UL TXOP end to the EMLSR Manager auto edca = DynamicCast(txop); NS_ASSERT(edca); - auto txopStart = edca->GetTxopStartTime(m_linkId); NS_ASSERT(m_staMac->GetEmlsrManager()); - m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId, - (!txopStart || *txopStart == Simulator::Now()), - m_ongoingTxopEnd.IsPending()); + m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId, edca); } HeFrameExchangeManager::NotifyChannelReleased(txop); diff --git a/src/wifi/model/eht/emlsr-manager.cc b/src/wifi/model/eht/emlsr-manager.cc index a5b32642b..ace37d75e 100644 --- a/src/wifi/model/eht/emlsr-manager.cc +++ b/src/wifi/model/eht/emlsr-manager.cc @@ -765,9 +765,9 @@ EmlsrManager::NotifyProtectionCompleted(uint8_t linkId) } void -EmlsrManager::NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted, bool ongoingDlTxop) +EmlsrManager::NotifyTxopEnd(uint8_t linkId, Ptr edca) { - NS_LOG_FUNCTION(this << linkId << ulTxopNotStarted << ongoingDlTxop); + NS_LOG_FUNCTION(this << linkId << edca); if (!m_staMac->IsEmlsrLink(linkId)) { @@ -795,15 +795,19 @@ EmlsrManager::NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted, bool ongoingD // this link of an ICF starting a DL TXOP. If the EMLSR Manager unblocked the other EMLSR // links, another TXOP could be started on another EMLSR link (possibly leading to a crash) // while the DL TXOP on this link is ongoing. - if (ongoingDlTxop) + if (GetEhtFem(linkId)->GetOngoingTxopEndEvent().IsPending()) { NS_LOG_DEBUG("DL TXOP ongoing"); return; } - if (ulTxopNotStarted) + if (edca) { - NS_LOG_DEBUG("TXOP did not even start"); - return; + if (auto txopStart = edca->GetTxopStartTime(linkId); + !txopStart || *txopStart == Simulator::Now()) + { + NS_LOG_DEBUG("UL TXOP did not even start"); + return; + } } if (m_auxPhyToSleep) @@ -812,7 +816,7 @@ EmlsrManager::NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted, bool ongoingD SetSleepStateForAllAuxPhys(false); } - DoNotifyTxopEnd(linkId); + DoNotifyTxopEnd(linkId, edca); // unblock transmissions and resume medium access on other EMLSR links std::set linkIds; diff --git a/src/wifi/model/eht/emlsr-manager.h b/src/wifi/model/eht/emlsr-manager.h index 06cf659b5..6ebce0bfb 100644 --- a/src/wifi/model/eht/emlsr-manager.h +++ b/src/wifi/model/eht/emlsr-manager.h @@ -270,12 +270,10 @@ class EmlsrManager : public Object * Notify the end of a TXOP on the given link. * * @param linkId the ID of the given link - * @param ulTxopNotStarted whether this is a notification of the end of an UL TXOP that did - * not even start (no frame transmitted) - * @param ongoingDlTxop whether a DL TXOP is ongoing on the given link (if true, this is - * a notification of the end of an UL TXOP) + * @param edca the EDCAF that carried out the TXOP, in case of UL TXOP, or a null pointer, + * in case of DL TXOP */ - void NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted = false, bool ongoingDlTxop = false); + void NotifyTxopEnd(uint8_t linkId, Ptr edca = nullptr); /** * Notify that an STA affiliated with the EMLSR client is causing in-device interference @@ -602,8 +600,10 @@ class EmlsrManager : public Object * Notify the subclass of the end of a TXOP on the given link. * * @param linkId the ID of the given link + * @param edca the EDCAF that carried out the TXOP, in case of UL TXOP, or a null pointer, + * in case of DL TXOP */ - virtual void DoNotifyTxopEnd(uint8_t linkId) = 0; + virtual void DoNotifyTxopEnd(uint8_t linkId, Ptr edca) = 0; /** * Notify the acknowledgment of the given MPDU. diff --git a/src/wifi/test/wifi-emlsr-test.cc b/src/wifi/test/wifi-emlsr-test.cc index 9393dc599..1766fe750 100644 --- a/src/wifi/test/wifi-emlsr-test.cc +++ b/src/wifi/test/wifi-emlsr-test.cc @@ -5459,14 +5459,18 @@ EmlsrIcfSentDuringMainPhySwitchTest::RunOne() const auto interruptSwitch = ((m_testIndex & 0b010) != 0); const auto switchToOtherLink = ((m_testIndex & 0b100) != 0); + const auto keepMainPhyAfterDlTxop = useMacHdrInfo; + m_staMacs[0]->GetEmlsrManager()->SetAttribute("UseNotifiedMacHdr", BooleanValue(useMacHdrInfo)); auto advEmlsrMgr = DynamicCast(m_staMacs[0]->GetEmlsrManager()); NS_TEST_ASSERT_MSG_NE(advEmlsrMgr, nullptr, "Advanced EMLSR Manager required"); advEmlsrMgr->SetAttribute("InterruptSwitch", BooleanValue(interruptSwitch)); + advEmlsrMgr->SetAttribute("KeepMainPhyAfterDlTxop", BooleanValue(keepMainPhyAfterDlTxop)); m_testStr = "SwitchToOtherLink=" + std::to_string(switchToOtherLink) + ", InterruptSwitch=" + std::to_string(interruptSwitch) + ", UseMacHdrInfo=" + std::to_string(useMacHdrInfo) + + ", KeepMainPhyAfterDlTxop=" + std::to_string(keepMainPhyAfterDlTxop) + ", ChannelSwitchDurationIdx=" + std::to_string(m_csdIndex); NS_LOG_INFO("Starting test: " << m_testStr << "\n"); @@ -5682,6 +5686,16 @@ EmlsrIcfSentDuringMainPhySwitchTest::RunOne() Simulator::ScheduleNow([=, this]() { CheckInDeviceInterference(m_testStr + ", ACK", linkId, txDuration); }); + // check the KeepMainPhyAfterDlTxop attribute + Simulator::Schedule(txDuration + TimeStep(1), [=, this]() { + auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId); + auto shouldSwitch = (!keepMainPhyAfterDlTxop || switchToOtherLink); + NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(), + shouldSwitch, + m_testStr << ": Main PHY should " + << (shouldSwitch ? "" : "not") + << " be switching back after DL TXOP end"); + }); }); // Uplink TXOP @@ -5701,10 +5715,21 @@ EmlsrIcfSentDuringMainPhySwitchTest::RunOne() m_events.emplace_back( WIFI_MAC_CTL_ACK, [=, this](Ptr psdu, const WifiTxVector& txVector, uint8_t linkId) { + const auto txDuration = + WifiPhy::CalculateTxDuration(psdu, + txVector, + m_staMacs[0]->GetWifiPhy(linkId)->GetPhyBand()); + // check that main PHY switches back after UL TXOP + Simulator::Schedule(txDuration + TimeStep(1), [=, this]() { + auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId); + NS_TEST_EXPECT_MSG_EQ( + mainPhy->IsStateSwitching(), + true, + m_testStr << ": Main PHY should be switching back after UL TXOP end"); + }); // Continue with the next test scenario Simulator::Schedule(MilliSeconds(2), [=, this]() { NS_TEST_EXPECT_MSG_EQ(m_events.empty(), true, "Not all events took place"); - m_csdIndex = static_cast(static_cast(m_csdIndex) + 1); if (m_csdIndex == CSD_COUNT) { diff --git a/src/wifi/test/wifi-emlsr-test.h b/src/wifi/test/wifi-emlsr-test.h index aee4c8ca3..ee3fe9543 100644 --- a/src/wifi/test/wifi-emlsr-test.h +++ b/src/wifi/test/wifi-emlsr-test.h @@ -1131,7 +1131,14 @@ class SingleLinkEmlsrTest : public EmlsrOperationsTestBase * - if the main PHY switches to another link, the UL TXOP does not start because it is detected * that a frame which could be an ICF is being received on another link * - * The UL TXOP is started following the DL TXOP end. + * At the end of the DL TXOP, it is checked that: + * - if the KeepMainPhyAfterDlTxop attribute of the AdvancedEmlsrManager is false, the main PHY + * switches back to the preferred link + * - if the KeepMainPhyAfterDlTxop attribute of the AdvancedEmlsrManager is true, the main PHY + * stays on the current link to start an UL TXOP, if the UL frame can be sent on the same link + * as the DL frame, or switches back to the preferred link, otherwise + * + * At the end of the UL TXOP, the main PHY returns to the preferred link. * * It is also checked that the in-device interference generated by every transmission of the EMLSR * client is tracked by all the PHY interfaces of all the PHYs but the PHY that is transmitting