diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.cc b/src/wifi/model/eht/eht-frame-exchange-manager.cc index 53e094d70..f85e92ca3 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.cc +++ b/src/wifi/model/eht/eht-frame-exchange-manager.cc @@ -1334,6 +1334,7 @@ EhtFrameExchangeManager::DropReceivedIcf() // started before the reception of the ICF ended. We drop this ICF and let the // UL TXOP continue. NS_LOG_DEBUG("Drop ICF because another EMLSR link is being used"); + m_icfDropCallback(WifiIcfDrop::USING_OTHER_LINK, m_linkId); return true; } } @@ -1362,22 +1363,28 @@ EhtFrameExchangeManager::DropReceivedIcf() { const auto delay = mainPhy->GetChannelSwitchDelay(); auto lastTime = mainPhy->GetState()->GetLastTime({WifiPhyState::TX}); + auto reason = WifiIcfDrop::NOT_ENOUGH_TIME_TX; if (auto lastSwitch = mainPhy->GetState()->GetLastTime({WifiPhyState::SWITCHING}); lastSwitch > lastTime) { lastTime = lastSwitch; + reason = WifiIcfDrop::NOT_ENOUGH_TIME_SWITCH; } if (auto lastSleep = mainPhy->GetState()->GetLastTime({WifiPhyState::SLEEP}); lastSleep > lastTime) { lastTime = lastSleep; + reason = WifiIcfDrop::NOT_ENOUGH_TIME_SLEEP; } // ignore RX state for now if (lastTime > Simulator::Now() - delay) { - NS_LOG_DEBUG("Drop ICF due to not enough time for the main PHY to switch link"); + NS_LOG_DEBUG( + "Drop ICF due to not enough time for the main PHY to switch link; reason = " + << reason); + m_icfDropCallback(reason, m_linkId); return true; } } diff --git a/src/wifi/model/eht/eht-frame-exchange-manager.h b/src/wifi/model/eht/eht-frame-exchange-manager.h index 19fef4ec5..8f9f9885e 100644 --- a/src/wifi/model/eht/eht-frame-exchange-manager.h +++ b/src/wifi/model/eht/eht-frame-exchange-manager.h @@ -23,6 +23,47 @@ extern const Time EMLSR_RX_PHY_START_DELAY; class MgtEmlOmn; +/** + * \ingroup wifi + * Reasons for an EMLSR client to drop an ICF + */ +enum class WifiIcfDrop : uint8_t +{ + USING_OTHER_LINK = 0, // another EMLSR link is being used + NOT_ENOUGH_TIME_TX, // not enough time for the main PHY to switch (because in TX state) + NOT_ENOUGH_TIME_RX, // not enough time for the main PHY to switch (because in RX state) + NOT_ENOUGH_TIME_SWITCH, // not enough time for the main PHY to switch (already switching) + NOT_ENOUGH_TIME_SLEEP, // not enough time for the main PHY to switch (because in SLEEP state) +}; + +/** + * \brief Stream insertion operator. + * + * \param os the stream + * \param reason the reason for dropping the ICF + * \returns a reference to the stream + */ +inline std::ostream& +operator<<(std::ostream& os, WifiIcfDrop reason) +{ + switch (reason) + { + case WifiIcfDrop::USING_OTHER_LINK: + return (os << "USING_OTHER_LINK"); + case WifiIcfDrop::NOT_ENOUGH_TIME_TX: + return (os << "NOT_ENOUGH_TIME_TX"); + case WifiIcfDrop::NOT_ENOUGH_TIME_RX: + return (os << "NOT_ENOUGH_TIME_RX"); + case WifiIcfDrop::NOT_ENOUGH_TIME_SWITCH: + return (os << "NOT_ENOUGH_TIME_SWITCH"); + case WifiIcfDrop::NOT_ENOUGH_TIME_SLEEP: + return (os << "NOT_ENOUGH_TIME_SLEEP"); + default: + NS_FATAL_ERROR("Unknown wifi ICF drop reason"); + return (os << "UNKNOWN"); + } +} + /** * \ingroup wifi * @@ -120,6 +161,9 @@ class EhtFrameExchangeManager : public HeFrameExchangeManager */ EventId& GetOngoingTxopEndEvent(); + /// ICF drop reason traced callback (WifiMac exposes this trace source) + TracedCallback m_icfDropCallback; + protected: void DoDispose() override; void RxStartIndication(WifiTxVector txVector, Time psduDuration) override; diff --git a/src/wifi/model/wifi-mac.cc b/src/wifi/model/wifi-mac.cc index ac97cb473..e9311e6a0 100644 --- a/src/wifi/model/wifi-mac.cc +++ b/src/wifi/model/wifi-mac.cc @@ -359,7 +359,14 @@ WifiMac::GetTypeId() "a SU frame or aggregated to PSDUs in the DL MU PPDU), a Basic Trigger Frame or " "a BSRP Trigger Frame.", MakeTraceSourceAccessor(&WifiMac::m_psduMapResponseTimeoutCallback), - "ns3::WifiMac::PsduMapResponseTimeoutCallback"); + "ns3::WifiMac::PsduMapResponseTimeoutCallback") + .AddTraceSource("IcfDropReason", + "An ICF is dropped by an EMLSR client for the given reason on the " + "link with the given ID. This trace source is actually fed by the " + "EHT Frame Exchange Manager through the m_icfDropCallback member " + "variable.", + MakeTraceSourceAccessor(&WifiMac::m_icfDropCallback), + "ns3::WifiMac::IcfDropCallback"); return tid; } @@ -975,6 +982,11 @@ WifiMac::SetFrameExchangeManagers(const std::vector>& MakeCallback(&DroppedMpduTracedCallback::operator(), &m_droppedMpduCallback)); link->feManager->SetAckedMpduCallback( MakeCallback(&MpduTracedCallback::operator(), &m_ackedMpduCallback)); + if (auto ehtFem = DynamicCast(link->feManager)) + { + ehtFem->m_icfDropCallback.ConnectWithoutContext( + MakeCallback(&IcfDropTracedCallback::operator(), &m_icfDropCallback)); + } } CompleteConfig(); diff --git a/src/wifi/model/wifi-mac.h b/src/wifi/model/wifi-mac.h index 893bf349e..cbbcce512 100644 --- a/src/wifi/model/wifi-mac.h +++ b/src/wifi/model/wifi-mac.h @@ -47,6 +47,7 @@ class ExtendedCapabilities; class OriginatorBlockAckAgreement; class RecipientBlockAckAgreement; class UniformRandomVariable; +enum class WifiIcfDrop : uint8_t; // opaque enum declaration /** * \ingroup wifi @@ -1334,6 +1335,19 @@ class WifiMac : public Object * This trace source is fed by a WifiTxTimer object. */ PsduMapResponseTimeoutTracedCallback m_psduMapResponseTimeoutCallback; + + /** + * TracedCallback signature for ICF drop events. + * + * \param reason the reason why the ICF was dropped by the EMLSR client + * \param linkId the ID of the link on which the ICF was dropped + */ + typedef void (*IcfDropCallback)(WifiIcfDrop reason, uint8_t linkId); + + /// TracedCallback for ICF drop events typedef + using IcfDropTracedCallback = TracedCallback; + + IcfDropTracedCallback m_icfDropCallback; //!< traced callback for ICF drop events }; } // namespace ns3