wifi: Support switching main PHY back while main PHY is still switching

This commit is contained in:
Stefano Avallone
2025-03-01 17:09:03 +01:00
parent 7048b99fc1
commit 2fe280db62
3 changed files with 137 additions and 57 deletions

View File

@@ -167,10 +167,7 @@ AdvancedEmlsrManager::DoDispose()
"PhyRxMacHeaderEnd",
MakeCallback(&AdvancedEmlsrManager::ReceivedMacHdr, this).Bind(phy));
}
if (!GetAuxPhyTxCapable())
{
GetStaMac()->GetDevice()->GetPhy(GetMainPhyId())->UnregisterListener(m_phyListener);
}
UnregisterListener();
m_phyListener.reset();
DefaultEmlsrManager::DoDispose();
}
@@ -212,10 +209,30 @@ AdvancedEmlsrManager::DoSetWifiMac(Ptr<StaWifiMac> mac)
"PhyRxMacHeaderEnd",
MakeCallback(&AdvancedEmlsrManager::ReceivedMacHdr, this).Bind(phy));
}
if (!GetAuxPhyTxCapable())
}
void
AdvancedEmlsrManager::RegisterListener(Ptr<WifiPhy> phy)
{
NS_LOG_FUNCTION(this << phy->GetPhyId());
NS_ASSERT_MSG(!m_auxPhyWithListener,
"PHY listener is still connected to PHY " << +m_auxPhyWithListener->GetPhyId());
phy->RegisterListener(m_phyListener);
m_auxPhyWithListener = phy;
}
void
AdvancedEmlsrManager::UnregisterListener()
{
if (!m_auxPhyWithListener)
{
mac->GetDevice()->GetPhy(GetMainPhyId())->RegisterListener(m_phyListener);
return; // do nothing
}
NS_LOG_FUNCTION(this << m_auxPhyWithListener->GetPhyId());
m_auxPhyWithListener->UnregisterListener(m_phyListener);
m_auxPhyWithListener = nullptr;
}
std::pair<bool, Time>
@@ -272,7 +289,7 @@ AdvancedEmlsrManager::ReceivedMacHdr(Ptr<WifiPhy> phy,
Time psduDuration)
{
auto linkId = GetStaMac()->GetLinkForPhy(phy);
if (!linkId.has_value())
if (!linkId.has_value() || !m_useNotifiedMacHdr)
{
return;
}
@@ -280,8 +297,8 @@ AdvancedEmlsrManager::ReceivedMacHdr(Ptr<WifiPhy> phy,
auto& ongoingTxopEnd = GetEhtFem(*linkId)->GetOngoingTxopEndEvent();
if (m_useNotifiedMacHdr && ongoingTxopEnd.IsPending() &&
macHdr.GetAddr1() != GetEhtFem(*linkId)->GetAddress() && !macHdr.GetAddr1().IsBroadcast() &&
if (ongoingTxopEnd.IsPending() && macHdr.GetAddr1() != GetEhtFem(*linkId)->GetAddress() &&
!macHdr.GetAddr1().IsBroadcast() &&
!(macHdr.IsCts() && macHdr.GetAddr1() == GetEhtFem(*linkId)->GetBssid() /* CTS-to-self */))
{
// the EMLSR client is no longer involved in the TXOP and switching to listening mode
@@ -306,13 +323,15 @@ AdvancedEmlsrManager::ReceivedMacHdr(Ptr<WifiPhy> phy,
const auto delay =
Simulator::GetDelayLeft(m_switchMainPhyBackEvent) + phy->GetChannelSwitchDelay();
if (WifiExpectedAccessReason reason;
m_switchMainPhyBackEvent.IsPending() && mainPhyInvolved &&
(reason = GetStaMac()->GetChannelAccessManager(*linkId)->GetExpectedAccessWithin(delay)) !=
WifiExpectedAccessReason::ACCESS_EXPECTED)
{
SwitchMainPhyBackDelayExpired(*linkId, reason);
}
Simulator::ScheduleNow([=, this]() {
if (WifiExpectedAccessReason reason;
m_switchMainPhyBackEvent.IsPending() && mainPhyInvolved &&
(reason = GetStaMac()->GetChannelAccessManager(*linkId)->GetExpectedAccessWithin(
delay)) != WifiExpectedAccessReason::ACCESS_EXPECTED)
{
SwitchMainPhyBackDelayExpired(*linkId, reason);
}
});
}
void
@@ -454,31 +473,32 @@ AdvancedEmlsrManager::CheckNavAndCcaLastPifs(Ptr<WifiPhy> phy, uint8_t linkId, P
auto txopNotStarted = [=, this]() {
// check when access may be granted to determine whether to switch the main PHY back
// to the preferred link (if aux PHYs do not switch link)
const auto mainPhy = GetStaMac()->GetDevice()->GetPhy(GetMainPhyId());
const auto delay =
Simulator::GetDelayLeft(m_switchMainPhyBackEvent) + phy->GetChannelSwitchDelay();
Simulator::GetDelayLeft(m_switchMainPhyBackEvent) + mainPhy->GetChannelSwitchDelay();
if (WifiExpectedAccessReason reason;
!m_switchAuxPhy &&
!m_switchAuxPhy && m_switchMainPhyBackEvent.IsPending() &&
(reason = GetStaMac()->GetChannelAccessManager(linkId)->GetExpectedAccessWithin(
delay)) != WifiExpectedAccessReason::ACCESS_EXPECTED)
{
NS_LOG_DEBUG("No AC is expected to get backoff soon, switch main PHY back");
if (auto mainPhy = GetStaMac()->GetDevice()->GetPhy(GetMainPhyId());
!mainPhy->IsStateSwitching())
{
SwitchMainPhyBackDelayExpired(linkId, reason);
}
return;
SwitchMainPhyBackDelayExpired(linkId, reason);
}
// medium busy, restart channel access
// restart channel access
edca->NotifyChannelReleased(linkId); // to set access to NOT_REQUESTED
edca->StartAccessAfterEvent(linkId,
Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
Txop::CHECK_MEDIUM_BUSY);
};
if (!isBusy && width > MHz_u{0})
if (!m_switchAuxPhy && !m_switchMainPhyBackEvent.IsPending())
{
NS_LOG_DEBUG("Main PHY switched back (or scheduled to switch back) before PIFS check");
txopNotStarted();
}
else if (!isBusy && width > MHz_u{0})
{
// medium idle, start TXOP
width = std::min(width, GetChannelForMainPhy(linkId).GetTotalWidth());
@@ -521,11 +541,7 @@ AdvancedEmlsrManager::SwitchMainPhyBackDelayExpired(
m_switchMainPhyBackEvent.Cancel();
if (m_switchAuxPhy)
{
return; // nothing to do
}
NS_ASSERT_MSG(!m_switchAuxPhy, "Don't expect this to be called when aux PHYs switch link");
Time extension{0};
// check if the timer must be restarted because a frame is being received on any link
@@ -576,11 +592,46 @@ AdvancedEmlsrManager::SwitchMainPhyBackDelayExpired(
return;
}
// no need to wait further, switch the main PHY back to the preferred link
// no need to wait further, switch the main PHY back to the preferred link and unregister
// the PHY listener from the aux PHY
const auto elapsed = Simulator::Now() - m_mainPhySwitchInfo.start;
const auto isSwitching = GetStaMac()->GetDevice()->GetPhy(GetMainPhyId())->IsStateSwitching();
SwitchMainPhyBackToPreferredLink(linkId,
EmlsrSwitchMainPhyBackTrace(elapsed, stopReason, isSwitching));
// if scheduled, invoke CheckNavAndCcaLastPifs(), which will just restart channel access
if (m_ccaLastPifs.IsPending())
{
m_ccaLastPifs.PeekEventImpl()->Invoke();
m_ccaLastPifs.Cancel();
}
UnregisterListener();
}
void
AdvancedEmlsrManager::SwitchMainPhyBackToPreferredLink(uint8_t linkId,
EmlsrMainPhySwitchTrace&& traceInfo)
{
if (!m_interruptSwitching)
{
DefaultEmlsrManager::SwitchMainPhyBackToPreferredLink(
linkId,
std::forward<EmlsrMainPhySwitchTrace>(traceInfo));
return;
}
NS_LOG_FUNCTION(this << linkId << traceInfo.GetName());
NS_ABORT_MSG_IF(m_switchAuxPhy, "This method can only be called when SwitchAuxPhy is false");
if (!m_auxPhyToReconnect)
{
return;
}
SwitchMainPhy(GetMainPhyId(),
false,
REQUEST_ACCESS,
std::forward<EmlsrMainPhySwitchTrace>(traceInfo));
}
void
@@ -599,8 +650,9 @@ AdvancedEmlsrManager::InterruptSwitchMainPhyBackTimerIfNeeded()
if (!linkId.has_value())
{
NS_LOG_DEBUG("Main PHY is not operating on any link");
return;
NS_ASSERT(m_mainPhySwitchInfo.disconnected);
linkId = m_mainPhySwitchInfo.to;
NS_LOG_DEBUG("Main PHY is switching to link " << +linkId.value());
}
const auto delay =
@@ -618,6 +670,7 @@ AdvancedEmlsrManager::DoNotifyIcfReceived(uint8_t linkId)
NS_LOG_FUNCTION(this << linkId);
m_switchMainPhyBackEvent.Cancel();
m_ccaLastPifs.Cancel();
UnregisterListener();
}
void
@@ -626,6 +679,7 @@ AdvancedEmlsrManager::DoNotifyUlTxopStart(uint8_t linkId)
NS_LOG_FUNCTION(this << linkId);
m_switchMainPhyBackEvent.Cancel();
m_ccaLastPifs.Cancel();
UnregisterListener();
}
bool
@@ -780,16 +834,21 @@ AdvancedEmlsrManager::SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex
DONT_REQUEST_ACCESS,
EmlsrUlTxopAuxPhyNotTxCapableTrace(aci, Time{0}, remNav));
// the main PHY must stay for some time on this link to check if it gets channel access.
// The timer is stopped if a DL or UL TXOP is started. When the timer expires, the main PHY
// switches back to the preferred link if SwitchAuxPhy is false
m_switchMainPhyBackEvent.Cancel();
m_switchMainPhyBackEvent =
Simulator::Schedule(mainPhy->GetChannelSwitchDelay() + m_switchMainPhyBackDelay,
&AdvancedEmlsrManager::SwitchMainPhyBackDelayExpired,
this,
linkId,
std::nullopt);
// if SwitchAuxPhy is false, the main PHY must stay for some time on this link to check if
// it gets channel access. The timer is stopped if a DL or UL TXOP is started. When the
// timer expires, the main PHY switches back to the preferred link
if (!m_switchAuxPhy)
{
m_switchMainPhyBackEvent.Cancel();
m_switchMainPhyBackEvent =
Simulator::Schedule(mainPhy->GetChannelSwitchDelay() + m_switchMainPhyBackDelay,
&AdvancedEmlsrManager::SwitchMainPhyBackDelayExpired,
this,
linkId,
std::nullopt);
// start checking PHY activity on the link the main PHY is switching to
RegisterListener(auxPhy);
}
return;
}
@@ -989,16 +1048,21 @@ AdvancedEmlsrManager::SwitchMainPhyIfTxopToBeGainedByAuxPhy(uint8_t linkId,
DONT_REQUEST_ACCESS,
EmlsrUlTxopAuxPhyNotTxCapableTrace(aci, delay, remNav));
// the main PHY must stay for some time on this link to check if it gets channel access. The
// timer is stopped if a DL or UL TXOP is started. When the timer expires, the main PHY switches
// back to the preferred link if SwitchAuxPhy is false
m_switchMainPhyBackEvent.Cancel();
m_switchMainPhyBackEvent =
Simulator::Schedule(switchDelay + m_switchMainPhyBackDelay,
&AdvancedEmlsrManager::SwitchMainPhyBackDelayExpired,
this,
linkId,
std::nullopt);
// if SwitchAuxPhy is false, the main PHY must stay for some time on this link to check if it
// gets channel access. The timer is stopped if a DL or UL TXOP is started. When the timer
// expires, the main PHY switches back to the preferred link
if (!m_switchAuxPhy)
{
m_switchMainPhyBackEvent.Cancel();
m_switchMainPhyBackEvent =
Simulator::Schedule(switchDelay + m_switchMainPhyBackDelay,
&AdvancedEmlsrManager::SwitchMainPhyBackDelayExpired,
this,
linkId,
std::nullopt);
// start checking PHY activity on the link the main PHY is switching to
RegisterListener(auxPhy);
}
}
} // namespace ns3

