wifi: Main PHY can switch link if aux PHY is not TX capable
This commit is contained in:
committed by
Stefano Avallone
parent
b762cf2f3a
commit
d30c67fd93
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user