wifi: Main PHY can switch link if aux PHY is not TX capable

This commit is contained in:
Stefano Avallone
2023-10-11 17:31:31 +02:00
committed by Stefano Avallone
parent b762cf2f3a
commit d30c67fd93
4 changed files with 183 additions and 25 deletions

View File

@@ -24,6 +24,7 @@
#include "ns3/boolean.h"
#include "ns3/channel-access-manager.h"
#include "ns3/log.h"
#include "ns3/qos-txop.h"
#include "ns3/wifi-mpdu.h"
#include "ns3/wifi-net-device.h"
#include "ns3/wifi-phy.h"
@@ -192,4 +193,109 @@ DefaultEmlsrManager::DoNotifyTxopEnd(uint8_t linkId)
}
}
bool
DefaultEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId)
{
NS_LOG_FUNCTION(this << linkId);
NS_ASSERT_MSG(!m_auxPhyTxCapable,
"This function should only be called if aux PHY is not TX capable");
// the aux PHY is not TX capable; check if main PHY has to switch to the aux PHY's link
auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
// if the main PHY is idle, check whether the remaining backoff counter on at least an AC with
// queued packets is greater than the main PHY channel switch delay
auto backoffGreaterThanSwitchDelay = false;
if (mainPhy->IsStateIdle())
{
auto mainPhyLinkId = GetStaMac()->GetLinkForPhy(mainPhy);
NS_ASSERT(mainPhyLinkId.has_value());
// update backoff on main PHY link for all ACs
GetStaMac()
->GetChannelAccessManager(*mainPhyLinkId)
->NeedBackoffUponAccess(GetStaMac()->GetQosTxop(AC_BE),
Txop::HAD_FRAMES_TO_TRANSMIT,
Txop::CHECK_MEDIUM_BUSY);
for (const auto& [aci, ac] : wifiAcList)
{
if (auto edca = GetStaMac()->GetQosTxop(aci); edca->HasFramesToTransmit(linkId))
{
auto backoffEnd =
GetStaMac()->GetChannelAccessManager(*mainPhyLinkId)->GetBackoffEndFor(edca);
NS_LOG_DEBUG("Backoff end for " << aci
<< " on primary link: " << backoffEnd.As(Time::US));
if (backoffEnd > Simulator::Now() + mainPhy->GetChannelSwitchDelay() +
GetStaMac()->GetWifiPhy(linkId)->GetPifs())
{
backoffGreaterThanSwitchDelay = true;
break;
}
}
}
}
if ((mainPhy->IsStateCcaBusy() && !mainPhy->IsReceivingPhyHeader()) ||
(mainPhy->IsStateIdle() && backoffGreaterThanSwitchDelay))
{
// switch main PHY
SwitchMainPhy(linkId, false, RESET_BACKOFF, REQUEST_ACCESS);
return true;
}
// Determine if and when we need to request channel access again for the aux PHY based on
// the main PHY state.
// Note that, if we have requested the main PHY to switch (above), the function has returned
// and the EHT FEM will start a TXOP if the medium is idle for a PIFS interval following
// the end of the main PHY channel switch.
// If the state is switching, but we have not requested the main PHY to switch, then we
// request channel access again for the aux PHY a PIFS after that the main PHY state is back
// to IDLE (to avoid stealing the main PHY from the non-primary link which the main PHY is
// switching to), and then we will determine if the main PHY has to switch link.
// If the state is CCA_BUSY, the medium is busy but the main PHY is not receiving a PPDU.
// In this case, we request channel access again for the aux PHY a PIFS after that the main
// PHY state is back to IDLE, and then we will determine if the main PHY has to switch link.
// If the state is TX or RX, it means that the main PHY is involved in a TXOP. In this
// case, do nothing because the channel access will be requested when unblocking links
// at the end of the TXOP.
// If the state is IDLE, then either no AC has traffic to send or the backoff on the link
// of the main PHY is shorter than the channel switch delay. In the former case, do
// nothing because channel access will be triggered when new packets arrive; in the latter
// case, do nothing because the main PHY will start a TXOP and at the end of such TXOP
// links will be unblocked and the channel access requested on all links
if (!mainPhy->IsStateSwitching() && !mainPhy->IsStateCcaBusy())
{
NS_LOG_DEBUG("Main PHY state is " << mainPhy->GetState()->GetState() << ". Do nothing");
return false;
}
auto delay = mainPhy->GetDelayUntilIdle();
NS_ASSERT(delay.IsStrictlyPositive());
delay += mainPhy->GetSifs() + mainPhy->GetSlot();
NS_LOG_DEBUG("Main PHY state is " << mainPhy->GetState()->GetState()
<< ". Schedule channel access request on link " << +linkId
<< " at time " << (Simulator::Now() + delay).As(Time::NS));
Simulator::Schedule(delay, [=, this]() {
for (const auto& [aci, ac] : wifiAcList)
{
auto edca = GetStaMac()->GetQosTxop(aci);
if (edca->GetAccessStatus(linkId) != Txop::REQUESTED &&
edca->HasFramesToTransmit(linkId))
{
NS_LOG_DEBUG("Request channel access on link " << +linkId << " for " << aci);
GetStaMac()->GetChannelAccessManager(linkId)->RequestAccess(edca);
}
}
});
return false;
}
} // namespace ns3

View File

@@ -44,6 +44,8 @@ class DefaultEmlsrManager : public EmlsrManager
DefaultEmlsrManager();
~DefaultEmlsrManager() override;
bool SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId) override;
protected:
uint8_t GetLinkToSendEmlOmn() override;
std::optional<uint8_t> ResendNotification(Ptr<const WifiMpdu> mpdu) override;

