From b953cae778bfdecccbb7d88aa63f7e3b42388e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deronne?= Date: Sun, 17 Nov 2024 11:11:02 +0100 Subject: [PATCH] wifi: Add Special User Info field for trigger frames soliciting EHT TB PPDUs --- RELEASE_NOTES.md | 1 + examples/wireless/examples-to-run.py | 2 +- src/wifi/model/ctrl-headers.cc | 218 ++++++++++++++++++++++++++- src/wifi/model/ctrl-headers.h | 105 +++++++++++++ 4 files changed, 323 insertions(+), 3 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 8443dde25..f544fb7ae 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -97,6 +97,7 @@ The minimum clang-format version is version 15. - (wifi) 2009! - Added WifiTxStatsHelper for Wi-Fi MAC-level tracing. - (wifi) - Added support for 802.11aa groupcast with retries (GCR). Both unsolicited retries (GCR-UR) and Block Ack (GCR-BA) mechanisms are implemented. - (zigbee) Added a new Zigbee module. +- (wifi) Trigger frames have been extended to support Special User Info field ### Bugs fixed diff --git a/examples/wireless/examples-to-run.py b/examples/wireless/examples-to-run.py index 561cb077f..8c8d9dfd1 100644 --- a/examples/wireless/examples-to-run.py +++ b/examples/wireless/examples-to-run.py @@ -195,7 +195,7 @@ cpp_examples = [ "True", ), ( - "wifi-eht-network --simulationTime=0.25s --udp=0 --downlink=0 --useRts=0 --nStations=4 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mpduBufferSize=1024 --mcs=8 --muSchedAccessReqInterval=45ms --frequency2=6 --minExpectedThroughput=50 --maxExpectedThroughput=550", + "wifi-eht-network --simulationTime=0.25s --udp=0 --downlink=0 --useRts=0 --nStations=4 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mpduBufferSize=1024 --mcs=8 --muSchedAccessReqInterval=45ms --frequency2=6 --minExpectedThroughput=50 --maxExpectedThroughput=550 --RngRun=3", "True", "True", ), diff --git a/src/wifi/model/ctrl-headers.cc b/src/wifi/model/ctrl-headers.cc index 8d8007acf..e9ff6931e 100644 --- a/src/wifi/model/ctrl-headers.cc +++ b/src/wifi/model/ctrl-headers.cc @@ -1391,6 +1391,9 @@ CtrlTriggerUserInfoField::GetPreambleType() const void CtrlTriggerUserInfoField::SetAid12(uint16_t aid) { + NS_ASSERT_MSG((m_variant == TriggerFrameVariant::HE) || (aid != AID_SPECIAL_USER), + std::to_string(AID_SPECIAL_USER) + << " is reserved for Special User Info Field in EHT variant"); m_aid12 = aid & 0x0fff; } @@ -1730,6 +1733,176 @@ CtrlTriggerUserInfoField::GetMuBarTriggerDepUserInfo() const return m_muBarTriggerDependentUserInfo; } +/***************************************** + * Trigger frame - Special User Info field + *****************************************/ + +CtrlTriggerSpecialUserInfoField::CtrlTriggerSpecialUserInfoField(TriggerFrameType triggerType) + : m_triggerType(triggerType) +{ +} + +CtrlTriggerSpecialUserInfoField& +CtrlTriggerSpecialUserInfoField::operator=(const CtrlTriggerSpecialUserInfoField& other) +{ + // check for self-assignment + if (&other == this) + { + return *this; + } + + m_triggerType = other.m_triggerType; + m_ulBwExt = other.m_ulBwExt; + m_muBarTriggerDependentUserInfo = other.m_muBarTriggerDependentUserInfo; + + return *this; +} + +uint32_t +CtrlTriggerSpecialUserInfoField::GetSerializedSize() const +{ + uint32_t size = 0; + size += 5; // User Info (excluding Trigger Dependent User Info) + + switch (m_triggerType) + { + case TriggerFrameType::BASIC_TRIGGER: + case TriggerFrameType::BFRP_TRIGGER: + size += 1; + break; + case TriggerFrameType::MU_BAR_TRIGGER: + size += + m_muBarTriggerDependentUserInfo.GetSerializedSize(); // BAR Control and BAR Information + break; + default:; + // The Trigger Dependent User Info subfield is not present in the other variants + } + + return size; +} + +Buffer::Iterator +CtrlTriggerSpecialUserInfoField::Serialize(Buffer::Iterator start) const +{ + NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::BFRP_TRIGGER, + "BFRP Trigger frame is not supported"); + NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER, + "GCR-MU-BAR Trigger frame is not supported"); + NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER, + "NFRP Trigger frame is not supported"); + + Buffer::Iterator i = start; + + uint32_t userInfo = 0; + userInfo |= (AID_SPECIAL_USER & 0x0fff); + userInfo |= (static_cast(m_ulBwExt) << 15); + i.WriteHtolsbU32(userInfo); + i.WriteU8(0); + // TODO: EHT Spatial Reuse and U-SIG Disregard And Validate + + if (m_triggerType == TriggerFrameType::BASIC_TRIGGER) + { + // The length is one octet and all the subfields are reserved in a Basic Trigger frame and + // in a BFRP Trigger frame + i.WriteU8(0); + } + else if (m_triggerType == TriggerFrameType::MU_BAR_TRIGGER) + { + m_muBarTriggerDependentUserInfo.Serialize(i); + i.Next(m_muBarTriggerDependentUserInfo.GetSerializedSize()); + } + + return i; +} + +Buffer::Iterator +CtrlTriggerSpecialUserInfoField::Deserialize(Buffer::Iterator start) +{ + NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::BFRP_TRIGGER, + "BFRP Trigger frame is not supported"); + NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::GCR_MU_BAR_TRIGGER, + "GCR-MU-BAR Trigger frame is not supported"); + NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER, + "NFRP Trigger frame is not supported"); + + Buffer::Iterator i = start; + + const auto userInfo = i.ReadLsbtohU32(); + i.ReadU8(); + // TODO: EHT Spatial Reuse and U-SIG Disregard And Validate + + const uint16_t aid12 = userInfo & 0x0fff; + NS_ABORT_MSG_IF(aid12 != AID_SPECIAL_USER, "Failed to deserialize Special User Info field"); + m_ulBwExt = (userInfo >> 15) & 0x03; + + if (m_triggerType == TriggerFrameType::BASIC_TRIGGER) + { + i.ReadU8(); + } + else if (m_triggerType == TriggerFrameType::MU_BAR_TRIGGER) + { + const auto len = m_muBarTriggerDependentUserInfo.Deserialize(i); + i.Next(len); + } + + return i; +} + +TriggerFrameType +CtrlTriggerSpecialUserInfoField::GetType() const +{ + return m_triggerType; +} + +void +CtrlTriggerSpecialUserInfoField::SetUlBwExt(MHz_u bw) +{ + switch (static_cast(bw)) + { + case 20: + case 40: + case 80: + m_ulBwExt = 0; + break; + case 160: + m_ulBwExt = 1; + break; + case 320: + m_ulBwExt = 2; + // TODO: differentiate channelization 1 from channelization 2 + break; + default: + NS_FATAL_ERROR("Bandwidth value not allowed."); + break; + } +} + +uint8_t +CtrlTriggerSpecialUserInfoField::GetUlBwExt() const +{ + return m_ulBwExt; +} + +void +CtrlTriggerSpecialUserInfoField::SetMuBarTriggerDepUserInfo(const CtrlBAckRequestHeader& bar) +{ + NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::MU_BAR_TRIGGER, + "Not a MU-BAR Trigger frame"); + NS_ABORT_MSG_IF(bar.GetType().m_variant != BlockAckReqType::COMPRESSED && + bar.GetType().m_variant != BlockAckReqType::MULTI_TID, + "BAR Control indicates it is neither the Compressed nor the Multi-TID variant"); + m_muBarTriggerDependentUserInfo = bar; +} + +const CtrlBAckRequestHeader& +CtrlTriggerSpecialUserInfoField::GetMuBarTriggerDepUserInfo() const +{ + NS_ABORT_MSG_IF(m_triggerType != TriggerFrameType::MU_BAR_TRIGGER, + "Not a MU-BAR Trigger frame"); + + return m_muBarTriggerDependentUserInfo; +} + /*********************************** * Trigger frame ***********************************/ @@ -1753,6 +1926,7 @@ CtrlTriggerHeader::CtrlTriggerHeader() CtrlTriggerHeader::CtrlTriggerHeader(TriggerFrameType type, const WifiTxVector& txVector) : CtrlTriggerHeader() { + m_triggerType = type; NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::MU_RTS_TRIGGER, "This constructor cannot be used for MU-RTS"); @@ -1769,7 +1943,13 @@ CtrlTriggerHeader::CtrlTriggerHeader(TriggerFrameType type, const WifiTxVector& << txVector.GetPreambleType()); } - m_triggerType = type; + // special user is always present if solicited TB PPDU format is EHT or later + if (txVector.GetModulationClass() >= WifiModulationClass::WIFI_MOD_CLASS_EHT) + { + NS_ASSERT(m_variant == TriggerFrameVariant::EHT); + m_specialUserInfoField.emplace(m_triggerType); + } + SetUlBandwidth(txVector.GetChannelWidth()); SetUlLength(txVector.GetLength()); const auto gi = txVector.GetGuardInterval().GetNanoSeconds(); @@ -1781,6 +1961,7 @@ CtrlTriggerHeader::CtrlTriggerHeader(TriggerFrameType type, const WifiTxVector& { m_giAndLtfType = 2; } + for (auto& userInfo : txVector.GetHeMuUserInfoMap()) { CtrlTriggerUserInfoField& ui = AddUserInfoField(); @@ -1814,6 +1995,8 @@ CtrlTriggerHeader::operator=(const CtrlTriggerHeader& trigger) m_apTxPower = trigger.m_apTxPower; m_ulSpatialReuse = trigger.m_ulSpatialReuse; m_padding = trigger.m_padding; + m_specialUserInfoField.reset(); + m_specialUserInfoField = trigger.m_specialUserInfoField; m_userInfoFields.clear(); m_userInfoFields = trigger.m_userInfoFields; return *this; @@ -1853,6 +2036,11 @@ CtrlTriggerHeader::SetVariant(TriggerFrameVariant variant) NS_ABORT_MSG_IF(!m_userInfoFields.empty(), "Cannot change Common Info field variant if User Info fields are present"); m_variant = variant; + // special user is always present if User Info field variant is EHT or later + if (!m_specialUserInfoField && (m_variant >= TriggerFrameVariant::EHT)) + { + m_specialUserInfoField.emplace(m_triggerType); + } } TriggerFrameVariant @@ -1873,6 +2061,12 @@ CtrlTriggerHeader::GetSerializedSize() const size += 4; } + if (m_specialUserInfoField) + { + NS_ASSERT(m_variant == TriggerFrameVariant::EHT); + size += m_specialUserInfoField->GetSerializedSize(); + } + for (auto& ui : m_userInfoFields) { size += ui.GetSerializedSize(); @@ -1912,6 +2106,12 @@ CtrlTriggerHeader::Serialize(Buffer::Iterator start) const i.WriteHtolsbU64(commonInfo); + if (m_specialUserInfoField) + { + NS_ASSERT(m_variant == TriggerFrameVariant::EHT); + i = m_specialUserInfoField->Serialize(i); + } + for (auto& ui : m_userInfoFields) { i = ui.Serialize(i); @@ -1950,6 +2150,18 @@ CtrlTriggerHeader::Deserialize(Buffer::Iterator start) NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER, "NFRP Trigger frame is not supported"); + if (m_variant == TriggerFrameVariant::EHT) + { + m_specialUserInfoField.reset(); + const auto userInfo = i.ReadLsbtohU16(); + i.Prev(2); + if (const auto aid12 = userInfo & 0x0fff; aid12 == AID_SPECIAL_USER) + { + m_specialUserInfoField.emplace(m_triggerType); + i = m_specialUserInfoField->Deserialize(i); + } + } + while (i.GetRemainingSize() >= 2) { // read the first 2 bytes to check if we encountered the Padding field @@ -2246,8 +2458,10 @@ CtrlTriggerHeader::GetPaddingSize() const CtrlTriggerHeader CtrlTriggerHeader::GetCommonInfoField() const { - // make a copy of this Trigger Frame and remove the User Info fields from the copy + // make a copy of this Trigger Frame and remove the User Info fields (including the Special User + // Info field) from the copy CtrlTriggerHeader trigger(*this); + trigger.m_specialUserInfoField.reset(); trigger.m_userInfoFields.clear(); return trigger; } diff --git a/src/wifi/model/ctrl-headers.h b/src/wifi/model/ctrl-headers.h index 9caa6d9ab..9a4628342 100644 --- a/src/wifi/model/ctrl-headers.h +++ b/src/wifi/model/ctrl-headers.h @@ -17,6 +17,7 @@ #include "ns3/mac48-address.h" #include +#include #include namespace ns3 @@ -910,6 +911,107 @@ class CtrlTriggerUserInfoField m_muBarTriggerDependentUserInfo; //!< MU-BAR variant of Trigger Dependent User Info subfield }; +/** + * @ingroup wifi + * @brief Special User Info field of Trigger frames. + * + * Trigger frames have been extended by 802.11be amendment and may include one Special User Info + * field for a solicited EHT TB PPDU (see Section 9.3.1.22.3 of D7.0). The Special User Info field + * is located immediately after the Common Info field, it does not carry user specific information + * but carries extended common information not provided in the Common Info field. + */ +class CtrlTriggerSpecialUserInfoField +{ + public: + /** + * Constructor + * + * @param triggerType the Trigger frame type + */ + explicit CtrlTriggerSpecialUserInfoField(TriggerFrameType triggerType); + + /** + * Copy assignment operator. + * + * @param other the Special User Info field to copy + * @return a reference to the copied object + * + * Checks that the given Special User Info fields is included in the same type of Trigger Frame. + */ + CtrlTriggerSpecialUserInfoField& operator=(const CtrlTriggerSpecialUserInfoField& other); + + /** + * Get the expected size of this Special User Info field + * + * @return the expected size of this Special User Info field. + */ + uint32_t GetSerializedSize() const; + + /** + * Serialize the Special User Info field to the given buffer. + * + * @param start an iterator which points to where the header should + * be written + * @return Buffer::Iterator to the next available buffer + */ + Buffer::Iterator Serialize(Buffer::Iterator start) const; + + /** + * Deserialize the Special User Info field from the given buffer. + * + * @param start an iterator which points to where the header should + * read from + * @return Buffer::Iterator to the next available buffer + */ + Buffer::Iterator Deserialize(Buffer::Iterator start); + + /** + * Get the type of the Trigger Frame this Special User Info field belongs to. + * + * @return the type of the Trigger Frame this Special User Info field belongs to + */ + TriggerFrameType GetType() const; + + /** + * Set the UL Bandwidth Extension subfield value of the solicited EHT TB PPDU. + * + * @param bw bandwidth (allowed values: 20 MHz, 40 MHz, 80 MHz, 160 MHz, 320 MHz) + */ + void SetUlBwExt(MHz_u bw); + + /** + * Get the UL Bandwidth Extension subfield value of the solicited EHT TB PPDU. + * + * @return the UL Bandwidth Extension subfield value + */ + uint8_t GetUlBwExt() const; + + /** + * Set the Trigger Dependent Special User Info subfield for the MU-BAR variant of Trigger + * frames, which includes the BAR Type subfield. The BAR Type subfield must indicate a + * Compressed BAR in an MU BAR Trigger frame. + * + * @param bar the BlockAckRequest header object including the BAR Control + * subfield and the BAR Information subfield + */ + void SetMuBarTriggerDepUserInfo(const CtrlBAckRequestHeader& bar); + + /** + * Get the Trigger Dependent Special User Info subfield for the MU-BAR variant of + * Trigger frames, which includes a BAR Type subfield. + * + * @return the BlockAckRequest header object including the BAR Control + * subfield and the BAR Information subfield + */ + const CtrlBAckRequestHeader& GetMuBarTriggerDepUserInfo() const; + + private: + TriggerFrameType m_triggerType{}; //!< Trigger type + uint8_t m_ulBwExt{}; //!< UL Bandwidth Extension + CtrlBAckRequestHeader m_muBarTriggerDependentUserInfo{}; //!< MU-BAR variant of Trigger + //!< Dependent User Info subfield +}; + /** * @ingroup wifi * @brief Headers for Trigger frames. @@ -1339,6 +1441,9 @@ class CtrlTriggerHeader : public Header uint16_t m_ulSpatialReuse; //!< Value for the Spatial Reuse field in HE-SIG-A std::size_t m_padding; //!< the size in bytes of the Padding field + std::optional + m_specialUserInfoField; //!< Special User Info field (EHT variant only) + /** * List of User Info fields */