View File

@@ -54,6 +54,8 @@ class AdvancedEmlsrManager : public DefaultEmlsrManager
std::pair<bool, Time> GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId) override;
void SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex aci) override;
void NotifyEmlsrModeChanged() override;
void SwitchMainPhyBackToPreferredLink(uint8_t linkId,
EmlsrMainPhySwitchTrace&& traceInfo) override;
/**
* Possibly take actions when notified of the MAC header of the MPDU being received by the
@@ -120,6 +122,17 @@ class AdvancedEmlsrManager : public DefaultEmlsrManager
void SwitchMainPhyBackDelayExpired(uint8_t linkId,
std::optional<WifiExpectedAccessReason> stopReason);
/**
* Register a PHY listener so that this EMLSR Manager is notified of PHY events generated by
* the given PHY.
*
* @param phy the PHY which a listener is connected to
*/
void RegisterListener(Ptr<WifiPhy> phy);
/// Disconnect the PHY listener from the PHY it is connected to (if any)
void UnregisterListener();
private:
void DoNotifyTxopEnd(uint8_t linkId) override;
void DoNotifyIcfReceived(uint8_t linkId) override;
@@ -143,8 +156,10 @@ class AdvancedEmlsrManager : public DefaultEmlsrManager
//!< medium is sensed busy during the PIFS interval
//!< preceding/following the main PHY switch end
std::shared_ptr<WifiPhyListener>
m_phyListener; //!< PHY listener connected to the main PHY while operating on the link of
//!< an aux PHY that is not TX capable
m_phyListener; //!< PHY listener connected to an aux PHY (that is not TX capable and does
//!< not switch link) while the main PHY attempts to gain access on the aux
//!< PHY link
Ptr<WifiPhy> m_auxPhyWithListener; //!< aux PHY which a PHY listener is connected to
};
/**

View File

@@ -73,7 +73,8 @@ class DefaultEmlsrManager : public EmlsrManager
* @param traceInfo information to pass to the main PHY switch traced callback (the fromLinkId
* and toLinkId fields are set by SwitchMainPhy)
*/
void SwitchMainPhyBackToPreferredLink(uint8_t linkId, EmlsrMainPhySwitchTrace&& traceInfo);
virtual void SwitchMainPhyBackToPreferredLink(uint8_t linkId,
EmlsrMainPhySwitchTrace&& traceInfo);
bool m_switchAuxPhy; /**< whether Aux PHY should switch channel to operate on the link on which
the Main PHY was operating before moving to the link of the Aux PHY */