View File

@@ -285,27 +285,51 @@ EhtFrameExchangeManager::StartTransmission(Ptr<Txop> edca, ChannelWidthMhz allow
mainPhy != m_phy)
{
// an aux PHY is operating on this link
if (!emlsrManager->GetAuxPhyTxCapable())
{
NS_LOG_DEBUG("Aux PHY is not capable of transmitting a PPDU");
if (emlsrManager->SwitchMainPhyIfTxopGainedByAuxPhy(m_linkId))
{
NS_ASSERT_MSG(mainPhy->IsStateSwitching(),
"SwitchMainPhyIfTxopGainedByAuxPhy returned true but main PHY is "
"not switching");
const auto pifs = m_phy->GetSifs() + m_phy->GetSlot();
auto checkMediumLastPifs = [=, this]() {
// check if the medium has been idle for the last PIFS interval
auto width = m_staMac->GetChannelAccessManager(m_linkId)
->GetLargestIdlePrimaryChannel(pifs, Simulator::Now());
if (width == 0)
{
NS_LOG_DEBUG("Medium busy in the last PIFS after channel switch end");
edca->StartAccessAfterEvent(m_linkId,
Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
Txop::CHECK_MEDIUM_BUSY);
return;
}
// medium idle, start a TXOP
if (HeFrameExchangeManager::StartTransmission(edca, width))
{
// notify the EMLSR Manager of the UL TXOP start on an EMLSR link
emlsrManager->NotifyUlTxopStart(m_linkId, std::nullopt);
}
};
Simulator::Schedule(mainPhy->GetDelayUntilIdle() + pifs, checkMediumLastPifs);
}
NotifyChannelReleased(edca);
return false;
}
if (mainPhy->IsStateRx())
{
NS_LOG_DEBUG(
"Main PHY is receiving a PPDU (may be, e.g., an ICF or a Beacon); do not "
"transmit to avoid dropping that PPDU due to the main PHY switching to this "
"link to take over the TXOP");
// Note that we do not prevent a (main or aux) PHY from starting a TXOP when
// an(other) aux PHY is receiving a PPDU. The reason is that if the aux PHY is
// receiving a Beacon frame, the aux PHY will not be affected by the start of
// a TXOP; if the aux PHY is receiving an ICF, the ICF will be dropped by
// ReceiveMpdu because another EMLSR link is being used.
NotifyChannelReleased(edca);
return false;
}
// Note that we do not prevent a (main or aux) PHY from starting a TXOP when
// an(other) aux PHY is receiving a PPDU. The reason is that if the aux PHY is
// receiving a Beacon frame, the aux PHY will not be affected by the start of
// a TXOP; if the aux PHY is receiving an ICF, the ICF will be dropped by
// ReceiveMpdu because another EMLSR link is being used.
const auto rtsTxVector =
GetWifiRemoteStationManager()->GetRtsTxVector(m_bssid, allowedWidth);
@@ -323,21 +347,37 @@ EhtFrameExchangeManager::StartTransmission(Ptr<Txop> edca, ChannelWidthMhz allow
auto switchingTime = mainPhy->GetChannelSwitchDelay();
if (mainPhy->IsStateSwitching())
switch (mainPhy->GetState()->GetState())
{
// the main PHY is switching (to another link), hence the remaining time to the
// end of the current channel switch needs to be added up
case WifiPhyState::RX:
case WifiPhyState::SWITCHING:
// the main PHY is receiving or switching (to another link), hence the remaining
// time to the end of the current reception/channel switch needs to be added up
switchingTime += mainPhy->GetDelayUntilIdle();
}
if (switchingTime > timeToCtsEnd)
{
// switching takes longer than RTS/CTS exchange, do not transmit anything to
// avoid that the main PHY is requested to switch while already switching
NS_LOG_DEBUG("Main PHY will still be switching channel when RTS/CTS ends, thus it "
"will not be able to take over this TXOP");
[[fallthrough]];
case WifiPhyState::IDLE:
case WifiPhyState::CCA_BUSY:
if (!mainPhy->IsReceivingPhyHeader() && switchingTime <= timeToCtsEnd)
{
break; // start TXOP
}
// release channel
if (mainPhy->IsReceivingPhyHeader())
{
NS_LOG_DEBUG(
"Aux PHY cannot start TXOP because main PHY is receiving a PHY header");
}
else
{
// switching takes longer than RTS/CTS exchange, do not transmit anything to
// avoid that the main PHY is requested to switch while already switching
NS_LOG_DEBUG("Not enough time for main PHY to switch link (main PHY state: "
<< mainPhy->GetState() << ")");
}
NotifyChannelReleased(edca);
return false;
default:
NS_ABORT_MSG("Main PHY cannot be in state " << mainPhy->GetState());
}
}
}

View File

@@ -194,6 +194,16 @@ class EmlsrManager : public Object
*/
void NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted = false, bool ongoingDlTxop = false);
/**
* This method is intended to notify the EMLSR Manager that an aux PHY that is NOT TX capable
* has gained a TXOP on a given link and returns whether the main PHY has been requested to
* switch to the given link to take over the TXOP.
*
* \param linkId the ID of the given link
* \return whether main PHY has been requested to switch
*/
virtual bool SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId) = 0;
/**
* Check whether the MediumSyncDelay timer is running for the STA operating on the given link.
* If so, returns the time elapsed since the timer started.