wifi: STA wifi MAC handles Power Management mode change

This commit is contained in:
Stefano Avallone
2023-03-03 16:20:40 +01:00
committed by Stefano Avallone
parent f3824350c9
commit 63f8e14284
3 changed files with 257 additions and 2 deletions

View File

@@ -27,6 +27,7 @@
#include "qos-txop.h"
#include "snr-tag.h"
#include "wifi-assoc-manager.h"
#include "wifi-mac-queue.h"
#include "wifi-net-device.h"
#include "wifi-phy.h"
@@ -36,6 +37,7 @@
#include "ns3/ht-configuration.h"
#include "ns3/log.h"
#include "ns3/packet.h"
#include "ns3/pair.h"
#include "ns3/pointer.h"
#include "ns3/random-variable-stream.h"
#include "ns3/simulator.h"
@@ -96,6 +98,23 @@ StaWifiMac::GetTypeId()
StringValue("ns3::UniformRandomVariable[Min=50.0|Max=250.0]"),
MakePointerAccessor(&StaWifiMac::m_probeDelay),
MakePointerChecker<RandomVariableStream>())
.AddAttribute(
"PowerSaveMode",
"Enable/disable power save mode on the given link. The power management mode is "
"actually changed when the AP acknowledges a frame sent with the Power Management "
"field set to the value corresponding to the requested mode",
TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time
PairValue<BooleanValue, UintegerValue>(),
MakePairAccessor<BooleanValue, UintegerValue>(&StaWifiMac::SetPowerSaveMode),
MakePairChecker<BooleanValue, UintegerValue>(MakeBooleanChecker(),
MakeUintegerChecker<uint8_t>()))
.AddAttribute("PmModeSwitchTimeout",
"If switching to a new Power Management mode is not completed within "
"this amount of time, make another attempt at switching Power "
"Management mode.",
TimeValue(Seconds(0.1)),
MakeTimeAccessor(&StaWifiMac::m_pmModeSwitchTimeout),
MakeTimeChecker())
.AddTraceSource("Assoc",
"Associated with an access point. If this is an MLD that associated "
"with an AP MLD, the AP MLD address is provided.",
@@ -144,6 +163,8 @@ StaWifiMac::DoInitialize()
{
NS_LOG_FUNCTION(this);
StartScanning();
NS_ABORT_IF(!TraceConnectWithoutContext("AckedMpdu", MakeCallback(&StaWifiMac::TxOk, this)));
WifiMac::DoInitialize();
}
void
@@ -1225,7 +1246,90 @@ StaWifiMac::ReceiveAssocResp(Ptr<const WifiMpdu> mpdu, uint8_t linkId)
NS_LOG_DEBUG("association refused");
SetState(REFUSED);
StartScanning();
return;
}
SetPmModeAfterAssociation(linkId);
}
void
StaWifiMac::SetPmModeAfterAssociation(uint8_t linkId)
{
NS_LOG_FUNCTION(this << linkId);
// STAs operating on setup links may need to transition to a new PM mode after the
// acknowledgement of the Association Response. For this purpose, we connect a callback to
// the PHY TX begin trace to catch the Ack transmitted after the Association Response.
CallbackBase cb = Callback<void, WifiConstPsduMap, WifiTxVector, double>(
[=](WifiConstPsduMap psduMap, WifiTxVector txVector, double /* txPowerW */) {
NS_ASSERT_MSG(psduMap.size() == 1 && psduMap.begin()->second->GetNMpdus() == 1 &&
psduMap.begin()->second->GetHeader(0).IsAck(),
"Expected a Normal Ack after Association Response frame");
auto ackDuration =
WifiPhy::CalculateTxDuration(psduMap, txVector, GetLink(linkId).phy->GetPhyBand());
for (uint8_t id = 0; id < GetNLinks(); id++)
{
auto& link = GetLink(id);
if (!link.bssid)
{
// link has not been setup
continue;
}
if (id == linkId)
{
/**
* When a link becomes enabled for a non-AP STA that is affiliated with a
* non-AP MLD after successful association with an AP MLD with (Re)Association
* Request/Response frames transmitted on that link [..], the power management
* mode of the non-AP STA, immediately after the acknowledgement of the
* (Re)Association Response frame [..], is active mode.
* (Sec. 35.3.7.1.4 of 802.11be D3.0)
*/
// if the user requested this link to be in powersave mode, we have to
// switch PM mode
if (link.pmMode == WIFI_PM_POWERSAVE)
{
Simulator::Schedule(ackDuration,
&StaWifiMac::SetPowerSaveMode,
this,
std::pair<bool, uint8_t>{true, id});
}
link.pmMode = WIFI_PM_ACTIVE;
}
else
{
/**
* When a link becomes enabled for a non-AP STA that is affiliated with a
* non-AP MLD after successful association with an AP MLD with (Re)Association
* Request/Response frames transmitted on another link [..], the power
* management mode of the non-AP STA, immediately after the acknowledgement of
* the (Re)Association Response frame [..], is power save mode, and its power
* state is doze. (Sec. 35.3.7.1.4 of 802.11be D3.0)
*/
// if the user requested this link to be in active mode, we have to
// switch PM mode
if (link.pmMode == WIFI_PM_ACTIVE)
{
Simulator::Schedule(ackDuration,
&StaWifiMac::SetPowerSaveMode,
this,
std::pair<bool, uint8_t>{false, id});
}
link.pmMode = WIFI_PM_POWERSAVE;
}
}
});
// connect the callback to the PHY TX begin trace to catch the Ack and disconnect
// after its transmission begins
auto phy = GetLink(linkId).phy;
phy->TraceConnectWithoutContext("PhyTxPsduBegin", cb);
Simulator::Schedule(phy->GetSifs() + NanoSeconds(1),
[=]() { phy->TraceDisconnectWithoutContext("PhyTxPsduBegin", cb); });
}
bool
@@ -1469,6 +1573,111 @@ StaWifiMac::UpdateApInfo(const MgtFrameType& frame,
std::visit(commonOps, frame);
}
void
StaWifiMac::SetPowerSaveMode(const std::pair<bool, uint8_t>& enableLinkIdPair)
{
const auto [enable, linkId] = enableLinkIdPair;
NS_LOG_FUNCTION(this << enable << linkId);
auto& link = GetLink(linkId);
if (!IsAssociated())
{
NS_LOG_DEBUG("Not associated yet, record the PM mode to switch to upon association");
link.pmMode = enable ? WIFI_PM_POWERSAVE : WIFI_PM_ACTIVE;
return;
}
if (!link.bssid)
{
NS_LOG_DEBUG("Link " << +linkId << " has not been setup, ignore request");
return;
}
if ((enable && link.pmMode == WIFI_PM_POWERSAVE) || (!enable && link.pmMode == WIFI_PM_ACTIVE))
{
NS_LOG_DEBUG("No PM mode change needed");
return;
}
link.pmMode = enable ? WIFI_PM_SWITCHING_TO_PS : WIFI_PM_SWITCHING_TO_ACTIVE;
// reschedule a call to this function to make sure that the PM mode switch
// is eventually completed
Simulator::Schedule(m_pmModeSwitchTimeout,
&StaWifiMac::SetPowerSaveMode,
this,
enableLinkIdPair);
if (HasFramesToTransmit(linkId))
{
NS_LOG_DEBUG("Next transmitted frame will be sent with PM=" << enable);
return;
}
// No queued frames. Enqueue a Data Null frame to inform the AP of the PM mode change
WifiMacHeader hdr(WIFI_MAC_DATA_NULL);
hdr.SetAddr1(GetBssid(linkId));
hdr.SetAddr2(GetFrameExchangeManager(linkId)->GetAddress());
hdr.SetAddr3(GetBssid(linkId));
hdr.SetDsNotFrom();
hdr.SetDsTo();
enable ? hdr.SetPowerManagement() : hdr.SetNoPowerManagement();
if (GetQosSupported())
{
GetQosTxop(AC_BE)->Queue(Create<WifiMpdu>(Create<Packet>(), hdr));
}
else
{
m_txop->Queue(Create<WifiMpdu>(Create<Packet>(), hdr));
}
}
WifiPowerManagementMode
StaWifiMac::GetPmMode(uint8_t linkId) const
{
return GetLink(linkId).pmMode;
}
void
StaWifiMac::TxOk(Ptr<const WifiMpdu> mpdu)
{
NS_LOG_FUNCTION(this << *mpdu);
auto linkId = GetLinkIdByAddress(mpdu->GetHeader().GetAddr2());
if (!linkId)
{
// the given MPDU may be the original copy containing MLD addresses and not carrying
// a valid PM bit (which is set on the aliases).
auto linkIds = mpdu->GetInFlightLinkIds();
NS_ASSERT_MSG(!linkIds.empty(),
"The TA of the acked MPDU (" << *mpdu
<< ") is not a link "
"address and the MPDU is not inflight");
// in case the ack'ed MPDU is inflight on multiple links, we cannot really know if
// it was received by the AP on all links or only on some links. Hence, we only
// consider the first link ID in the set, given that in the most common case of MPDUs
// that cannot be sent concurrently on multiple links, there will be only one link ID
linkId = *linkIds.begin();
mpdu = GetTxopQueue(mpdu->GetQueueAc())->GetAlias(mpdu, *linkId);
}
auto& link = GetLink(*linkId);
const WifiMacHeader& hdr = mpdu->GetHeader();
// we received an acknowledgment while switching PM mode; the PM mode change is effective now
if (hdr.IsPowerManagement() && link.pmMode == WIFI_PM_SWITCHING_TO_PS)
{
link.pmMode = WIFI_PM_POWERSAVE;
}
else if (!hdr.IsPowerManagement() && link.pmMode == WIFI_PM_SWITCHING_TO_ACTIVE)
{
link.pmMode = WIFI_PM_ACTIVE;
}
}
AllSupportedRates
StaWifiMac::GetSupportedRates(uint8_t linkId) const
{

View File

@@ -83,6 +83,19 @@ struct WifiScanParams
Time maxChannelTime; ///< maximum time to spend on each channel
};
/**
* \ingroup wifi
*
* Enumeration for power management modes
*/
enum WifiPowerManagementMode : uint8_t
{
WIFI_PM_ACTIVE = 0,
WIFI_PM_SWITCHING_TO_PS,
WIFI_PM_POWERSAVE,
WIFI_PM_SWITCHING_TO_ACTIVE
};
/**
* \ingroup wifi
*
@@ -246,6 +259,35 @@ class StaWifiMac : public WifiMac
*/
uint16_t GetAssociationId() const;
/**
* Enable or disable Power Save mode on the given link.
*
* \param enableLinkIdPair a pair indicating whether to enable or not power save mode on
* the link with the given ID
*/
void SetPowerSaveMode(const std::pair<bool, uint8_t>& enableLinkIdPair);
/**
* \param linkId the ID of the given link
* \return the current Power Management mode of the STA operating on the given link
*/
WifiPowerManagementMode GetPmMode(uint8_t linkId) const;
/**
* Set the Power Management mode of the setup links after association.
*
* \param linkId the ID of the link used to establish association
*/
void SetPmModeAfterAssociation(uint8_t linkId);
/**
* Notify that the MPDU we sent was successfully received by the receiver
* (i.e. we received an Ack from the receiver).
*
* \param mpdu the MPDU that we successfully sent
*/
void TxOk(Ptr<const WifiMpdu> mpdu);
void NotifyChannelSwitching(uint8_t linkId) override;
/**
@@ -277,6 +319,9 @@ class StaWifiMac : public WifiMac
std::optional<Mac48Address> bssid; //!< BSSID of the AP to associate with over this link
EventId beaconWatchdog; //!< beacon watchdog
Time beaconWatchdogEnd{0}; //!< beacon watchdog end
WifiPowerManagementMode pmMode{WIFI_PM_ACTIVE}; /**< the current PM mode, if the STA is
associated, or the PM mode to switch
to upon association, otherwise */
};
/**
@@ -520,6 +565,7 @@ class StaWifiMac : public WifiMac
bool m_activeProbing; ///< active probing
Ptr<RandomVariableStream> m_probeDelay; ///< RandomVariable used to randomize the time
///< of the first Probe Response on each channel
Time m_pmModeSwitchTimeout; ///< PM mode switch timeout
TracedCallback<Mac48Address> m_assocLogger; ///< association logger
TracedCallback<uint8_t, Mac48Address> m_setupCompleted; ///< link setup completed logger

View File

@@ -1923,8 +1923,8 @@ OfdmaAckSequenceTest::DoRun()
{
uint32_t previousSeed = RngSeedManager::GetSeed();
uint64_t previousRun = RngSeedManager::GetRun();
Config::SetGlobal("RngSeed", UintegerValue(1));
Config::SetGlobal("RngRun", UintegerValue(1));
Config::SetGlobal("RngSeed", UintegerValue(2));
Config::SetGlobal("RngRun", UintegerValue(2));
int64_t streamNumber = 10;
NodeContainer wifiApNode;