diff --git a/src/wifi/model/eht/advanced-emlsr-manager.cc b/src/wifi/model/eht/advanced-emlsr-manager.cc index 41182b92f..9c91456dd 100644 --- a/src/wifi/model/eht/advanced-emlsr-manager.cc +++ b/src/wifi/model/eht/advanced-emlsr-manager.cc @@ -112,12 +112,6 @@ AdvancedEmlsrManager::GetTypeId() .SetParent() .SetGroupName("Wifi") .AddConstructor() - .AddAttribute("UseNotifiedMacHdr", - "Whether to use the information about the MAC header of the MPDU " - "being received, if notified by the PHY.", - BooleanValue(true), - MakeBooleanAccessor(&AdvancedEmlsrManager::m_useNotifiedMacHdr), - MakeBooleanChecker()) .AddAttribute("AllowUlTxopInRx", "Whether a (main or aux) PHY is allowed to start an UL TXOP if " "another PHY is receiving a PPDU (possibly starting a DL TXOP). " @@ -231,92 +225,23 @@ AdvancedEmlsrManager::DoGetDelayUntilAccessRequest(uint8_t linkId) // prevent or allow an UL TXOP depending on whether another PHY is receiving a PPDU for (const auto id : GetStaMac()->GetLinkIds()) { - if (auto phy = GetStaMac()->GetWifiPhy(id); - phy && id != linkId && GetStaMac()->IsEmlsrLink(id)) + if (id == linkId) { - if (auto macHdr = GetEhtFem(id)->GetReceivedMacHdr(); macHdr && m_useNotifiedMacHdr) - { - NS_ASSERT(phy->GetState()->GetLastTime({WifiPhyState::RX}) == Simulator::Now()); - // we are receiving the MAC payload of a PSDU; if the PSDU being received on - // another link is an ICF, give up the TXOP and restart channel access at the - // end of PSDU reception. Note that we cannot be sure that the PSDU being received - // is an ICF addressed to us until we receive the entire PSDU - if (const auto& hdr = macHdr->get(); - hdr.IsTrigger() && - (hdr.GetAddr1().IsBroadcast() || hdr.GetAddr1() == GetEhtFem(id)->GetAddress())) - { - return {false, phy->GetDelayUntilIdle()}; - } - continue; - } + continue; + } - if (auto txVector = phy->GetInfoIfRxingPhyHeader()) - { - if (txVector->get().GetModulationClass() >= WIFI_MOD_CLASS_HT) - { - // The initial Control frame of frame exchanges shall be sent in the non-HT PPDU - // or non-HT duplicate PPDU format (Sec. 35.3.17 of 802.11be D7.0), so this is - // not an ICF, we can ignore it - continue; - } - // we don't know yet the type of the frame being received; prevent or allow - // the UL TXOP based on user configuration - if (!m_allowUlTxopInRx) - { - // retry channel access after the end of the current PHY header field - return {false, phy->GetDelayUntilIdle()}; - } - continue; - } + const auto [maybeIcf, delay] = CheckPossiblyReceivingIcf(id); - if (phy->IsStateRx()) - { - // we don't know yet the type of the frame being received; prevent or allow - // the UL TXOP based on user configuration - if (!m_allowUlTxopInRx) - { - if (!m_useNotifiedMacHdr) - { - // restart channel access at the end of PSDU reception - return {false, phy->GetDelayUntilIdle()}; - } + if (!maybeIcf) + { + // not receiving anything or receiving something that is certainly not an ICF + continue; + } - // retry channel access after the expected end of the MAC header reception - auto macHdrSize = WifiMacHeader(WIFI_MAC_QOSDATA).GetSerializedSize() + - 4 /* A-MPDU subframe header length */; - auto ongoingRxInfo = GetEhtFem(id)->GetOngoingRxInfo(); - // if a PHY is in RX state, it should have info about received MAC header. - // The exception is represented by this situation: - // - an aux PHY is disconnected from the MAC stack because the main PHY is - // operating on its link - // - the main PHY notifies the MAC header info to the FEM and then leaves the - // link (e.g., because it recognizes that the MPDU is not addressed to the - // EMLSR client). Disconnecting the main PHY from the MAC stack causes the - // MAC header info to be discarded by the FEM - // - the aux PHY is re-connected to the MAC stack and is still in RX state - // when the main PHY gets channel access on another link (and we get here) - if (!ongoingRxInfo.has_value()) - { - NS_ASSERT_MSG(phy != GetStaMac()->GetDevice()->GetPhy(GetMainPhyId()), - "Main PHY should have MAC header info when in RX state"); - // we are in the situation described above; if the MPDU being received - // by the aux PHY is not addressed to the EMLSR client, we can ignore it - continue; - } - const auto& txVector = ongoingRxInfo->get().txVector; - if (txVector.GetModulationClass() >= WIFI_MOD_CLASS_HT) - { - // this is not an ICF, ignore it - continue; - } - auto macHdrDuration = DataRate(txVector.GetMode().GetDataRate(txVector)) - .CalculateBytesTxTime(macHdrSize); - const auto timeSinceRxStart = - Simulator::Now() - phy->GetState()->GetLastTime({WifiPhyState::CCA_BUSY}); - return {false, Max(macHdrDuration - timeSinceRxStart, Time{0})}; - } - continue; - } + // a PPDU that may be an ICF is being received + if (!m_allowUlTxopInRx) + { + return {false, delay}; } } @@ -552,7 +477,7 @@ AdvancedEmlsrManager::SwitchMainPhyBackDelayExpired(uint8_t linkId) return; // nothing to do } - Time delay{0}; + Time extension{0}; // check if the timer must be restarted because a frame is being received on any link for (const auto id : GetStaMac()->GetLinkIds()) @@ -571,37 +496,11 @@ AdvancedEmlsrManager::SwitchMainPhyBackDelayExpired(uint8_t linkId) continue; } - if (auto macHdr = GetEhtFem(id)->GetReceivedMacHdr(); macHdr && m_useNotifiedMacHdr) + const auto [maybeIcf, delay] = CheckPossiblyReceivingIcf(id); + + if (maybeIcf) { - // the MAC header has been received; if this is a Trigger Frame, we shall restart the - // timer, so that we do not yet switch the main PHY back to the preferred link - if (const auto& hdr = macHdr->get(); - hdr.IsTrigger() && - (hdr.GetAddr1().IsBroadcast() || hdr.GetAddr1() == GetEhtFem(id)->GetAddress())) - { - delay = Max(delay, phy->GetDelayUntilIdle()); - } - } - else if (auto txVector = phy->GetInfoIfRxingPhyHeader()) - { - if (txVector->get().GetModulationClass() < WIFI_MOD_CLASS_HT) - { - // the PHY header of a non-HT PPDU, which may be an ICF, is being received; check - // again after the TX duration of a non-HT PHY header - delay = Max(delay, EMLSR_RX_PHY_START_DELAY); - } - } - else if (phy->IsStateRx()) - { - if (auto ongoingRxInfo = GetEhtFem(id)->GetOngoingRxInfo(); - ongoingRxInfo && - ongoingRxInfo->get().txVector.GetModulationClass() < WIFI_MOD_CLASS_HT) - { - // the MAC header of a non-HT PPDU, which may be an ICF, has not been received yet - // (or we cannot use its info); restart the timer, we will be called back when the - // MAC header is received - delay = Max(delay, phy->GetDelayUntilIdle()); - } + extension = Max(extension, delay); } else if (id == linkId && phy->IsStateIdle()) { @@ -610,16 +509,16 @@ AdvancedEmlsrManager::SwitchMainPhyBackDelayExpired(uint8_t linkId) // the timer to have the main PHY stay a bit longer on this link if (GetExpectedAccessWithinDelay(linkId, phy->GetChannelSwitchDelay())) { - delay = Max(delay, phy->GetChannelSwitchDelay()); + extension = Max(extension, phy->GetChannelSwitchDelay()); } } } - if (delay.IsStrictlyPositive()) + if (extension.IsStrictlyPositive()) { - NS_LOG_DEBUG("Restarting the timer, check again in " << delay.As(Time::US)); + NS_LOG_DEBUG("Restarting the timer, check again in " << extension.As(Time::US)); m_switchMainPhyBackEvent = - Simulator::Schedule(delay, + Simulator::Schedule(extension, &AdvancedEmlsrManager::SwitchMainPhyBackDelayExpired, this, linkId); diff --git a/src/wifi/model/eht/advanced-emlsr-manager.h b/src/wifi/model/eht/advanced-emlsr-manager.h index 517d4f1f8..4c7c4e8a4 100644 --- a/src/wifi/model/eht/advanced-emlsr-manager.h +++ b/src/wifi/model/eht/advanced-emlsr-manager.h @@ -117,8 +117,6 @@ class AdvancedEmlsrManager : public DefaultEmlsrManager void DoNotifyIcfReceived(uint8_t linkId) override; void DoNotifyUlTxopStart(uint8_t linkId) override; - bool m_useNotifiedMacHdr; //!< whether to use the information about the MAC header of - //!< the MPDU being received (if notified by the PHY) bool m_allowUlTxopInRx; //!< whether a (main or aux) PHY is allowed to start an UL //!< TXOP if another PHY is receiving a PPDU bool m_interruptSwitching; //!< whether a main PHY switching can be interrupted to start diff --git a/src/wifi/model/eht/emlsr-manager.cc b/src/wifi/model/eht/emlsr-manager.cc index f01d3a3bf..d404ea7c9 100644 --- a/src/wifi/model/eht/emlsr-manager.cc +++ b/src/wifi/model/eht/emlsr-manager.cc @@ -111,6 +111,12 @@ EmlsrManager::GetTypeId() BooleanValue(false), MakeBooleanAccessor(&EmlsrManager::m_auxPhyToSleep), MakeBooleanChecker()) + .AddAttribute("UseNotifiedMacHdr", + "Whether to use the information about the MAC header of the MPDU " + "being received, if notified by the PHY.", + BooleanValue(true), + MakeBooleanAccessor(&EmlsrManager::m_useNotifiedMacHdr), + MakeBooleanChecker()) .AddAttribute( "EmlsrLinkSet", "IDs of the links on which EMLSR mode will be enabled. An empty set " @@ -504,6 +510,90 @@ EmlsrManager::GetDelayUntilAccessRequest(uint8_t linkId, AcIndex aci) return GetDelayUnlessMainPhyTakesOverUlTxop(linkId); } +std::pair +EmlsrManager::CheckPossiblyReceivingIcf(uint8_t linkId) const +{ + NS_LOG_FUNCTION(this << linkId); + + auto phy = GetStaMac()->GetWifiPhy(linkId); + + if (!phy || !GetStaMac()->IsEmlsrLink(linkId)) + { + NS_LOG_DEBUG("No PHY (" << phy << ") or not an EMLSR link (" << +linkId << ")"); + return {false, Time{0}}; + } + + if (auto macHdr = GetEhtFem(linkId)->GetReceivedMacHdr(); macHdr && m_useNotifiedMacHdr) + { + NS_LOG_DEBUG("Receiving the MAC payload of a PSDU and MAC header info can be used"); + NS_ASSERT(phy->GetState()->GetLastTime({WifiPhyState::RX}) == Simulator::Now()); + + if (const auto& hdr = macHdr->get(); + hdr.IsTrigger() && + (hdr.GetAddr1().IsBroadcast() || hdr.GetAddr1() == GetEhtFem(linkId)->GetAddress())) + { + // the PSDU being received _may_ be an ICF. Note that we cannot be sure that the PSDU + // being received is an ICF addressed to us until we receive the entire PSDU + NS_LOG_DEBUG("Based on MAC header, may be an ICF, postpone by " + << phy->GetDelayUntilIdle().As(Time::US)); + return {true, phy->GetDelayUntilIdle()}; + } + } + else if (auto txVector = phy->GetInfoIfRxingPhyHeader()) + { + NS_LOG_DEBUG("Receiving PHY header"); + if (txVector->get().GetModulationClass() < WIFI_MOD_CLASS_HT) + { + // the PHY header of a non-HT PPDU, which may be an ICF, is being received; check + // again after the TX duration of a non-HT PHY header + NS_LOG_DEBUG("PHY header of a non-HT PPDU, which may be an ICF, is being received"); + return {true, EMLSR_RX_PHY_START_DELAY}; + } + } + else if (phy->IsStateRx()) + { + // we have not yet received the MAC header or we cannot use its info + + auto ongoingRxInfo = GetEhtFem(linkId)->GetOngoingRxInfo(); + // if a PHY is in RX state, it should have info about received MAC header. + // The exception is represented by this situation: + // - an aux PHY is disconnected from the MAC stack because the main PHY is + // operating on its link + // - the main PHY notifies the MAC header info to the FEM and then leaves the + // link (e.g., because it recognizes that the MPDU is not addressed to the + // EMLSR client). Disconnecting the main PHY from the MAC stack causes the + // MAC header info to be discarded by the FEM + // - the aux PHY is re-connected to the MAC stack and is still in RX state + // when the main PHY gets channel access on another link (and we get here) + if (!ongoingRxInfo.has_value()) + { + NS_ASSERT_MSG(phy != GetStaMac()->GetDevice()->GetPhy(GetMainPhyId()), + "Main PHY should have MAC header info when in RX state"); + // we are in the situation described above; if the MPDU being received + // by the aux PHY is not addressed to the EMLSR client, we can ignore it + } + else if (const auto& txVector = ongoingRxInfo->get().txVector; + txVector.GetModulationClass() < WIFI_MOD_CLASS_HT) + { + if (auto remTime = phy->GetTimeToMacHdrEnd(SU_STA_ID); + m_useNotifiedMacHdr && remTime.has_value() && remTime->IsStrictlyPositive()) + { + NS_LOG_DEBUG("Wait until the expected end of the MAC header reception: " + << remTime->As(Time::US)); + return {true, *remTime}; + } + + NS_LOG_DEBUG( + "MAC header info will not be available. Wait until the end of PSDU reception: " + << phy->GetDelayUntilIdle().As(Time::US)); + return {true, phy->GetDelayUntilIdle()}; + } + } + + NS_LOG_DEBUG("No ICF being received, state: " << phy->GetState()->GetState()); + return {false, Time{0}}; +} + void EmlsrManager::NotifyUlTxopStart(uint8_t linkId) { diff --git a/src/wifi/model/eht/emlsr-manager.h b/src/wifi/model/eht/emlsr-manager.h index 101007074..70108c129 100644 --- a/src/wifi/model/eht/emlsr-manager.h +++ b/src/wifi/model/eht/emlsr-manager.h @@ -334,6 +334,16 @@ class EmlsrManager : public Object */ bool MediumSyncDelayNTxopsExceeded(uint8_t linkId); + /** + * Check whether a PPDU that may be an ICF is being received on the given link. If so, return + * true along with the time to wait to know more information about the PPDU being received. + * Otherwise, return false. + * + * @param linkId the ID of the given link + * @return a pair indicating whether a PPDU that may be an ICF is being received on the link + */ + std::pair CheckPossiblyReceivingIcf(uint8_t linkId) const; + protected: void DoDispose() override; @@ -505,8 +515,10 @@ class EmlsrManager : public Object MHz_u m_auxPhyMaxWidth; //!< max channel width supported by aux PHYs WifiModulationClass m_auxPhyMaxModClass; //!< max modulation class supported by aux PHYs bool m_auxPhyTxCapable; //!< whether Aux PHYs are capable of transmitting PPDUs - bool m_auxPhyToSleep; //!< whether Aux PHYs should be put into sleep mode while the Main PHY - //!< is carrying out a (DL or UL) TXOP + bool m_auxPhyToSleep; //!< whether Aux PHYs should be put into sleep mode while the Main PHY + //!< is carrying out a (DL or UL) TXOP + bool m_useNotifiedMacHdr; //!< whether to use the information about the MAC header of + //!< the MPDU being received (if notified by the PHY) std::map m_auxPhyToSleepEvents; //!< PHY ID-indexed map of events scheduled to //!< put an Aux PHY to sleep std::map m_startSleep; //!< PHY ID-indexed map of last time sleep mode started