diff --git a/src/wifi/model/ap-wifi-mac.cc b/src/wifi/model/ap-wifi-mac.cc index 5914f8d56..d4f141410 100644 --- a/src/wifi/model/ap-wifi-mac.cc +++ b/src/wifi/model/ap-wifi-mac.cc @@ -1498,6 +1498,56 @@ ApWifiMac::TxFailed(WifiMacDropReason timeoutReason, Ptr mpdu) } } +void +ApWifiMac::ProcessPowerManagementFlag(Ptr mpdu, uint8_t linkId) +{ + NS_LOG_FUNCTION(this << *mpdu << linkId); + + Mac48Address staAddr = mpdu->GetHeader().GetAddr2(); + bool staInPsMode = GetWifiRemoteStationManager(linkId)->IsInPsMode(staAddr); + + if (!staInPsMode && mpdu->GetHeader().IsPowerManagement()) + { + // the sending STA is switching to Power Save mode + StaSwitchingToPsMode(staAddr, linkId); + } + else if (staInPsMode && !mpdu->GetHeader().IsPowerManagement()) + { + // the sending STA is switching back to Active mode + StaSwitchingToActiveModeOrDeassociated(staAddr, linkId); + } +} + +void +ApWifiMac::StaSwitchingToPsMode(const Mac48Address& staAddr, uint8_t linkId) +{ + NS_LOG_FUNCTION(this << staAddr << linkId); + + GetWifiRemoteStationManager(linkId)->SetPsMode(staAddr, true); + + // Block frames addressed to the STA in PS mode + NS_LOG_DEBUG("Block destination " << staAddr << " on link " << +linkId); + auto staMldAddr = GetWifiRemoteStationManager(linkId)->GetMldAddress(staAddr).value_or(staAddr); + BlockUnicastTxOnLinks(WifiQueueBlockedReason::POWER_SAVE_MODE, staMldAddr, {linkId}); +} + +void +ApWifiMac::StaSwitchingToActiveModeOrDeassociated(const Mac48Address& staAddr, uint8_t linkId) +{ + NS_LOG_FUNCTION(this << staAddr << linkId); + + GetWifiRemoteStationManager(linkId)->SetPsMode(staAddr, false); + + if (GetWifiRemoteStationManager(linkId)->IsAssociated(staAddr)) + { + // the station is still associated, unblock its frames + NS_LOG_DEBUG("Unblock destination " << staAddr << " on link " << +linkId); + auto staMldAddr = + GetWifiRemoteStationManager(linkId)->GetMldAddress(staAddr).value_or(staAddr); + UnblockUnicastTxOnLinks(WifiQueueBlockedReason::POWER_SAVE_MODE, staMldAddr, {linkId}); + } +} + std::optional ApWifiMac::IsAssociated(const Mac48Address& address) const { @@ -1546,6 +1596,10 @@ ApWifiMac::Receive(Ptr mpdu, uint8_t linkId) (apLinkId = IsAssociated(mpdu->GetHeader().GetAddr2())) && mpdu->GetHeader().GetAddr1() == GetFrameExchangeManager(*apLinkId)->GetAddress()) { + // this MPDU is being acknowledged by the AP, so we can process + // the Power Management flag + ProcessPowerManagementFlag(mpdu, *apLinkId); + Mac48Address to = hdr->GetAddr3(); // Address3 can be our MLD address (e.g., this is an MPDU containing a single MSDU // addressed to us) or a BSSID (e.g., this is an MPDU containing an A-MSDU) @@ -1611,6 +1665,13 @@ ApWifiMac::Receive(Ptr mpdu, uint8_t linkId) } else if (hdr->IsMgt()) { + if (hdr->GetAddr1() == GetFrameExchangeManager(linkId)->GetAddress() && + GetWifiRemoteStationManager(linkId)->IsAssociated(from)) + { + // this MPDU is being acknowledged by the AP, so we can process + // the Power Management flag + ProcessPowerManagementFlag(mpdu, linkId); + } if (hdr->IsProbeReq() && (hdr->GetAddr1().IsGroup() || hdr->GetAddr1() == GetFrameExchangeManager(linkId)->GetAddress())) { @@ -1685,6 +1746,7 @@ ApWifiMac::Receive(Ptr mpdu, uint8_t linkId) } UpdateShortSlotTimeEnabled(linkId); UpdateShortPreambleEnabled(linkId); + StaSwitchingToActiveModeOrDeassociated(from, linkId); break; } } diff --git a/src/wifi/model/ap-wifi-mac.h b/src/wifi/model/ap-wifi-mac.h index d716b13f9..4dde49756 100644 --- a/src/wifi/model/ap-wifi-mac.h +++ b/src/wifi/model/ap-wifi-mac.h @@ -357,6 +357,31 @@ class ApWifiMac : public WifiMac */ void SendOneBeacon(uint8_t linkId); + /** + * Process the Power Management bit in the Frame Control field of an MPDU + * successfully received on the given link. + * + * \param mpdu the successfully received MPDU + * \param linkId the ID of the given link + */ + void ProcessPowerManagementFlag(Ptr mpdu, uint8_t linkId); + /** + * Perform the necessary actions when a given station switches from active mode + * to powersave mode. + * + * \param staAddr the MAC address of the given station + * \param linkId the ID of the link on which the given station is operating + */ + void StaSwitchingToPsMode(const Mac48Address& staAddr, uint8_t linkId); + /** + * Perform the necessary actions when a given station deassociates or switches + * from powersave mode to active mode. + * + * \param staAddr the MAC address of the given station + * \param linkId the ID of the link on which the given station is operating + */ + void StaSwitchingToActiveModeOrDeassociated(const Mac48Address& staAddr, uint8_t linkId); + /** * Return the Capability information of the current AP for the given link. * diff --git a/src/wifi/model/wifi-mac-queue-scheduler.h b/src/wifi/model/wifi-mac-queue-scheduler.h index 755d37562..609a8e0a4 100644 --- a/src/wifi/model/wifi-mac-queue-scheduler.h +++ b/src/wifi/model/wifi-mac-queue-scheduler.h @@ -42,6 +42,7 @@ class WifiMac; enum class WifiQueueBlockedReason : uint8_t { WAITING_ADDBA_RESP = 0, + POWER_SAVE_MODE, REASONS_COUNT }; @@ -59,6 +60,8 @@ operator<<(std::ostream& os, WifiQueueBlockedReason reason) { case WifiQueueBlockedReason::WAITING_ADDBA_RESP: return (os << "WAITING_ADDBA_RESP"); + case WifiQueueBlockedReason::POWER_SAVE_MODE: + return (os << "POWER_SAVE_MODE"); case WifiQueueBlockedReason::REASONS_COUNT: return (os << "REASONS_COUNT"); default: