wifi: AP MLD starts transition delay when EMLSR client switches to listening operations
... when the remaining TXOP time in a DL TXOP is not enough to send a CF-End frame
This commit is contained in:
@@ -580,9 +580,9 @@ EhtFrameExchangeManager::IntraBssNavResetTimeout()
|
||||
}
|
||||
|
||||
bool
|
||||
EhtFrameExchangeManager::UnblockEmlsrLinksIfAllowed(Mac48Address address)
|
||||
EhtFrameExchangeManager::UnblockEmlsrLinksIfAllowed(Mac48Address address, bool checkThisLink)
|
||||
{
|
||||
NS_LOG_FUNCTION(this << address);
|
||||
NS_LOG_FUNCTION(this << address << checkThisLink);
|
||||
|
||||
auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
|
||||
NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
|
||||
@@ -634,9 +634,8 @@ EhtFrameExchangeManager::UnblockEmlsrLinksIfAllowed(Mac48Address address)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (linkId == m_linkId)
|
||||
if (linkId == m_linkId && !checkThisLink)
|
||||
{
|
||||
// no need to check if the EMLSR client is involved in a DL TXOP on this link
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -670,8 +669,8 @@ EhtFrameExchangeManager::EmlsrSwitchToListening(Mac48Address address, const Time
|
||||
NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
|
||||
NS_ASSERT_MSG(m_apMac, "This function shall only be called by AP MLDs");
|
||||
|
||||
auto blockLinks = [=, this]() {
|
||||
if (!UnblockEmlsrLinksIfAllowed(address))
|
||||
auto blockLinks = [=, this](bool checkThisLink) {
|
||||
if (!UnblockEmlsrLinksIfAllowed(address, checkThisLink))
|
||||
{
|
||||
NS_LOG_DEBUG("Could not unblock transmissions to " << address);
|
||||
return;
|
||||
@@ -709,7 +708,16 @@ EhtFrameExchangeManager::EmlsrSwitchToListening(Mac48Address address, const Time
|
||||
Simulator::Schedule(endDelay, unblockLinks));
|
||||
};
|
||||
|
||||
delay.IsZero() ? blockLinks() : static_cast<void>(Simulator::Schedule(delay, blockLinks));
|
||||
// it makes sense to check if the EMLSR client is involved in a DL TXOP on this link only if
|
||||
// the transition delay start is scheduled to start after some delay, because the AP MLD may
|
||||
// start another DL TXOP in the meantime. An example is when the AP MLD terminates a TXOP on
|
||||
// this link due to the remaining TXOP time being not enough to send another frame (not even a
|
||||
// CF-End), delays the start of the transition delay to align with the EMLSR client (which is
|
||||
// waiting for a SIFS + slot + PHY RXSTART delay after the last frame to switch to listening
|
||||
// operations), gains channel access on this link again before starting the transition delay
|
||||
// timer and sends an ICF.
|
||||
delay.IsZero() ? blockLinks(false)
|
||||
: static_cast<void>(Simulator::Schedule(delay, [=]() { blockLinks(true); }));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -922,8 +930,8 @@ EhtFrameExchangeManager::SwitchToListeningOrUnblockLinks(const std::set<Mac48Add
|
||||
{
|
||||
// EMLSR client switched to listening operations if it was protected, otherwise
|
||||
// simply unblock transmissions
|
||||
m_protectedStas.contains(address) ? EmlsrSwitchToListening(address, Seconds(0))
|
||||
: (void)(UnblockEmlsrLinksIfAllowed(address));
|
||||
m_protectedStas.contains(address) ? EmlsrSwitchToListening(address, Time{0})
|
||||
: (void)(UnblockEmlsrLinksIfAllowed(address, false));
|
||||
m_protectedStas.erase(address);
|
||||
}
|
||||
}
|
||||
@@ -1233,13 +1241,21 @@ EhtFrameExchangeManager::NotifyChannelReleased(Ptr<Txop> txop)
|
||||
|
||||
if (m_apMac)
|
||||
{
|
||||
// the channel has been released; all EMLSR clients are switching back to
|
||||
// listening operation
|
||||
// the channel has been released; if the TXNAV is still set, it means that there is not
|
||||
// enough time left to send a CF-End. In this case, EMLSR clients wait for a slot plus the
|
||||
// PHY RX start delay before switching back to listening operation (in this case, this
|
||||
// function is called a SIFS after the last frame in the TXOP)
|
||||
Time delay{0};
|
||||
if (const auto remTxNav = m_txNav - Simulator::Now(); remTxNav.IsStrictlyPositive())
|
||||
{
|
||||
delay = Min(m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY, remTxNav);
|
||||
}
|
||||
|
||||
for (const auto& address : m_protectedStas)
|
||||
{
|
||||
if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
|
||||
{
|
||||
EmlsrSwitchToListening(address, Seconds(0));
|
||||
EmlsrSwitchToListening(address, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1748,6 +1764,8 @@ EhtFrameExchangeManager::UpdateTxopEndOnTxStart(Time txDuration, Time durationId
|
||||
// after the end of this PPDU, hence we need to postpone the TXOP end in order to
|
||||
// get the PHY-RXSTART.indication
|
||||
delay = txDuration + m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY;
|
||||
// TXOP end cannot be beyond the period protected via Duration/ID
|
||||
delay = Min(delay, txDuration + durationId);
|
||||
}
|
||||
|
||||
NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
|
||||
@@ -1801,6 +1819,8 @@ EhtFrameExchangeManager::UpdateTxopEndOnRxEnd(Time durationId)
|
||||
// we may send a response after a SIFS or we may receive another frame after a SIFS.
|
||||
// Postpone the TXOP end by considering the latter (which takes longer)
|
||||
auto delay = m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY;
|
||||
// TXOP end cannot be beyond the period protected via Duration/ID
|
||||
delay = Min(delay, durationId);
|
||||
NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
|
||||
m_ongoingTxopEnd =
|
||||
Simulator::Schedule(delay, &EhtFrameExchangeManager::TxopEnd, this, m_txopHolder);
|
||||
|
||||
@@ -245,9 +245,11 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager
|
||||
* is not involved in any DL or UL TXOP on another link.
|
||||
*
|
||||
* @param address the link MAC address of the given EMLSR client
|
||||
* @param checkThisLink whether to check if the EMLSR client is involved in a DL TXOP on
|
||||
* this link
|
||||
* @return whether transmissions could be unblocked
|
||||
*/
|
||||
bool UnblockEmlsrLinksIfAllowed(Mac48Address address);
|
||||
bool UnblockEmlsrLinksIfAllowed(Mac48Address address, bool checkThisLink);
|
||||
|
||||
private:
|
||||
/**
|
||||
|
||||
@@ -612,14 +612,8 @@ FrameExchangeManager::ForwardMpduDown(Ptr<WifiMpdu> mpdu, WifiTxVector& txVector
|
||||
auto psdu = Create<WifiPsdu>(mpdu, false);
|
||||
FinalizeMacHeader(psdu);
|
||||
m_allowedWidth = std::min(m_allowedWidth, txVector.GetChannelWidth());
|
||||
auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
|
||||
// The TXNAV timer is a single timer, shared by the EDCAFs within a STA, that is initialized
|
||||
// with the duration from the Duration/ID field in the frame most recently successfully
|
||||
// transmitted by the TXOP holder, except for PS-Poll frames. (Sec.10.23.2.2 IEEE 802.11-2020)
|
||||
if (!mpdu->GetHeader().IsPsPoll())
|
||||
{
|
||||
m_txNav = Max(m_txNav, Simulator::Now() + txDuration + mpdu->GetHeader().GetDuration());
|
||||
}
|
||||
const auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
|
||||
SetTxNav(mpdu, txDuration);
|
||||
m_phy->Send(psdu, txVector);
|
||||
}
|
||||
|
||||
@@ -1018,11 +1012,11 @@ FrameExchangeManager::TransmissionFailed(bool forceCurrentCw)
|
||||
m_dcf->UpdateFailedCw(m_linkId);
|
||||
}
|
||||
m_sentFrameTo.clear();
|
||||
// reset TXNAV because transmission failed
|
||||
ResetTxNav();
|
||||
// A non-QoS station always releases the channel upon a transmission failure
|
||||
NotifyChannelReleased(m_dcf);
|
||||
m_dcf = nullptr;
|
||||
// reset TXNAV because transmission failed
|
||||
m_txNav = Simulator::Now();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1368,6 +1362,29 @@ FrameExchangeManager::NavResetTimeout()
|
||||
m_channelAccessManager->NotifyNavResetNow(Seconds(0));
|
||||
}
|
||||
|
||||
void
|
||||
FrameExchangeManager::SetTxNav(Ptr<const WifiMpdu> mpdu, const Time& txDuration)
|
||||
{
|
||||
// The TXNAV timer is a single timer, shared by the EDCAFs within a STA, that is initialized
|
||||
// with the duration from the Duration/ID field in the frame most recently successfully
|
||||
// transmitted by the TXOP holder, except for PS-Poll frames. The TXNAV timer begins counting
|
||||
// down from the end of the transmission of the PPDU containing that frame.
|
||||
// (Sec.10.23.2.2 IEEE 802.11-2020)
|
||||
if (!mpdu->GetHeader().IsPsPoll())
|
||||
{
|
||||
const auto txNav = Simulator::Now() + txDuration + mpdu->GetHeader().GetDuration();
|
||||
NS_LOG_DEBUG("Setting TXNAV to " << txNav.As(Time::S));
|
||||
m_txNav = Max(m_txNav, txNav);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FrameExchangeManager::ResetTxNav()
|
||||
{
|
||||
NS_LOG_FUNCTION(this);
|
||||
m_txNav = Simulator::Now();
|
||||
}
|
||||
|
||||
bool
|
||||
FrameExchangeManager::VirtualCsMediumIdle() const
|
||||
{
|
||||
|
||||
@@ -365,6 +365,17 @@ class FrameExchangeManager : public Object
|
||||
*/
|
||||
virtual void NavResetTimeout();
|
||||
|
||||
/**
|
||||
* Set the TXNAV upon sending an MPDU.
|
||||
*
|
||||
* @param mpdu the MPDU being sent
|
||||
* @param txDuration the TX duration of the MPDU
|
||||
*/
|
||||
void SetTxNav(Ptr<const WifiMpdu> mpdu, const Time& txDuration);
|
||||
|
||||
/// Reset the TXNAV
|
||||
void ResetTxNav();
|
||||
|
||||
/**
|
||||
* This method is called when the reception of a PSDU fails.
|
||||
*
|
||||
|
||||
@@ -941,11 +941,8 @@ HeFrameExchangeManager::ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVecto
|
||||
txVector.SetAggregation(true);
|
||||
}
|
||||
|
||||
auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand());
|
||||
// The TXNAV timer is a single timer, shared by the EDCAFs within a STA, that is initialized
|
||||
// with the duration from the Duration/ID field in the frame most recently successfully
|
||||
// transmitted by the TXOP holder, except for PS-Poll frames. (Sec.10.23.2.2 IEEE 802.11-2020)
|
||||
m_txNav = Max(m_txNav, Simulator::Now() + txDuration + psduMap.cbegin()->second->GetDuration());
|
||||
const auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand());
|
||||
SetTxNav(*psduMap.cbegin()->second->begin(), txDuration);
|
||||
|
||||
m_phy->Send(psduMap, txVector);
|
||||
}
|
||||
|
||||
@@ -1367,14 +1367,8 @@ HtFrameExchangeManager::ForwardPsduDown(Ptr<const WifiPsdu> psdu, WifiTxVector&
|
||||
txVector.SetAggregation(true);
|
||||
}
|
||||
|
||||
auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
|
||||
// The TXNAV timer is a single timer, shared by the EDCAFs within a STA, that is initialized
|
||||
// with the duration from the Duration/ID field in the frame most recently successfully
|
||||
// transmitted by the TXOP holder, except for PS-Poll frames. (Sec.10.23.2.2 IEEE 802.11-2020)
|
||||
if (!psdu->GetHeader(0).IsPsPoll())
|
||||
{
|
||||
m_txNav = Max(m_txNav, Simulator::Now() + txDuration + psdu->GetDuration());
|
||||
}
|
||||
const auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
|
||||
SetTxNav(*psdu->begin(), txDuration);
|
||||
|
||||
m_phy->Send(psdu, txVector);
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ QosFrameExchangeManager::SendCfEndIfNeeded()
|
||||
&QosFrameExchangeManager::NotifyChannelReleased,
|
||||
this,
|
||||
m_edca);
|
||||
ResetTxNav();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -674,7 +675,7 @@ QosFrameExchangeManager::TransmissionFailed(bool forceCurrentCw)
|
||||
m_initialFrame = false;
|
||||
m_sentFrameTo.clear();
|
||||
// reset TXNAV because transmission failed
|
||||
m_txNav = Simulator::Now();
|
||||
ResetTxNav();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user