A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
eht-frame-exchange-manager.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2022 Universita' degli Studi di Napoli Federico II
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Stefano Avallone <stavallo@unina.it>
7 */
8
10
11#include "ap-emlsr-manager.h"
12#include "eht-phy.h"
13#include "emlsr-manager.h"
14
15#include "ns3/abort.h"
16#include "ns3/ap-wifi-mac.h"
17#include "ns3/log.h"
18#include "ns3/mgt-action-headers.h"
19#include "ns3/spectrum-signal-parameters.h"
20#include "ns3/sta-wifi-mac.h"
21#include "ns3/wifi-mac-queue.h"
22#include "ns3/wifi-net-device.h"
23#include "ns3/wifi-spectrum-phy-interface.h"
24
25#include <algorithm>
26
27#undef NS_LOG_APPEND_CONTEXT
28#define NS_LOG_APPEND_CONTEXT WIFI_FEM_NS_LOG_APPEND_CONTEXT
29
30namespace ns3
31{
32
34
35/**
36 * Additional time (exceeding 20 us) to wait for a PHY-RXSTART.indication when the PHY is
37 * decoding a PHY header.
38 *
39 * Values for aRxPHYStartDelay:
40 * - OFDM : 20 us (for 20 MHz) [Table 17-21 of 802.11-2020]
41 * - ERP-OFDM : 20 us [Table 18-5 of 802.11-2020]
42 * - HT : 28 us (HT-mixed), 24 us (HT-greenfield) [Table 19-25 of 802.11-2020]
43 * - VHT : 36 + 4 * max N_VHT-LTF + 4 = 72 us [Table 21-28 of 802.11-2020]
44 * - HE : 32 us (for HE SU and HE TB PPDUs)
45 * 32 + 4 * N_HE-SIG-B us (for HE MU PPDUs) [Table 27-54 of 802.11ax-2021]
46 * - EHT : 32 us (for EHT TB PPDUs)
47 * 32 + 4 * N_EHT-SIG us (for EHT MU PPDUs) [Table 36-70 of 802.11be D3.2]
48 */
49static constexpr uint8_t WAIT_FOR_RXSTART_DELAY_USEC = 52;
50
51NS_LOG_COMPONENT_DEFINE("EhtFrameExchangeManager");
52
54
57{
58 static TypeId tid = TypeId("ns3::EhtFrameExchangeManager")
60 .AddConstructor<EhtFrameExchangeManager>()
61 .SetGroupName("Wifi");
62 return tid;
63}
64
69
74
75void
82
83void
85{
86 NS_LOG_FUNCTION(this << txVector << psduDuration.As(Time::MS));
87
88 HeFrameExchangeManager::RxStartIndication(txVector, psduDuration);
90}
91
92void
94{
95 if (auto protectionManager = GetProtectionManager())
96 {
97 protectionManager->SetLinkId(linkId);
98 }
99 if (auto ackManager = GetAckManager())
100 {
101 ackManager->SetLinkId(linkId);
102 }
103 m_msduAggregator->SetLinkId(linkId);
104 m_mpduAggregator->SetLinkId(linkId);
106}
107
110{
111 NS_LOG_FUNCTION(this << *mpdu);
112
113 // alias needs only be created for non-broadcast QoS data frames exchanged between two MLDs
114 if (!mpdu->GetHeader().IsQosData() || m_mac->GetNLinks() == 1 ||
115 mpdu->GetHeader().GetAddr1().IsGroup() ||
116 !GetWifiRemoteStationManager()->GetMldAddress(mpdu->GetHeader().GetAddr1()))
117 {
119 }
120
121 mpdu = mpdu->CreateAlias(m_linkId);
122 auto& hdr = mpdu->GetHeader();
123 hdr.SetAddr2(GetAddress());
124 auto address = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(hdr.GetAddr1());
125 NS_ASSERT(address);
126 hdr.SetAddr1(*address);
127 /*
128 * Set Address3 according to Table 9-30 of 802.11-2020 and Section 35.3.3 of
129 * 802.11be D2.0 ["the value of the Address 3 field and the Address 4 field (if present)
130 * in the MAC header of a data frame shall be set based on Table 9-30 (Address field
131 * contents) and the settings of the To DS and From DS bits, where the BSSID is the
132 * MAC address of the AP affiliated with the AP MLD corresponding to that link"].
133 */
134 if (hdr.IsQosAmsdu())
135 {
136 if (hdr.IsToDs() && !hdr.IsFromDs())
137 {
138 // from STA to AP: BSSID is in Address1
139 hdr.SetAddr3(hdr.GetAddr1());
140 }
141 else if (!hdr.IsToDs() && hdr.IsFromDs())
142 {
143 // from AP to STA: BSSID is in Address2
144 hdr.SetAddr3(hdr.GetAddr2());
145 }
146 }
147
148 return mpdu;
149}
150
151bool
153{
154 if (!m_staMac || !m_staMac->IsEmlsrLink(m_linkId))
155 {
156 return false;
157 }
158 auto apAddress = GetWifiRemoteStationManager()->GetMldAddress(m_bssid);
159 NS_ASSERT_MSG(apAddress, "MLD address not found for BSSID " << m_bssid);
160 // when EMLSR links are blocked, all TIDs are blocked (we test TID 0 here)
162 auto mask = m_staMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, m_linkId);
163 NS_ASSERT_MSG(mask, "No mask for AP " << *apAddress << " on link " << m_linkId);
164 return mask->test(static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK));
165}
166
167bool
169{
170 NS_LOG_FUNCTION(this << edca << allowedWidth);
171
172 m_allowedWidth = allowedWidth;
173
174 if (m_apMac)
175 {
176 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
177 {
178 if (linkId == m_linkId)
179 {
180 continue;
181 }
182
183 // EMLSR clients involved in a DL or UL TXOP on another link
184 std::set<Mac48Address> emlsrClients;
185
186 // check if an EMLSR client is the holder of an UL TXOP on the other link
187 if (auto ehtFem =
188 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
189 ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
190 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(
191 ehtFem->m_txopHolder.value()))
192 {
193 emlsrClients.insert(ehtFem->m_txopHolder.value());
194 }
195
196 // check if EMLSR clients are involved in a DL TXOP on another link
197 for (const auto& address : m_protectedStas)
198 {
199 if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(address))
200 {
201 emlsrClients.insert(address);
202 }
203 }
204
205 for (const auto& address : emlsrClients)
206 {
207 auto mldAddress =
208 m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(address);
209 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
210
211 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(*mldAddress))
212 {
213 // EMLSR client did not enable EMLSR mode on this link, we can transmit to it
214 continue;
215 }
216
217 // check that this link is blocked as expected
218 WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *mldAddress, 0);
219 auto mask =
220 m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, m_linkId);
221 NS_ASSERT_MSG(mask,
222 "No mask for client " << *mldAddress << " on link " << +m_linkId);
223 if (!mask->test(
224 static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK)))
225 {
226 NS_ASSERT_MSG(false,
227 "Transmissions to " << *mldAddress << " on link " << +m_linkId
228 << " are not blocked");
229 // in case asserts are disabled, block transmissions on the other links because
230 // this is what we need
232 *mldAddress,
233 {m_linkId});
234 }
235 }
236 }
237 }
238
239 std::optional<Time> timeToCtsEnd;
240
241 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
242 {
243 // Cannot start a transmission on a link blocked because another EMLSR link is being used
245 {
246 NS_LOG_DEBUG("StartTransmission called while another EMLSR link is being used");
248 return false;
249 }
250
251 auto emlsrManager = m_staMac->GetEmlsrManager();
252
253 if (auto elapsed = emlsrManager->GetElapsedMediumSyncDelayTimer(m_linkId);
254 elapsed && emlsrManager->MediumSyncDelayNTxopsExceeded(m_linkId))
255 {
256 NS_LOG_DEBUG("No new TXOP attempts allowed while MediumSyncDelay is running");
257 // request channel access if needed when the MediumSyncDelay timer expires; in the
258 // meantime no queued packet can be transmitted
260 emlsrManager->GetMediumSyncDuration() - *elapsed,
262 edca,
263 m_linkId,
264 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT, // queued frames cannot be transmitted until
265 // MSD expires
266 Txop::DONT_CHECK_MEDIUM_BUSY); // generate backoff regardless of medium busy
268 return false;
269 }
270
271 if (!m_phy)
272 {
273 NS_LOG_DEBUG("No PHY is currently operating on EMLSR link " << +m_linkId);
275 return false;
276 }
277
278 // let EMLSR manager decide whether to prevent or allow this UL TXOP
279 if (const auto [startTxop, delay] = emlsrManager->GetDelayUntilAccessRequest(
280 m_linkId,
281 DynamicCast<QosTxop>(edca)->GetAccessCategory());
282 !startTxop)
283
284 {
285 if (delay.IsStrictlyPositive())
286 {
289 delay,
291 edca,
292 m_linkId,
293 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT, // queued frames cannot be
294 // transmitted until RX ends
295 Txop::CHECK_MEDIUM_BUSY); // generate backoff if medium busy
296 }
297 return false;
298 }
299 }
300
301 auto started = HeFrameExchangeManager::StartTransmission(edca, allowedWidth);
302
303 if (started && m_staMac && m_staMac->IsEmlsrLink(m_linkId))
304 {
305 // notify the EMLSR Manager of the UL TXOP start on an EMLSR link
306 NS_ASSERT(m_staMac->GetEmlsrManager());
307 m_staMac->GetEmlsrManager()->NotifyUlTxopStart(m_linkId);
308 }
309
310 if (started)
311 {
312 // we are starting a new TXOP, hence consider the previous ongoing TXOP as terminated
314 }
315
316 return started;
317}
318
319void
321{
322 NS_LOG_FUNCTION(this << psdu << txVector);
323
324 // EHT-SIG, the equivalent of HE-SIG-B, is present in EHT SU transmissions, too
325 if (txVector.GetPreambleType() == WIFI_PREAMBLE_EHT_MU)
326 {
328 auto sigBMode = phy->GetSigBMode(txVector);
329 txVector.SetSigBMode(sigBMode);
330 }
331
332 auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
333
335 UpdateTxopEndOnTxStart(txDuration, psdu->GetDuration());
336
337 if (m_apMac && m_apMac->GetApEmlsrManager())
338 {
339 auto delay = m_apMac->GetApEmlsrManager()->GetDelayOnTxPsduNotForEmlsr(psdu,
340 txVector,
341 m_phy->GetPhyBand());
342
343 // check if the EMLSR clients shall switch back to listening operation
344 for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
345 {
346 auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt);
347
348 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) &&
349 GetEmlsrSwitchToListening(psdu, aid, *clientIt))
350 {
351 EmlsrSwitchToListening(*clientIt, delay);
352 // this client is no longer involved in the current TXOP
353 clientIt = m_protectedStas.erase(clientIt);
354 }
355 else
356 {
357 clientIt++;
358 }
359 }
360 }
361 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
362 m_staMac->GetEmlsrManager()->GetInDeviceInterference())
363 {
364 for (const auto linkId : m_staMac->GetLinkIds())
365 {
366 if (auto phy = m_mac->GetWifiPhy(linkId);
367 phy && linkId != m_linkId && m_staMac->IsEmlsrLink(linkId))
368 {
369 auto txPowerDbm = phy->GetPower(txVector.GetTxPowerLevel()) + phy->GetTxGain();
370 // generate in-device interference on the other EMLSR link for the duration of this
371 // transmission
372 GenerateInDeviceInterference(linkId, txDuration, DbmToW(txPowerDbm));
373 }
374 }
375 }
376}
377
378void
380{
381 NS_LOG_FUNCTION(this << psduMap << txVector);
382
383 auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand());
384
386 UpdateTxopEndOnTxStart(txDuration, psduMap.begin()->second->GetDuration());
387
388 if (m_apMac)
389 {
390 // check if the EMLSR clients shall switch back to listening operation at the end of this
391 // PPDU
392 for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
393 {
394 auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt);
395
396 if (auto psduMapIt = psduMap.find(aid);
397 GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) &&
398 (psduMapIt == psduMap.cend() ||
399 GetEmlsrSwitchToListening(psduMapIt->second, aid, *clientIt)))
400 {
401 EmlsrSwitchToListening(*clientIt, txDuration);
402 // this client is no longer involved in the current TXOP
403 clientIt = m_protectedStas.erase(clientIt);
404 }
405 else
406 {
407 clientIt++;
408 }
409 }
410 }
411 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
412 m_staMac->GetEmlsrManager()->GetInDeviceInterference())
413 {
414 for (const auto linkId : m_staMac->GetLinkIds())
415 {
416 if (auto phy = m_mac->GetWifiPhy(linkId);
417 phy && linkId != m_linkId && m_staMac->IsEmlsrLink(linkId))
418 {
419 auto txPowerDbm = phy->GetPower(txVector.GetTxPowerLevel()) + phy->GetTxGain();
420 // generate in-device interference on the other EMLSR link for the duration of this
421 // transmission
422 GenerateInDeviceInterference(linkId, txDuration, DbmToW(txPowerDbm));
423 }
424 }
425 }
426}
427
428void
430{
431 NS_LOG_FUNCTION(this << linkId << duration.As(Time::US) << txPower);
432
433 auto rxPhy = DynamicCast<SpectrumWifiPhy>(m_mac->GetWifiPhy(linkId));
434
435 if (!rxPhy)
436 {
437 NS_LOG_DEBUG("No spectrum PHY operating on link " << +linkId);
438 return;
439 }
440
442 NS_ASSERT(txPhy);
443
444 auto psd = Create<SpectrumValue>(rxPhy->GetCurrentInterface()->GetRxSpectrumModel());
445 *psd = txPower;
446
447 auto spectrumSignalParams = Create<SpectrumSignalParameters>();
448 spectrumSignalParams->duration = duration;
449 spectrumSignalParams->txPhy = txPhy->GetCurrentInterface();
450 spectrumSignalParams->txAntenna = txPhy->GetAntenna();
451 spectrumSignalParams->psd = psd;
452
453 rxPhy->StartRx(spectrumSignalParams, rxPhy->GetCurrentInterface());
454}
455
456void
458{
459 NS_LOG_FUNCTION(this);
461 {
462 // the CTS may have been missed because another EMLSR link is being used; do not reset NAV
463 return;
464 }
466}
467
468void
470{
471 NS_LOG_FUNCTION(this);
473 {
474 // the CTS may have been missed because another EMLSR link is being used; do not reset NAV
475 return;
476 }
478}
479
480void
482{
483 NS_LOG_FUNCTION(this << address << delay.As(Time::US));
484
485 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
486 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
487 NS_ASSERT_MSG(m_apMac, "This function shall only be called by AP MLDs");
488
489 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
490 {
491 if (auto ehtFem =
492 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
493 ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
494 m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(*ehtFem->m_txopHolder) ==
495 mldAddress)
496 {
497 // this EMLSR client is the holder of an UL TXOP, do not unblock links
498 return;
499 }
500 }
501
502 // this EMLSR client switches back to listening operation a transition delay
503 // after the given delay
504 auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
505 NS_ASSERT(emlCapabilities);
506
507 std::set<uint8_t> linkIds;
508 for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
509 {
510 if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
511 {
512 linkIds.insert(linkId);
513 }
514 }
515
516 auto blockLinks = [=, this]() {
517 // the reason for blocking the other EMLSR links has changed now
519 *mldAddress,
520 linkIds);
521
522 // block DL transmissions on this link until transition delay elapses
524 *mldAddress,
525 linkIds);
526 };
527
528 delay.IsZero() ? blockLinks() : static_cast<void>(Simulator::Schedule(delay, blockLinks));
529
530 // unblock all EMLSR links when the transition delay elapses
531 auto unblockLinks = [=, this]() {
533 *mldAddress,
534 linkIds);
535 };
536
538 emlCapabilities->get().emlsrTransitionDelay);
539
540 endDelay.IsZero() ? unblockLinks()
541 : static_cast<void>(m_transDelayTimer[*mldAddress] =
542 Simulator::Schedule(endDelay, unblockLinks));
543}
544
545void
547{
548 NS_LOG_FUNCTION(this << phy << linkId << delay.As(Time::US));
549
550 // TODO Shall we assert that there is no ongoing frame exchange sequence? Or is it possible
551 // that there is an ongoing frame exchange sequence (in such a case, we need to force a
552 // timeout, just like it is done in case of a normal channel switch
553
554 NS_ABORT_MSG_IF(!m_staMac, "This method can only be called on a STA");
555
556 // if we receive the notification from a PHY that is not connected to us, it means that
557 // we have been already connected to another PHY operating on this link, hence we do not
558 // have to reset the connected PHY. Similarly, we do not have to reset the connected PHY if
559 // the link does not change (this occurs when changing the channel width of aux PHYs upon
560 // enabling the EMLSR mode).
561 if (phy == m_phy && linkId != m_linkId)
562 {
563 ResetPhy();
564 }
565 m_staMac->NotifySwitchingEmlsrLink(phy, linkId, delay);
566}
567
568void
570{
571 NS_LOG_FUNCTION(this << dest << frame);
572
573 WifiMacHeader hdr;
575 hdr.SetAddr1(dest);
576 hdr.SetAddr2(m_self);
577 hdr.SetAddr3(m_bssid);
578 hdr.SetDsNotTo();
579 hdr.SetDsNotFrom();
580
581 // get the sequence number for the TWT Setup management frame
582 const auto sequence = m_txMiddle->GetNextSequenceNumberFor(&hdr);
583 hdr.SetSequenceNumber(sequence);
584
585 WifiActionHeader actionHdr;
589
590 auto packet = Create<Packet>();
591 packet->AddHeader(frame);
592 packet->AddHeader(actionHdr);
593
594 // Use AC_VO to send management frame addressed to a QoS STA (Sec. 10.2.3.2 of 802.11-2020)
595 m_mac->GetQosTxop(AC_VO)->Queue(Create<WifiMpdu>(packet, hdr));
596}
597
598std::optional<dBm_u>
600{
601 auto optRssi = HeFrameExchangeManager::GetMostRecentRssi(address);
602
603 if (optRssi)
604 {
605 return optRssi;
606 }
607
608 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
609
610 if (!mldAddress)
611 {
612 // not an MLD, nothing else can be done
613 return std::nullopt;
614 }
615
616 for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
617 {
618 std::optional<Mac48Address> linkAddress;
619 if (linkId != m_linkId &&
620 (linkAddress = m_mac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(
621 *mldAddress)) &&
622 (optRssi = m_mac->GetWifiRemoteStationManager(linkId)->GetMostRecentRssi(*linkAddress)))
623 {
624 return optRssi;
625 }
626 }
627
628 return std::nullopt;
629}
630
631void
633{
634 NS_LOG_FUNCTION(this << &txParams);
635
636 uint8_t maxPaddingDelay = 0;
637
638 // block transmissions on the other EMLSR links of the EMLSR clients
639 for (const auto& address : m_sentRtsTo)
640 {
641 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
642 {
643 continue;
644 }
645
646 auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
647 NS_ASSERT(emlCapabilities);
648 maxPaddingDelay = std::max(maxPaddingDelay, emlCapabilities->get().emlsrPaddingDelay);
649
650 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
651 NS_ASSERT(mldAddress);
652
653 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
654 {
655 if (linkId != m_linkId &&
656 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
657 {
659 *mldAddress,
660 {linkId});
661 }
662 }
663 }
664
665 // add padding (if needed)
666 if (maxPaddingDelay > 0)
667 {
668 NS_ASSERT(txParams.m_protection &&
669 txParams.m_protection->method == WifiProtection::MU_RTS_CTS);
670 auto protection = static_cast<WifiMuRtsCtsProtection*>(txParams.m_protection.get());
671 NS_ASSERT(protection->muRts.IsMuRts());
672
673 // see formula (35-1) in Sec. 35.5.2.2.3 of 802.11be D3.0
674 auto rate = protection->muRtsTxVector.GetMode().GetDataRate(protection->muRtsTxVector);
675 std::size_t nDbps = rate / 1e6 * 4; // see Table 17-4 of 802.11-2020
676 protection->muRts.SetPaddingSize((1 << (maxPaddingDelay + 2)) * nDbps / 8);
677 }
678
680}
681
682void
684 const CtrlTriggerHeader& trigger,
685 double muRtsSnr)
686{
687 NS_LOG_FUNCTION(this << muRtsHdr << trigger << muRtsSnr);
688
690 if (auto emlsrManager = m_staMac->GetEmlsrManager())
691 {
692 auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId());
693
694 // an aux PHY that is not TX capable may get a TXOP, release the channel and request
695 // the main PHY to switch channel. Shortly afterwards, the AP MLD may send an ICF, thus
696 // when the main PHY is scheduled to send the CTS, the main PHY may be switching channel
697 // or may be operating on another link
698 if (mainPhy->IsStateSwitching() || m_mac->GetLinkForPhy(mainPhy) != m_linkId)
699 {
700 NS_LOG_DEBUG("Main PHY is switching or operating on another link, abort sending CTS");
701 return;
702 }
703 }
704 HeFrameExchangeManager::SendCtsAfterMuRts(muRtsHdr, trigger, muRtsSnr);
705}
706
707void
709{
710 NS_LOG_FUNCTION(this << *muRts << txVector);
711
712 // check if all the clients solicited by the MU-RTS are EMLSR clients that have sent (or
713 // are sending) a frame to the AP
714 auto crossLinkCollision = true;
715
716 // we blocked transmissions on the other EMLSR links for the EMLSR clients we sent the ICF to.
717 // Given that no client responded, we can unblock transmissions for a client if there is no
718 // ongoing UL TXOP held by that client
719 for (const auto& address : m_sentRtsTo)
720 {
721 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
722 {
723 crossLinkCollision = false;
724 continue;
725 }
726
727 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
728 NS_ASSERT(mldAddress);
729
730 std::set<uint8_t> linkIds; // all EMLSR links of EMLSR client
731 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
732 {
733 if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
734 {
735 linkIds.insert(linkId);
736 }
737 }
738
739 if (std::any_of(linkIds.cbegin(),
740 linkIds.cend(),
741 /* lambda returning true if an UL TXOP is ongoing on the given link ID */
742 [=, this](uint8_t id) {
743 auto ehtFem = StaticCast<EhtFrameExchangeManager>(
744 m_mac->GetFrameExchangeManager(id));
745 return ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
746 m_mac->GetMldAddress(ehtFem->m_txopHolder.value()) == mldAddress;
747 }))
748 {
749 // an UL TXOP is ongoing on one EMLSR link, do not unblock links
750 continue;
751 }
752
753 // no UL TXOP is ongoing on any EMLSR link; if the EMLSR client is not transmitting a
754 // frame to the AP on any EMLSR link, then the lack of response to the MU-RTS was not
755 // caused by a simultaneous UL transmission
756 if (std::none_of(linkIds.cbegin(),
757 linkIds.cend(),
758 /* lambda returning true if an MPDU from the EMLSR client is being received
759 on the given link ID */
760 [=, this](uint8_t id) {
761 auto macHdr = m_mac->GetFrameExchangeManager(id)->GetReceivedMacHdr();
762 return macHdr.has_value() &&
763 m_mac->GetMldAddress(macHdr->get().GetAddr2()) == mldAddress;
764 }))
765 {
766 crossLinkCollision = false;
767 }
768
769 linkIds.erase(m_linkId);
771 *mldAddress,
772 linkIds);
773 }
774
775 auto updateFailedCw =
776 crossLinkCollision ? m_apMac->GetApEmlsrManager()->UpdateCwAfterFailedIcf() : true;
777 DoCtsAfterMuRtsTimeout(muRts, txVector, updateFailedCw);
778}
779
780void
782 WifiMode rtsTxMode,
783 double rtsSnr)
784{
785 NS_LOG_FUNCTION(this << rtsHdr << rtsTxMode << rtsSnr);
786
787 if (m_apMac && GetWifiRemoteStationManager()->GetEmlsrEnabled(rtsHdr.GetAddr2()))
788 {
789 // we are going to send a CTS to an EMLSR client, transmissions to such EMLSR client
790 // must be blocked on the other EMLSR links
791
792 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(rtsHdr.GetAddr2());
793 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << rtsHdr.GetAddr2());
794
795 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
796 {
797 if (linkId != m_linkId &&
798 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
799 {
800 // check that other links are blocked as expected
801 WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *mldAddress, 0);
802 auto mask =
803 m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
804 NS_ASSERT_MSG(mask, "No mask for client " << *mldAddress << " on link " << +linkId);
805 if (!mask->test(
806 static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK)))
807 {
808 NS_ASSERT_MSG(false,
809 "Transmissions to " << *mldAddress << " on link " << +linkId
810 << " are not blocked");
811 // in case asserts are disabled, block transmissions on the other links because
812 // this is what we need
814 *mldAddress,
815 {linkId});
816 }
817 }
818 }
819 }
820
821 HeFrameExchangeManager::SendCtsAfterRts(rtsHdr, rtsTxMode, rtsSnr);
822}
823
824bool
826 uint16_t aid,
827 const Mac48Address& address) const
828{
829 NS_LOG_FUNCTION(this << psdu << aid << address);
830
831 // Sec. 35.3.17 of 802.11be D3.0:
832 // The non-AP MLD shall be switched back to the listening operation on the EMLSR links after
833 // the EMLSR transition delay time if [...] the non-AP STA affiliated with the non-AP MLD
834 // does not detect [...] any of the following frames:
835 // - an individually addressed frame with the RA equal to the MAC address of the non-AP STA
836 // affiliated with the non-AP MLD
837 if (psdu->GetAddr1() == address)
838 {
839 return false;
840 }
841
842 // - a Trigger frame that has one of the User Info fields addressed to the non-AP STA
843 // affiliated with the non-AP MLD
844 for (const auto& mpdu : *PeekPointer(psdu))
845 {
846 if (mpdu->GetHeader().IsTrigger())
847 {
848 CtrlTriggerHeader trigger;
849 mpdu->GetPacket()->PeekHeader(trigger);
850 if (trigger.FindUserInfoWithAid(aid) != trigger.end())
851 {
852 return false;
853 }
854 }
855 }
856
857 // - a CTS-to-self frame with the RA equal to the MAC address of the AP affiliated with
858 // the AP MLD
859 if (psdu->GetHeader(0).IsCts())
860 {
861 if (m_apMac && psdu->GetAddr1() == m_self)
862 {
863 return false;
864 }
865 if (m_staMac && psdu->GetAddr1() == m_bssid)
866 {
867 return false;
868 }
869 }
870
871 // - a Multi-STA BlockAck frame that has one of the Per AID TID Info fields addressed to
872 // the non-AP STA affiliated with the non-AP MLD
873 if (psdu->GetHeader(0).IsBlockAck())
874 {
875 CtrlBAckResponseHeader blockAck;
876 psdu->GetPayload(0)->PeekHeader(blockAck);
877 if (blockAck.IsMultiSta() && !blockAck.FindPerAidTidInfoWithAid(aid).empty())
878 {
879 return false;
880 }
881 }
882
883 // - a NDP Announcement frame that has one of the STA Info fields addressed to the non-AP
884 // STA affiliated with the non-AP MLD and a sounding NDP
885 // TODO NDP Announcement frame not supported yet
886
887 return true;
888}
889
890void
892{
893 NS_LOG_FUNCTION(this);
894
895 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
896 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
897 {
898 NS_LOG_DEBUG("Reset the counter of TXOP attempts allowed while "
899 "MediumSyncDelay is running");
900 m_staMac->GetEmlsrManager()->ResetMediumSyncDelayNTxops(m_linkId);
901 }
902
904}
905
906void
908{
909 NS_LOG_FUNCTION(this);
910
911 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
912 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
913 {
914 NS_LOG_DEBUG("Decrement the remaining number of TXOP attempts allowed while "
915 "MediumSyncDelay is running");
916 m_staMac->GetEmlsrManager()->DecrementMediumSyncDelayNTxops(m_linkId);
917 }
918
920}
921
922void
924{
925 NS_LOG_FUNCTION(this << txop);
926
927 if (m_apMac)
928 {
929 // the channel has been released; all EMLSR clients are switching back to
930 // listening operation
931 for (const auto& address : m_protectedStas)
932 {
933 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
934 {
935 EmlsrSwitchToListening(address, Seconds(0));
936 }
937 }
938 }
939 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
940 {
941 // Notify the UL TXOP end to the EMLSR Manager
942 auto edca = DynamicCast<QosTxop>(txop);
943 NS_ASSERT(edca);
944 auto txopStart = edca->GetTxopStartTime(m_linkId);
945
946 NS_ASSERT(m_staMac->GetEmlsrManager());
947 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId,
948 (!txopStart || *txopStart == Simulator::Now()),
950 }
951
953}
954
955void
957{
958 NS_LOG_FUNCTION(this << psdu << txVector);
959
960 // In addition, the timer resets to zero when any of the following events occur:
961 // — The STA receives an MPDU
962 // (Sec. 35.3.16.8.1 of 802.11be D3.1)
963 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
964 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
965 {
966 m_staMac->GetEmlsrManager()->CancelMediumSyncDelayTimer(m_linkId);
967 }
968
969 if (m_apMac)
970 {
971 // we iterate over protected STAs to consider only the case when the AP is the TXOP holder.
972 // The AP received a PSDU from a non-AP STA; given that the AP is the TXOP holder, this
973 // PSDU has been likely solicited by the AP. In most of the cases, we identify which EMLSR
974 // clients are no longer involved in the TXOP when the AP transmits the frame soliciting
975 // response(s) from client(s). This is not the case, for example, for the acknowledgment
976 // in SU format of a DL MU PPDU, where all the EMLSR clients (but one) switch to listening
977 // operation after the immediate response (if any) by one of the EMLSR clients.
978 for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
979 {
980 // TB PPDUs are received by the AP at distinct times, so it is difficult to take a
981 // decision based on one of them. However, clients transmitting TB PPDUs are identified
982 // by the soliciting Trigger Frame, thus we have already identified (when sending the
983 // Trigger Frame) which EMLSR clients have switched to listening operation.
984 // If the PSDU is not carried in a TB PPDU, we can determine whether this EMLSR client
985 // is switching to listening operation by checking whether the AP is expecting a
986 // response from it.
987 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) && !txVector.IsUlMu() &&
988 !m_txTimer.GetStasExpectedToRespond().contains(*clientIt))
989 {
990 EmlsrSwitchToListening(*clientIt, Seconds(0));
991 // this client is no longer involved in the current TXOP
992 clientIt = m_protectedStas.erase(clientIt);
993 }
994 else
995 {
996 clientIt++;
997 }
998 }
999 }
1000
1002}
1003
1004void
1006{
1007 NS_LOG_FUNCTION(this << psdu << txVector);
1008
1010
1011 if (m_apMac && m_apMac->GetApEmlsrManager())
1012 {
1013 m_apMac->GetApEmlsrManager()->NotifyPsduRxOk(m_linkId, psdu);
1014 }
1015
1016 if (m_apMac && m_txopHolder == psdu->GetAddr2() &&
1017 GetWifiRemoteStationManager()->GetEmlsrEnabled(*m_txopHolder))
1018 {
1020 {
1021 // an EMLSR client has started an UL TXOP. Start the ongoingTxopEnd timer so that
1022 // the next call to UpdateTxopEndOnRxEnd does its job
1025 }
1026
1027 UpdateTxopEndOnRxEnd(psdu->GetDuration());
1028 }
1029
1031 {
1032 if (GetEmlsrSwitchToListening(psdu, m_staMac->GetAssociationId(), m_self))
1033 {
1034 // we are no longer involved in the TXOP and switching to listening mode
1036 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
1037 }
1038 else
1039 {
1040 UpdateTxopEndOnRxEnd(psdu->GetDuration());
1041 }
1042 }
1043}
1044
1045bool
1047 const WifiTxVector& txVector)
1048{
1049 NS_LOG_FUNCTION(this);
1050
1051 auto sender = hdr.GetAddr2();
1052
1054 {
1055 NS_LOG_DEBUG("A TXOP is already ongoing");
1056 return false;
1057 }
1058
1059 if (auto holder = FindTxopHolder(hdr, txVector); holder != sender)
1060 {
1061 NS_LOG_DEBUG("Sender (" << sender << ") differs from the TXOP holder ("
1062 << (holder ? Address(*holder) : Address()) << ")");
1063 return false;
1064 }
1065
1066 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(sender))
1067 {
1068 NS_LOG_DEBUG("Sender (" << sender << ") is not an EMLSR client");
1069 return false;
1070 }
1071
1072 NS_LOG_DEBUG("EMLSR client " << sender << " is starting a TXOP");
1073
1074 // Block transmissions for this EMLSR client on other links
1075 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(sender);
1076 NS_ASSERT(mldAddress);
1077
1078 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
1079 {
1080 if (linkId != m_linkId &&
1081 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
1082 {
1084 *mldAddress,
1085 {linkId});
1086 }
1087 }
1088
1089 // Make sure that transmissions for this EMLSR client are not blocked on this link
1090 // (the AP MLD may have sent an ICF on another link right before receiving this MPDU,
1091 // thus transmissions on this link may have been blocked)
1093 *mldAddress,
1094 {m_linkId});
1095
1096 // Stop the transition delay timer for this EMLSR client, if any is running
1097 if (auto it = m_transDelayTimer.find(*mldAddress);
1098 it != m_transDelayTimer.end() && it->second.IsPending())
1099 {
1100 it->second.PeekEventImpl()->Invoke();
1101 it->second.Cancel();
1102 }
1103
1104 return true;
1105}
1106
1107EventId&
1112
1113void
1115{
1116 NS_LOG_FUNCTION(this << psdu);
1117
1118 if (m_apMac && m_apMac->GetApEmlsrManager())
1119 {
1120 m_apMac->GetApEmlsrManager()->NotifyPsduRxError(m_linkId, psdu);
1121 }
1122}
1123
1124void
1126 RxSignalInfo rxSignalInfo,
1127 const WifiTxVector& txVector,
1128 bool inAmpdu)
1129{
1130 // The received MPDU is either broadcast or addressed to this station
1131 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
1132
1133 const auto& hdr = mpdu->GetHeader();
1134
1135 if (m_apMac)
1136 {
1137 // if the AP MLD received an MPDU from an EMLSR client that is starting an UL TXOP,
1138 // block transmissions to the EMLSR client on other links
1139 CheckEmlsrClientStartingTxop(hdr, txVector);
1140 }
1141
1142 bool icfReceived = false;
1143
1144 if (hdr.IsTrigger())
1145 {
1146 if (!m_staMac)
1147 {
1148 return; // Trigger Frames are only processed by STAs
1149 }
1150
1151 CtrlTriggerHeader trigger;
1152 mpdu->GetPacket()->PeekHeader(trigger);
1153
1154 if (hdr.GetAddr1() != m_self &&
1155 (!hdr.GetAddr1().IsBroadcast() || !m_staMac->IsAssociated() ||
1156 hdr.GetAddr2() != m_bssid // not sent by the AP this STA is associated with
1157 || trigger.FindUserInfoWithAid(m_staMac->GetAssociationId()) == trigger.end()))
1158 {
1159 return; // not addressed to us
1160 }
1161
1162 if (trigger.IsMuRts() && m_staMac->IsEmlsrLink(m_linkId))
1163 {
1164 // this is an initial Control frame
1165 if (DropReceivedIcf())
1166 {
1167 return;
1168 }
1169
1170 auto emlsrManager = m_staMac->GetEmlsrManager();
1171 NS_ASSERT(emlsrManager);
1172
1173 icfReceived = true;
1174
1175 // we just got involved in a DL TXOP. Check if we are still involved in the TXOP in a
1176 // SIFS (we are expected to reply by sending a CTS frame)
1178 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + m_phy->GetSifs()).As(Time::S));
1181 this,
1182 hdr.GetAddr2());
1183 }
1184 }
1185
1186 // We impose that an aux PHY is only able to receive an ICF, a CTS or a management frame
1187 // (we are interested in receiving mainly Beacon frames). Note that other frames are still
1188 // post-processed, e.g., used to set the NAV and the TXOP holder.
1189 // The motivation is that, e.g., an AP MLD may send an ICF to EMLSR clients A and B;
1190 // A responds while B does not; the AP MLD sends a DL MU PPDU to both clients followed
1191 // by an MU-BAR to solicit a BlockAck from both clients. If an aux PHY of client B is
1192 // operating on this link, the MU-BAR will be received and a TB PPDU response sent
1193 // through the aux PHY.
1194 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1195 m_mac->GetLinkForPhy(m_staMac->GetEmlsrManager()->GetMainPhyId()) != m_linkId &&
1196 !icfReceived && !mpdu->GetHeader().IsCts() && !mpdu->GetHeader().IsMgt())
1197 {
1198 NS_LOG_DEBUG("Dropping " << *mpdu << " received by an aux PHY on link " << +m_linkId);
1199 return;
1200 }
1201
1202 HeFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1203
1204 if (icfReceived)
1205 {
1206 m_staMac->GetEmlsrManager()->NotifyIcfReceived(m_linkId);
1207 }
1208}
1209
1210bool
1212{
1213 NS_LOG_FUNCTION(this);
1214
1215 auto emlsrManager = m_staMac->GetEmlsrManager();
1216 NS_ASSERT(emlsrManager);
1217
1218 if (UsingOtherEmlsrLink())
1219 {
1220 // we received an ICF on a link that is blocked because another EMLSR link is
1221 // being used. Check if there is an ongoing DL TXOP on the other EMLSR link
1222 auto apMldAddress = GetWifiRemoteStationManager()->GetMldAddress(m_bssid);
1223 NS_ASSERT_MSG(apMldAddress, "MLD address not found for " << m_bssid);
1224
1225 if (auto it = std::find_if(
1226 m_staMac->GetLinkIds().cbegin(),
1227 m_staMac->GetLinkIds().cend(),
1228 /* lambda to find an EMLSR link on which there is an ongoing DL TXOP */
1229 [=, this](uint8_t linkId) {
1230 auto ehtFem =
1231 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
1232 return linkId != m_linkId && m_staMac->IsEmlsrLink(linkId) &&
1233 ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
1234 m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(
1235 *ehtFem->m_txopHolder) == apMldAddress;
1236 });
1237 it != m_staMac->GetLinkIds().cend())
1238 {
1239 // AP is not expected to send ICFs on two links. If an ICF
1240 // has been received on this link, it means that the DL TXOP
1241 // on the other link terminated (e.g., the AP did not
1242 // receive our response)
1243 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(*it))
1244 ->m_ongoingTxopEnd.Cancel();
1245 // we are going to start a TXOP on this link; unblock
1246 // transmissions on this link, the other links will be
1247 // blocked subsequently
1249 }
1250 else
1251 {
1252 // We get here likely because transmission on the other EMLSR link
1253 // started before the reception of the ICF ended. We drop this ICF and let the
1254 // UL TXOP continue.
1255 NS_LOG_DEBUG("Drop ICF because another EMLSR link is being used");
1257 return true;
1258 }
1259 }
1260 /**
1261 * It might happen that, while the aux PHY is receiving an ICF, the main PHY is
1262 * completing a TXOP on another link or is returning to the primary link after a TXOP
1263 * is completed on another link. In order to respond to the ICF, it is necessary that
1264 * the main PHY has enough time to switch and be ready to operate on this link by the
1265 * end of the ICF padding.
1266 *
1267 * TXOP end
1268 * │
1269 * ┌───┐ another
1270 * AP MLD │ACK│ link
1271 * ───────────┬─────────┬┴───┴───────────────────────────────────────
1272 * EMLSR │ QoS │ │ main PHY
1273 * client │ Data │ │
1274 * └─────────┘ │
1275 * ┌─────┬───┐ this
1276 * AP MLD │ ICF │pad│ link
1277 * ────────────────────┴─────┴───┴───────────────────────────────────
1278 * aux PHY
1279 */
1280 else if (auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId());
1281 mainPhy != m_phy)
1282 {
1283 const auto delay = mainPhy->GetChannelSwitchDelay();
1284 auto lastTime = mainPhy->GetState()->GetLastTime({WifiPhyState::TX});
1285 auto reason = WifiIcfDrop::NOT_ENOUGH_TIME_TX;
1286
1287 if (auto lastSwitch = mainPhy->GetState()->GetLastTime({WifiPhyState::SWITCHING});
1288 lastSwitch > lastTime)
1289 {
1290 lastTime = lastSwitch;
1292 }
1293 if (auto lastSleep = mainPhy->GetState()->GetLastTime({WifiPhyState::SLEEP});
1294 lastSleep > lastTime)
1295 {
1296 lastTime = lastSleep;
1298 }
1299 // ignore RX state for now
1300
1301 if (lastTime > Simulator::Now() - delay)
1302 {
1304 "Drop ICF due to not enough time for the main PHY to switch link; reason = "
1305 << reason);
1306 m_icfDropCallback(reason, m_linkId);
1307 return true;
1308 }
1309 }
1310 return false;
1311}
1312
1313void
1314EhtFrameExchangeManager::TxopEnd(const std::optional<Mac48Address>& txopHolder)
1315{
1316 NS_LOG_FUNCTION(this << txopHolder.has_value());
1317
1319 {
1320 // we may get here because the PHY has not issued the PHY-RXSTART.indication before
1321 // the expiration of the timer started to detect new received frames, but the PHY is
1322 // currently decoding the PHY header of a PPDU, so let's wait some more time to check
1323 // if we receive a PHY-RXSTART.indication when the PHY is done decoding the PHY header
1324 NS_LOG_DEBUG("PHY is decoding the PHY header of PPDU, postpone TXOP end");
1327 this,
1328 txopHolder);
1329 return;
1330 }
1331
1332 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
1333 {
1334 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
1335 }
1336 else if (m_apMac && txopHolder && GetWifiRemoteStationManager()->GetEmlsrEnabled(*txopHolder))
1337 {
1338 // EMLSR client terminated its TXOP and is back to listening operation
1339 EmlsrSwitchToListening(*txopHolder, Seconds(0));
1340 }
1341}
1342
1343void
1345{
1346 NS_LOG_FUNCTION(this << txDuration.As(Time::MS) << durationId.As(Time::US));
1347
1349 {
1350 // nothing to do
1351 return;
1352 }
1353
1355 Time delay;
1356
1357 if (m_txTimer.IsRunning())
1358 {
1359 // the TX timer is running, hence we are expecting a response. Postpone the TXOP end
1360 // to match the TX timer (which is long enough to get the PHY-RXSTART.indication for
1361 // the response)
1362 delay = m_txTimer.GetDelayLeft();
1363 }
1364 else if (durationId <= m_phy->GetSifs())
1365 {
1366 // the TX timer is not running, hence no response is expected, and the Duration/ID value
1367 // is less than or equal to a SIFS; the TXOP will end after this transmission
1368 NS_LOG_DEBUG("Assume TXOP will end based on Duration/ID value");
1369 delay = txDuration;
1370 }
1371 else
1372 {
1373 // the TX Timer is not running, hence no response is expected (e.g., we are
1374 // transmitting a CTS after ICS). The TXOP holder may transmit a frame a SIFS
1375 // after the end of this PPDU, hence we need to postpone the TXOP end in order to
1376 // get the PHY-RXSTART.indication
1377 delay = txDuration + m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY;
1378 }
1379
1380 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
1383}
1384
1385void
1387{
1388 NS_LOG_FUNCTION(this << psduDuration.As(Time::MS));
1389
1390 if (!m_ongoingTxopEnd.IsPending() || !psduDuration.IsStrictlyPositive())
1391 {
1392 // nothing to do
1393 return;
1394 }
1395
1396 // postpone the TXOP end until after the reception of the PSDU is completed
1398
1399 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + psduDuration).As(Time::S));
1402 this,
1403 m_txopHolder);
1404}
1405
1406void
1408{
1409 NS_LOG_FUNCTION(this << durationId.As(Time::US));
1410
1412 {
1413 // nothing to do
1414 return;
1415 }
1416
1418
1419 // if the Duration/ID of the received frame is less than a SIFS, the TXOP
1420 // is terminated
1421 if (durationId <= m_phy->GetSifs())
1422 {
1423 NS_LOG_DEBUG("Assume TXOP ended based on Duration/ID value");
1425 return;
1426 }
1427
1428 // we may send a response after a SIFS or we may receive another frame after a SIFS.
1429 // Postpone the TXOP end by considering the latter (which takes longer)
1430 auto delay = m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY;
1431 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
1434}
1435
1436} // namespace ns3
a polymophic address class
Definition address.h:90
Headers for BlockAck response.
std::vector< uint32_t > FindPerAidTidInfoWithAid(uint16_t aid) const
For Multi-STA Block Acks, get the indices of the Per AID TID Info subfields carrying the given AID in...
bool IsMultiSta() const
Check if the BlockAck frame variant is Multi-STA Block Ack.
Headers for Trigger frames.
ConstIterator end() const
Get a const iterator indicating past-the-last User Info field in the list.
bool IsMuRts() const
Check if this is a MU-RTS Trigger frame.
ConstIterator FindUserInfoWithAid(ConstIterator start, uint16_t aid12) const
Get a const iterator pointing to the first User Info field found (starting from the one pointed to by...
EhtFrameExchangeManager handles the frame exchange sequences for EHT stations.
void PostProcessFrame(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector) override
Perform actions that are possibly needed after receiving any frame, independently of whether the fram...
void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector &txVector) override
Forward a map of PSDUs down to the PHY layer.
TracedCallback< WifiIcfDrop, uint8_t > m_icfDropCallback
ICF drop reason traced callback (WifiMac exposes this trace source)
void PreProcessFrame(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector) override
Perform actions that are possibly needed when receiving any frame, independently of whether the frame...
void NavResetTimeout() override
Reset the NAV upon expiration of the NAV reset timer.
void ForwardPsduDown(Ptr< const WifiPsdu > psdu, WifiTxVector &txVector) override
Forward a PSDU down to the PHY layer.
void PsduRxError(Ptr< const WifiPsdu > psdu) override
This method is called when the reception of a PSDU fails.
void SendEmlOmn(const Mac48Address &dest, const MgtEmlOmn &frame)
Send an EML Operating Mode Notification frame to the given station.
Ptr< WifiMpdu > CreateAliasIfNeeded(Ptr< WifiMpdu > mpdu) const override
Create an alias of the given MPDU for transmission by this Frame Exchange Manager.
void TransmissionFailed() override
Take necessary actions upon a transmission failure.
void IntraBssNavResetTimeout() override
Reset the intra-BSS NAV upon expiration of the intra-BSS NAV reset timer.
void SendMuRts(const WifiTxParameters &txParams) override
Send an MU-RTS to begin an MU-RTS/CTS frame exchange protecting an MU PPDU.
void SendCtsAfterMuRts(const WifiMacHeader &muRtsHdr, const CtrlTriggerHeader &trigger, double muRtsSnr) override
Send CTS after receiving an MU-RTS.
std::optional< dBm_u > GetMostRecentRssi(const Mac48Address &address) const override
Get the RSSI of the most recent packet received from the station having the given address.
void UpdateTxopEndOnRxEnd(Time durationId)
Update the TXOP end timer when a frame reception ends.
bool CheckEmlsrClientStartingTxop(const WifiMacHeader &hdr, const WifiTxVector &txVector)
Check if the frame received (or being received) is sent by an EMLSR client to start an UL TXOP.
void TransmissionSucceeded() override
Take necessary actions upon a transmission success.
bool GetEmlsrSwitchToListening(Ptr< const WifiPsdu > psdu, uint16_t aid, const Mac48Address &address) const
static TypeId GetTypeId()
Get the type ID.
void DoDispose() override
Destructor implementation.
std::unordered_map< Mac48Address, EventId, WifiAddressHash > m_transDelayTimer
MLD address-indexed map of transition delay timers.
void NotifyChannelReleased(Ptr< Txop > txop) override
Notify the given Txop that channel has been released.
EventId m_ongoingTxopEnd
event indicating the possible end of the current TXOP (of which we are not the holder)
void RxStartIndication(WifiTxVector txVector, Time psduDuration) override
void ReceiveMpdu(Ptr< const WifiMpdu > mpdu, RxSignalInfo rxSignalInfo, const WifiTxVector &txVector, bool inAmpdu) override
This method handles the reception of an MPDU (possibly included in an A-MPDU)
void UpdateTxopEndOnTxStart(Time txDuration, Time durationId)
Update the TXOP end timer when starting a frame transmission.
void GenerateInDeviceInterference(uint8_t linkId, Time duration, Watt_u txPower)
Generate an in-device interference of the given power on the given link for the given duration.
void SendCtsAfterRts(const WifiMacHeader &rtsHdr, WifiMode rtsTxMode, double rtsSnr) override
Send CTS after receiving RTS.
void UpdateTxopEndOnRxStartIndication(Time psduDuration)
Update the TXOP end timer when receiving a PHY-RXSTART.indication.
void EmlsrSwitchToListening(const Mac48Address &address, const Time &delay)
This method is intended to be called when an AP MLD detects that an EMLSR client previously involved ...
void TxopEnd(const std::optional< Mac48Address > &txopHolder)
Take actions when a TXOP (of which we are not the holder) ends.
void CtsAfterMuRtsTimeout(Ptr< WifiMpdu > muRts, const WifiTxVector &txVector) override
Called when no CTS frame is received after an MU-RTS.
void NotifySwitchingEmlsrLink(Ptr< WifiPhy > phy, uint8_t linkId, Time delay)
Notify that the given PHY will switch channel to operate on another EMLSR link after the given delay.
bool StartTransmission(Ptr< Txop > edca, MHz_u allowedWidth) override
Request the FrameExchangeManager to start a frame exchange sequence.
void SetLinkId(uint8_t linkId) override
Set the ID of the link this Frame Exchange Manager is associated with.
An identifier for simulation events.
Definition event-id.h:45
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition event-id.cc:44
bool IsPending() const
This method is syntactic sugar for !IsExpired().
Definition event-id.cc:65
std::set< Mac48Address > m_sentRtsTo
the STA(s) which we sent an RTS to (waiting for CTS)
uint8_t m_linkId
the ID of the link this object is associated with
Ptr< WifiMac > m_mac
the MAC layer on this station
virtual void ResetPhy()
Remove WifiPhy associated with this FrameExchangeManager.
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager() const
Ptr< MacTxMiddle > m_txMiddle
the MAC TX Middle on this station
Mac48Address m_self
the MAC address of this device
virtual void TransmissionFailed()
Take necessary actions upon a transmission failure.
WifiTxTimer m_txTimer
the timer set upon frame transmission
virtual void SendCtsAfterRts(const WifiMacHeader &rtsHdr, WifiMode rtsTxMode, double rtsSnr)
Send CTS after receiving RTS.
std::set< Mac48Address > m_protectedStas
STAs that have replied to an RTS in this TXOP.
Mac48Address GetAddress() const
Get the MAC address.
virtual void SetLinkId(uint8_t linkId)
Set the ID of the link this Frame Exchange Manager is associated with.
virtual void NotifyChannelReleased(Ptr< Txop > txop)
Notify the given Txop that channel has been released.
Ptr< WifiAckManager > GetAckManager() const
Get the Acknowledgment Manager used by this node.
Ptr< WifiProtectionManager > GetProtectionManager() const
Get the Protection Manager used by this node.
Ptr< WifiPhy > m_phy
the PHY layer on this station
virtual void PreProcessFrame(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector)
Perform actions that are possibly needed when receiving any frame, independently of whether the frame...
Mac48Address m_bssid
BSSID address (Mac48Address)
virtual bool StartTransmission(Ptr< Txop > dcf, MHz_u allowedWidth)
Request the FrameExchangeManager to start a frame exchange sequence.
MHz_u m_allowedWidth
the allowed width for the current transmission
HeFrameExchangeManager handles the frame exchange sequences for HE stations.
Ptr< ApWifiMac > m_apMac
MAC pointer (null if not an AP)
void DoDispose() override
Destructor implementation.
virtual void IntraBssNavResetTimeout()
Reset the intra-BSS NAV upon expiration of the intra-BSS NAV reset timer.
virtual void SendMuRts(const WifiTxParameters &txParams)
Send an MU-RTS to begin an MU-RTS/CTS frame exchange protecting an MU PPDU.
void RxStartIndication(WifiTxVector txVector, Time psduDuration) override
void DoCtsAfterMuRtsTimeout(Ptr< WifiMpdu > muRts, const WifiTxVector &txVector, bool updateFailedCw)
Called when no CTS frame is received after an MU-RTS.
std::optional< Mac48Address > FindTxopHolder(const WifiMacHeader &hdr, const WifiTxVector &txVector) override
Determine the holder of the TXOP, if possible, based on the received frame.
void NavResetTimeout() override
Reset the NAV upon expiration of the NAV reset timer.
virtual void SendCtsAfterMuRts(const WifiMacHeader &muRtsHdr, const CtrlTriggerHeader &trigger, double muRtsSnr)
Send CTS after receiving an MU-RTS.
Ptr< StaWifiMac > m_staMac
MAC pointer (null if not a STA)
void ReceiveMpdu(Ptr< const WifiMpdu > mpdu, RxSignalInfo rxSignalInfo, const WifiTxVector &txVector, bool inAmpdu) override
This method handles the reception of an MPDU (possibly included in an A-MPDU)
virtual void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector &txVector)
Forward a map of PSDUs down to the PHY layer.
void PostProcessFrame(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector) override
Perform actions that are possibly needed after receiving any frame, independently of whether the fram...
virtual std::optional< dBm_u > GetMostRecentRssi(const Mac48Address &address) const
Get the RSSI of the most recent packet received from the station having the given address.
void TransmissionSucceeded() override
Take necessary actions upon a transmission success.
Ptr< MpduAggregator > m_mpduAggregator
A-MPDU aggregator.
virtual void ForwardPsduDown(Ptr< const WifiPsdu > psdu, WifiTxVector &txVector)
Forward a PSDU down to the PHY layer.
Ptr< MsduAggregator > m_msduAggregator
A-MSDU aggregator.
an EUI-48 address
Implement the header for Action frames of type EML Operating Mode Notification.
Smart pointer class similar to boost::intrusive_ptr.
std::optional< Mac48Address > m_txopHolder
MAC address of the TXOP holder.
virtual Ptr< WifiMpdu > CreateAliasIfNeeded(Ptr< WifiMpdu > mpdu) const
Create an alias of the given MPDU for transmission by this Frame Exchange Manager.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:560
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition simulator.h:594
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:404
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:340
@ US
microsecond
Definition nstime.h:107
@ MS
millisecond
Definition nstime.h:106
@ S
second
Definition nstime.h:105
bool IsZero() const
Exactly equivalent to t == 0.
Definition nstime.h:304
void StartAccessAfterEvent(uint8_t linkId, bool hadFramesToTransmit, bool checkMediumBusy)
Request channel access on the given link after the occurrence of an event that possibly requires to g...
Definition txop.cc:682
static constexpr bool DIDNT_HAVE_FRAMES_TO_TRANSMIT
no packet available for transmission was in the queue
Definition txop.h:390
static constexpr bool CHECK_MEDIUM_BUSY
generation of backoff (also) depends on the busy/idle state of the medium
Definition txop.h:392
static constexpr bool DONT_CHECK_MEDIUM_BUSY
generation of backoff is independent of the busy/idle state of the medium
Definition txop.h:394
a unique identifier for an interface.
Definition type-id.h:48
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
See IEEE 802.11 chapter 7.3.1.11 Header format: | category: 1 | action value: 1 |.
void SetAction(CategoryValue type, ActionValue action)
Set action for this Action header.
Implements the IEEE 802.11 MAC header.
void SetSequenceNumber(uint16_t seq)
Set the sequence number of the header.
void SetDsNotFrom()
Un-set the From DS bit in the Frame Control field.
void SetAddr1(Mac48Address address)
Fill the Address 1 field with the given address.
virtual void SetType(WifiMacType type, bool resetToDsFromDs=true)
Set Type/Subtype values with the correct values depending on the given type.
Mac48Address GetAddr2() const
Return the address in the Address 2 field.
void SetAddr2(Mac48Address address)
Fill the Address 2 field with the given address.
void SetAddr3(Mac48Address address)
Fill the Address 3 field with the given address.
void SetDsNotTo()
Un-set the To DS bit in the Frame Control field.
represent a single transmission mode
Definition wifi-mode.h:40
Time GetSlot() const
Return the slot duration for this PHY.
Definition wifi-phy.cc:835
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition wifi-phy.cc:823
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1572
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition wifi-phy.cc:1063
bool IsReceivingPhyHeader() const
Definition wifi-phy.cc:1986
Ptr< PhyEntity > GetPhyEntity(WifiModulationClass modulation) const
Get the supported PHY entity corresponding to the modulation class.
Definition wifi-phy.cc:754
This class stores the TX parameters (TX vector, protection mechanism, acknowledgment mechanism,...
std::unique_ptr< WifiProtection > m_protection
protection method
bool IsRunning() const
Return true if the timer is running.
const std::set< Mac48Address > & GetStasExpectedToRespond() const
Time GetDelayLeft() const
Get the remaining time until the timer will expire.
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
WifiPreamble GetPreambleType() const
void SetSigBMode(const WifiMode &mode)
Set the MCS used for SIG-B.
uint8_t GetTxPowerLevel() const
Declaration of ns3::EhtPhy class.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#define NS_LOG_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:436
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1332
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1344
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1308
@ WIFI_PREAMBLE_EHT_MU
@ WIFI_MOD_CLASS_EHT
EHT (Clause 36)
@ AC_BE
Best Effort.
Definition qos-utils.h:64
@ AC_VO
Voice.
Definition qos-utils.h:70
Every class exported by the ns3 library is enclosed in the ns3 namespace.
U * PeekPointer(const Ptr< U > &p)
Definition ptr.h:443
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
std:: tuple< WifiContainerQueueType, WifiReceiverAddressType, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, receiver address type, Address, TID) identifying a container queue.
static constexpr uint8_t WAIT_FOR_RXSTART_DELAY_USEC
Additional time (exceeding 20 us) to wait for a PHY-RXSTART.indication when the PHY is decoding a PHY...
@ SWITCHING
The PHY layer is switching to other channel.
@ TX
The PHY layer is sending a packet.
@ SLEEP
The PHY layer is sleeping.
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:580
@ WIFI_MAC_MGT_ACTION
Watt_u DbmToW(dBm_u val)
Convert from dBm to Watts.
Definition wifi-utils.cc:31
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:587
const Time EMLSR_RX_PHY_START_DELAY
aRxPHYStartDelay value to use when waiting for a new frame in the context of EMLSR operations (Sec.
static Time DecodeEmlsrTransitionDelay(uint8_t value)
RxSignalInfo structure containing info on the received signal.
Definition wifi-types.h:72
WifiMuRtsCtsProtection specifies that MU-RTS/CTS protection method is used.
typedef for union of different ActionValues