wifi: Update TID-to-Link Mapping IE to 802.11be D3.1

This commit is contained in:
Stefano Avallone
2023-05-12 09:37:45 +02:00
parent 2c27007386
commit 338d625f55
3 changed files with 201 additions and 50 deletions

View File

@@ -17,20 +17,25 @@
* Author: Sharan Naribole <sharan.naribole@gmail.com>
*/
#include <ns3/assert.h>
#include <ns3/tid-to-link-mapping-element.h>
#include "tid-to-link-mapping-element.h"
#include "ns3/assert.h"
#include "ns3/simulator.h"
namespace ns3
{
/// Bitmask with all bits from 63 to 26 set to 1, all the others set to 0
static constexpr uint64_t BIT_63_TO_26_MASK = 0xfffffffffc000000;
uint16_t
TidToLinkMapping::Control::GetSubfieldSize() const
{
// IEEE 802.11be D2.0 Figure 9-1002an
NS_ASSERT_MSG(defaultMapping != presenceBitmap.has_value(),
// IEEE 802.11be D3.1 Figure 9-1002ap
NS_ASSERT_MSG(!defaultMapping || !presenceBitmap.has_value(),
"Presence bitmap not expected if default mapping is set");
auto size = WIFI_TID_TO_LINK_MAPPING_CONTROL_BASIC_SIZE_B;
if (defaultMapping)
if (!presenceBitmap.has_value())
{
return size;
}
@@ -40,10 +45,12 @@ TidToLinkMapping::Control::GetSubfieldSize() const
void
TidToLinkMapping::Control::Serialize(Buffer::Iterator& start) const
{
uint8_t val = static_cast<uint8_t>(direction);
val |= ((defaultMapping ? 1 : 0) << 2);
uint8_t val = static_cast<uint8_t>(direction) | ((defaultMapping ? 1 : 0) << 2) |
((mappingSwitchTimePresent ? 1 : 0) << 3) |
((expectedDurationPresent ? 1 : 0) << 4) | ((linkMappingSize == 1 ? 1 : 0) << 5);
start.WriteU8(val);
NS_ASSERT_MSG(defaultMapping != presenceBitmap.has_value(),
NS_ASSERT_MSG(!defaultMapping || !presenceBitmap.has_value(),
"Presence bitmap not expected if default mapping is set");
if (presenceBitmap.has_value())
{
@@ -61,8 +68,12 @@ TidToLinkMapping::Control::Deserialize(Buffer::Iterator start)
direction = static_cast<TidLinkMapDir>(val & 0x03);
defaultMapping = (((val >> 2) & 0x01) == 1);
mappingSwitchTimePresent = (((val >> 3) & 0x01) == 1);
expectedDurationPresent = (((val >> 4) & 0x01) == 1);
linkMappingSize = (((val >> 5) & 0x01) == 1 ? 1 : 2);
if (defaultMapping)
{
presenceBitmap.reset();
return count;
}
presenceBitmap = i.ReadU8();
@@ -82,7 +93,60 @@ TidToLinkMapping::ElementIdExt() const
}
void
TidToLinkMapping::SetLinkMappingOfTid(uint8_t tid, std::list<uint8_t> linkIds)
TidToLinkMapping::SetMappingSwitchTime(Time mappingSwitchTime)
{
// The 2 octet Mapping Switch Time field has units of TUs and is set to the time at which
// the new mapping is established using as a time-base the value of the TSF corresponding
// to the BSS identified by the BSSID of the frame containing the TID-To-Link Mapping
// element: i.e., bits 10 to 25 of the TSF. (Sec. 9.4.2.314 of 802.11be D3.1)
NS_ABORT_IF(mappingSwitchTime < Simulator::Now());
auto switchTimeUsec = static_cast<uint64_t>(mappingSwitchTime.GetMicroSeconds());
// set the Mapping Switch Time field to bits 10 to 25 of the given time
m_mappingSwitchTime = (switchTimeUsec & ~BIT_63_TO_26_MASK) >> 10;
m_control.mappingSwitchTimePresent = true;
}
std::optional<Time>
TidToLinkMapping::GetMappingSwitchTime() const
{
if (!m_control.mappingSwitchTimePresent)
{
return std::nullopt;
}
auto nowUsec = static_cast<uint64_t>(Simulator::Now().GetMicroSeconds());
uint64_t switchTimeUsec = (*m_mappingSwitchTime << 10) + (nowUsec & BIT_63_TO_26_MASK);
if (switchTimeUsec < nowUsec)
{
// The switch time derived from the value in the corresponding field may be less than the
// current time in case the bits 10 to 25 of TSF have been reset since the transmission
// of the frame carrying this field. In such a case we have to increase bits 63 to 26 by 1
switchTimeUsec += (1 << 26);
}
return MicroSeconds(switchTimeUsec);
}
void
TidToLinkMapping::SetExpectedDuration(Time expectedDuration)
{
auto durationTu = static_cast<uint64_t>(expectedDuration.GetMicroSeconds()) >> 10;
m_expectedDuration = (durationTu & 0x0000000000ffffff); // Expected Duration size is 3 bytes
m_control.expectedDurationPresent = true;
}
std::optional<Time>
TidToLinkMapping::GetExpectedDuration() const
{
if (!m_control.expectedDurationPresent)
{
return std::nullopt;
}
return MicroSeconds(*m_expectedDuration << 10);
}
void
TidToLinkMapping::SetLinkMappingOfTid(uint8_t tid, std::set<uint8_t> linkIds)
{
NS_ABORT_MSG_IF(tid > 7, "Invalid tid: " << +tid);
NS_ABORT_MSG_IF(m_control.defaultMapping,
@@ -94,35 +158,35 @@ TidToLinkMapping::SetLinkMappingOfTid(uint8_t tid, std::list<uint8_t> linkIds)
for (const auto& linkId : linkIds)
{
linkMapping |= (1 << linkId);
if (linkId > 7)
{
m_control.linkMappingSize = 2;
}
}
m_linkMapping[tid] = linkMapping;
if (!m_control.presenceBitmap.has_value())
{
m_control.presenceBitmap = 0;
}
*m_control.presenceBitmap |= (1 << tid);
m_control.presenceBitmap = m_control.presenceBitmap.value_or(0) | (1 << tid);
}
std::list<uint8_t>
std::set<uint8_t>
TidToLinkMapping::GetLinkMappingOfTid(uint8_t tid) const
{
auto it = m_linkMapping.find(tid);
if (it == m_linkMapping.end())
if (it == m_linkMapping.cend())
{
return {};
}
std::list<uint8_t> linkIds;
std::set<uint8_t> linkIds;
for (uint8_t linkId = 0; linkId < 15; linkId++)
{
if (((it->second >> linkId) & 0x0001) == 1)
{
linkIds.push_back(linkId);
linkIds.insert(linkId);
}
}
NS_ABORT_MSG_IF(linkIds.empty(), "TID " << +tid << " cannot be mapped to an empty link set");
return linkIds;
}
@@ -130,26 +194,53 @@ TidToLinkMapping::GetLinkMappingOfTid(uint8_t tid) const
uint16_t
TidToLinkMapping::GetInformationFieldSize() const
{
// IEEE 802.11be D2.0 9.4.2.314 TID-To-Link Mapping element
// IEEE 802.11be D3.1 9.4.2.314 TID-To-Link Mapping element
uint16_t ret = WIFI_IE_ELEMENT_ID_EXT_SIZE; // Element ID Extension
ret += m_control.GetSubfieldSize();
NS_ASSERT_MSG(m_linkMapping.empty() == m_control.defaultMapping,
if (m_control.mappingSwitchTimePresent)
{
ret += 2; // Mapping Switch Time
}
if (m_control.expectedDurationPresent)
{
ret += 3; // Expected Duration
}
NS_ASSERT_MSG(!m_control.defaultMapping || m_linkMapping.empty(),
"Per-TID link mapping not expected if default mapping is set");
ret += WIFI_LINK_MAPPING_PER_TID_SIZE_B * (m_linkMapping.size());
ret += m_control.linkMappingSize * (m_linkMapping.size());
return ret;
}
void
TidToLinkMapping::SerializeInformationField(Buffer::Iterator start) const
{
// IEEE 802.11be D2.0 9.4.2.314 TID-To-Link Mapping element
// IEEE 802.11be D3.1 9.4.2.314 TID-To-Link Mapping element
m_control.Serialize(start);
NS_ASSERT_MSG(m_linkMapping.empty() == m_control.defaultMapping,
if (m_control.mappingSwitchTimePresent)
{
start.WriteHtolsbU16(*m_mappingSwitchTime);
}
if (m_control.expectedDurationPresent)
{
start.WriteU8((*m_expectedDuration >> 0) & 0xff);
start.WriteU8((*m_expectedDuration >> 8) & 0xff);
start.WriteU8((*m_expectedDuration >> 16) & 0xff);
}
NS_ASSERT_MSG(!m_control.defaultMapping || m_linkMapping.empty(),
"Per-TID link mapping not expected if default mapping is set");
for (const auto& [tid, linkMapping] : m_linkMapping)
{
start.WriteHtolsbU16(linkMapping);
if (m_control.linkMappingSize == 1)
{
start.WriteU8(linkMapping);
}
else
{
start.WriteHtolsbU16(linkMapping);
}
}
}
@@ -162,6 +253,23 @@ TidToLinkMapping::DeserializeInformationField(Buffer::Iterator start, uint16_t l
NS_ASSERT_MSG(nCtrlOctets <= length, "Tid-to-Link Mapping deserialize error");
i.Next(nCtrlOctets);
count += nCtrlOctets;
if (m_control.mappingSwitchTimePresent)
{
m_mappingSwitchTime = i.ReadLsbtohU16();
count += 2;
}
if (m_control.expectedDurationPresent)
{
uint8_t byte0 = i.ReadU8();
uint8_t byte1 = i.ReadU8();
uint8_t byte2 = i.ReadU8();
m_expectedDuration = byte2;
m_expectedDuration.value() <<= 8;
m_expectedDuration.value() |= byte1;
m_expectedDuration.value() <<= 8;
m_expectedDuration.value() |= byte0;
count += 3;
}
m_linkMapping.clear();
if (m_control.presenceBitmap.has_value())
{
@@ -172,8 +280,16 @@ TidToLinkMapping::DeserializeInformationField(Buffer::Iterator start, uint16_t l
{
if (((presenceBitmap >> tid) & 0x01) == 1)
{
m_linkMapping[tid] = i.ReadLsbtohU16();
count += 2;
if (m_control.linkMappingSize == 1)
{
m_linkMapping[tid] = i.ReadU8();
count++;
}
else
{
m_linkMapping[tid] = i.ReadLsbtohU16();
count += 2;
}
}
}
}

View File

@@ -20,12 +20,12 @@
#ifndef TID_TO_LINK_MAPPING_H
#define TID_TO_LINK_MAPPING_H
#include <ns3/wifi-information-element.h>
#include "ns3/nstime.h"
#include "ns3/wifi-information-element.h"
#include <list>
#include <map>
#include <optional>
#include <vector>
#include <set>
namespace ns3
{
@@ -45,8 +45,6 @@ enum class TidLinkMapDir : uint8_t
constexpr auto DEFAULT_WIFI_TID_LINK_MAPPING{true};
/// default value for the Direction subfield of the TID-To-Link Control field
constexpr auto DEFAULT_WIFI_TID_LINK_MAP_DIR{TidLinkMapDir::BOTH_DIRECTIONS};
/// size in bytes of the Link Mapping Of TID n field (IEEE 802.11be D2.0 9.4.2.314)
constexpr uint16_t WIFI_LINK_MAPPING_PER_TID_SIZE_B = 2;
/// size in bytes of the TID-To-Link Control field with default link mapping
constexpr uint16_t WIFI_TID_TO_LINK_MAPPING_CONTROL_BASIC_SIZE_B =
1; // IEEE 802.11be D2.0 9.4.2.314
@@ -59,7 +57,7 @@ constexpr uint16_t WIFI_LINK_MAPPING_PRESENCE_IND_SIZE_B = 1;
*
* This class serializes and deserializes
* the TID-to-Link Mapping element
* IEEE 802.11be D2.0 9.4.2.314
* IEEE 802.11be D3.1 9.4.2.314
*
*/
class TidToLinkMapping : public WifiInformationElement
@@ -67,13 +65,14 @@ class TidToLinkMapping : public WifiInformationElement
public:
/**
* TID-to-Link Mapping Control subfield
* IEEE 802.11be D2.0 Figure 9-1002an
* IEEE 802.11be D3.1 Figure 9-1002ap
*/
struct Control
{
friend class TidToLinkMapping;
TidLinkMapDir direction{DEFAULT_WIFI_TID_LINK_MAP_DIR}; ///< Direction
bool defaultMapping{DEFAULT_WIFI_TID_LINK_MAPPING}; ///< Default link mapping
std::optional<uint8_t> presenceBitmap; ///< Link Mapping Presence Indicator
/// \return Serialized size of TID-to-Link Mapping Control in octets
uint16_t GetSubfieldSize() const;
@@ -91,11 +90,43 @@ class TidToLinkMapping : public WifiInformationElement
* \return the number of octets read
*/
uint16_t Deserialize(Buffer::Iterator start);
private:
/**
* These members are private to prevent users to manipulate them directly (their values
* depend on the values of other fields)
*/
bool mappingSwitchTimePresent{false}; ///< Mapping Switch Time Present
bool expectedDurationPresent{false}; ///< Expected Duration Present
uint8_t linkMappingSize{1}; ///< length of the Link Mapping Of TID n field in octets
std::optional<uint8_t> presenceBitmap; ///< Link Mapping Presence Indicator
};
WifiInformationElementId ElementId() const override;
WifiInformationElementId ElementIdExt() const override;
/**
* Set the Mapping Switch Time field.
*
* \param mappingSwitchTime the time when the new mapping is established (rounded to a
* multiple of a TU)
*/
void SetMappingSwitchTime(Time mappingSwitchTime);
/// \return the value of the Mapping Switch Time field, if present.
std::optional<Time> GetMappingSwitchTime() const;
/**
* Set the Expected Duration field.
*
* \param expectedDuration the value for the Expected Duration field (rounded to a
* multiple of a TU)
*/
void SetExpectedDuration(Time expectedDuration);
/// \return the value of the Expected Duration field, if present.
std::optional<Time> GetExpectedDuration() const;
/**
* Set the Link Mapping field of the given TID such that the given TID is mapped
* to the links associated with the given link IDs.
@@ -103,19 +134,22 @@ class TidToLinkMapping : public WifiInformationElement
* \param tid the given TID
* \param linkIds the IDs of the links which the given TID is mapped to
*/
void SetLinkMappingOfTid(uint8_t tid, std::list<uint8_t> linkIds);
void SetLinkMappingOfTid(uint8_t tid, std::set<uint8_t> linkIds);
/**
* Get the Link Mapping field of the given TID.
*
* \param tid the given TID
* \return the IDs of the links which the given TID is mapped to
*/
std::list<uint8_t> GetLinkMappingOfTid(uint8_t tid) const;
std::set<uint8_t> GetLinkMappingOfTid(uint8_t tid) const;
TidToLinkMapping::Control m_control; ///< TID-to-link Mapping Control
std::map<uint8_t, uint16_t> m_linkMapping; ///< TID-indexed Link Mapping
private:
std::optional<uint16_t> m_mappingSwitchTime; ///< Mapping Switch Time
std::optional<uint32_t> m_expectedDuration; ///< Expected Duration
uint16_t GetInformationFieldSize() const override;
void SerializeInformationField(Buffer::Iterator start) const override;
uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;

View File

@@ -27,6 +27,7 @@
#include "ns3/wifi-phy-operating-channel.h"
#include <optional>
#include <set>
#include <sstream>
#include <vector>
@@ -1224,7 +1225,7 @@ class TidToLinkMappingElementTest : public HeaderSerializationTestCase
* \param args A sequence of TID-Link mapping pairs
*/
template <typename... Args>
void SetLinkMapping(uint8_t tid, const std::list<uint8_t>& linkIds, Args&&... args);
void SetLinkMapping(uint8_t tid, const std::set<uint8_t>& linkIds, Args&&... args);
/**
* Base case to stop the recursion performed by the templated version of this method.
@@ -1251,7 +1252,7 @@ TidToLinkMappingElementTest::TidToLinkMappingElementTest(TidLinkMapDir direction
template <typename... Args>
void
TidToLinkMappingElementTest::SetLinkMapping(uint8_t tid,
const std::list<uint8_t>& linkIds,
const std::set<uint8_t>& linkIds,
Args&&... args)
{
m_tidToLinkMapping.m_control.defaultMapping = false;
@@ -1364,31 +1365,31 @@ WifiEhtInfoElemsTestSuite::WifiEhtInfoElemsTestSuite()
AddTestCase(new WifiEhtCapabilitiesIeTest(false, 320), TestCase::QUICK);
AddTestCase(new TidToLinkMappingElementTest(TidLinkMapDir::DOWNLINK), TestCase::QUICK);
AddTestCase(
new TidToLinkMappingElementTest(TidLinkMapDir::UPLINK, 3, std::list<uint8_t>{0, 4, 6}),
new TidToLinkMappingElementTest(TidLinkMapDir::UPLINK, 3, std::set<uint8_t>{0, 4, 6}),
TestCase::QUICK);
AddTestCase(new TidToLinkMappingElementTest(TidLinkMapDir::BOTH_DIRECTIONS,
3,
std::list<uint8_t>{0, 4, 6},
std::set<uint8_t>{0, 4, 6},
6,
std::list<uint8_t>{3, 7, 11, 14}),
std::set<uint8_t>{3, 7, 11, 14}),
TestCase::QUICK);
AddTestCase(new TidToLinkMappingElementTest(TidLinkMapDir::DOWNLINK,
0,
std::list<uint8_t>{0, 1, 2},
std::set<uint8_t>{0, 1, 2},
1,
std::list<uint8_t>{3, 4, 5},
std::set<uint8_t>{3, 4, 5},
2,
std::list<uint8_t>{6, 7},
std::set<uint8_t>{6, 7},
3,
std::list<uint8_t>{8, 9, 10},
std::set<uint8_t>{8, 9, 10},
4,
std::list<uint8_t>{11, 12, 13},
std::set<uint8_t>{11, 12, 13},
5,
std::list<uint8_t>{14},
std::set<uint8_t>{14},
6,
std::list<uint8_t>{1, 3, 6},
std::set<uint8_t>{1, 3, 6},
7,
std::list<uint8_t>{11, 14}),
std::set<uint8_t>{11, 14}),
TestCase::QUICK);
AddTestCase(new EhtOperationElementTest({0, 0, 0, 0, 0}, 1, 2, 3, 4, 5, 6, 7, 8, std::nullopt),
TestCase::QUICK);