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
33/**
34 * Additional time (exceeding 20 us) to wait for a PHY-RXSTART.indication when the PHY is
35 * decoding a PHY header.
36 *
37 * Values for aRxPHYStartDelay:
38 * - OFDM : 20 us (for 20 MHz) [Table 17-21 of 802.11-2020]
39 * - ERP-OFDM : 20 us [Table 18-5 of 802.11-2020]
40 * - HT : 28 us (HT-mixed), 24 us (HT-greenfield) [Table 19-25 of 802.11-2020]
41 * - VHT : 36 + 4 * max N_VHT-LTF + 4 = 72 us [Table 21-28 of 802.11-2020]
42 * - HE : 32 us (for HE SU and HE TB PPDUs)
43 * 32 + 4 * N_HE-SIG-B us (for HE MU PPDUs) [Table 27-54 of 802.11ax-2021]
44 * - EHT : 32 us (for EHT TB PPDUs)
45 * 32 + 4 * N_EHT-SIG us (for EHT MU PPDUs) [Table 36-70 of 802.11be D3.2]
46 */
47static constexpr uint8_t WAIT_FOR_RXSTART_DELAY_USEC = 52;
48
49NS_LOG_COMPONENT_DEFINE("EhtFrameExchangeManager");
50
52
55{
56 static TypeId tid = TypeId("ns3::EhtFrameExchangeManager")
58 .AddConstructor<EhtFrameExchangeManager>()
59 .SetGroupName("Wifi");
60 return tid;
61}
62
67
72
73void
80
81void
83{
84 NS_LOG_FUNCTION(this << txVector << psduDuration.As(Time::MS));
85
86 HeFrameExchangeManager::RxStartIndication(txVector, psduDuration);
88}
89
90void
92{
93 if (auto protectionManager = GetProtectionManager())
94 {
95 protectionManager->SetLinkId(linkId);
96 }
97 if (auto ackManager = GetAckManager())
98 {
99 ackManager->SetLinkId(linkId);
100 }
101 m_msduAggregator->SetLinkId(linkId);
102 m_mpduAggregator->SetLinkId(linkId);
104}
105
108{
109 NS_LOG_FUNCTION(this << *mpdu);
110
111 // alias needs only be created for non-broadcast QoS data frames exchanged between two MLDs
112 if (auto staMac = DynamicCast<StaWifiMac>(m_mac);
113 !mpdu->GetHeader().IsQosData() ||
114 (staMac ? (staMac->GetAssocType() == WifiAssocType::LEGACY) : (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 return m_staMac->GetMacQueueScheduler()->GetAllQueuesBlockedOnLink(
159 m_linkId,
161}
162
163bool
165{
166 NS_LOG_FUNCTION(this << edca << allowedWidth);
167
168 m_allowedWidth = allowedWidth;
169
170 if (m_apMac)
171 {
172 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
173 {
174 if (linkId == m_linkId)
175 {
176 continue;
177 }
178
179 // EMLSR clients involved in a DL or UL TXOP on another link
180 std::set<Mac48Address> emlsrClients;
181 auto ehtFem =
182 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
183
184 // check if an EMLSR client is the holder of an UL TXOP on the other link
185 if (ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
186 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(
187 ehtFem->m_txopHolder.value()))
188 {
189 NS_LOG_DEBUG("Involved in UL TXOP: " << ehtFem->m_txopHolder.value());
190 emlsrClients.insert(ehtFem->m_txopHolder.value());
191 }
192
193 // check if EMLSR clients are involved in a DL TXOP on another link
194 for (const auto& address : ehtFem->m_protectedStas)
195 {
196 if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(address))
197 {
198 NS_LOG_DEBUG("Involved in DL TXOP: " << address);
199 emlsrClients.insert(address);
200 }
201 }
202
203 for (const auto& address : emlsrClients)
204 {
205 auto mldAddress =
206 m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(address);
207 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
208
209 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(*mldAddress))
210 {
211 // EMLSR client did not enable EMLSR mode on this link, we can transmit to it
212 continue;
213 }
214
215 // check that this link is blocked as expected
218 *mldAddress,
219 0);
220 auto mask =
221 m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, m_linkId);
222 NS_ASSERT_MSG(mask,
223 "No mask for client " << *mldAddress << " on link " << +m_linkId);
224 if (!mask->test(
225 static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK)))
226 {
227 NS_ASSERT_MSG(false,
228 "Transmissions to " << *mldAddress << " on link " << +m_linkId
229 << " are not blocked");
230 // in case asserts are disabled, block transmissions on the other links because
231 // this is what we need
233 *mldAddress,
234 {m_linkId});
235 }
236 }
237 }
238 }
239
240 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
241 {
242 // Cannot start a transmission on a link blocked because another EMLSR link is being used
244 {
245 NS_LOG_DEBUG("StartTransmission called while another EMLSR link is being used");
247 return false;
248 }
249
250 auto emlsrManager = m_staMac->GetEmlsrManager();
251
252 if (auto elapsed = emlsrManager->GetElapsedMediumSyncDelayTimer(m_linkId);
253 elapsed && emlsrManager->MediumSyncDelayNTxopsExceeded(m_linkId))
254 {
255 NS_LOG_DEBUG("No new TXOP attempts allowed while MediumSyncDelay is running");
256 // request channel access if needed when the MediumSyncDelay timer expires; in the
257 // meantime no queued packet can be transmitted
259 emlsrManager->GetMediumSyncDuration() - *elapsed,
261 edca,
262 m_linkId,
263 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT, // queued frames cannot be transmitted until
264 // MSD expires
265 Txop::DONT_CHECK_MEDIUM_BUSY); // generate backoff regardless of medium busy
267 return false;
268 }
269
270 if (!m_phy)
271 {
272 NS_LOG_DEBUG("No PHY is currently operating on EMLSR link " << +m_linkId);
274 return false;
275 }
276
277 // let EMLSR manager decide whether to prevent or allow this UL TXOP
278 if (const auto [startTxop, delay] = emlsrManager->GetDelayUntilAccessRequest(
279 m_linkId,
280 DynamicCast<QosTxop>(edca)->GetAccessCategory());
281 !startTxop)
282
283 {
284 if (delay.IsStrictlyPositive())
285 {
288 delay,
290 edca,
291 m_linkId,
292 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT, // queued frames cannot be
293 // transmitted until RX ends
294 Txop::CHECK_MEDIUM_BUSY); // generate backoff if medium busy
295 }
296 return false;
297 }
298 }
299
300 auto started = HeFrameExchangeManager::StartTransmission(edca, allowedWidth);
301
302 if (started && m_staMac && m_staMac->IsEmlsrLink(m_linkId))
303 {
304 // notify the EMLSR Manager of the UL TXOP start on an EMLSR link
305 NS_ASSERT(m_staMac->GetEmlsrManager());
306 m_staMac->GetEmlsrManager()->NotifyUlTxopStart(m_linkId);
307 }
308
309 if (started)
310 {
311 // we are starting a new TXOP, hence consider the previous ongoing TXOP as terminated
313 }
314
315 return started;
316}
317
318void
320{
321 NS_LOG_FUNCTION(this);
322
323 if (m_staMac && m_staMac->GetEmlsrManager())
324 {
325 m_staMac->GetEmlsrManager()->NotifyProtectionCompleted(m_linkId);
326 }
327
329}
330
331void
333{
334 NS_LOG_FUNCTION(this << psdu << txVector);
335
336 // EHT-SIG, the equivalent of HE-SIG-B, is present in EHT SU transmissions, too
337 if (txVector.GetPreambleType() == WIFI_PREAMBLE_EHT_MU)
338 {
340 auto sigBMode = phy->GetSigBMode(txVector);
341 txVector.SetSigBMode(sigBMode);
342 }
343
344 auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
345
346 if (m_apMac && psdu->GetHeader(0).IsTrigger())
347 {
348 for (const auto& client : m_sentRtsTo)
349 {
350 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(client))
351 {
352 continue;
353 }
354 auto clientMld = GetWifiRemoteStationManager()->GetMldAddress(client);
355 NS_ASSERT(clientMld);
356
357 // block transmissions on the other EMLSR links of the EMLSR clients
358 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
359 {
360 if (linkId != m_linkId &&
361 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*clientMld))
362 {
364 *clientMld,
365 {linkId});
366 }
367 }
368 }
369 }
370
371 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) && psdu->GetAddr1() == m_bssid &&
372 psdu->GetHeader(0).IsRts())
373 {
374 NS_ASSERT(m_staMac->GetEmlsrManager());
375 m_staMac->GetEmlsrManager()->NotifyRtsSent(m_linkId, psdu, txVector);
376 }
377
379 UpdateTxopEndOnTxStart(txDuration, psdu->GetDuration());
380
381 if (m_apMac && m_apMac->GetApEmlsrManager())
382 {
383 auto delay = m_apMac->GetApEmlsrManager()->GetDelayOnTxPsduNotForEmlsr(psdu,
384 txVector,
385 m_phy->GetPhyBand());
386
387 // check if the EMLSR clients shall switch back to listening operation
388 for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
389 {
390 auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt);
391
392 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) &&
393 GetEmlsrSwitchToListening(psdu, aid, *clientIt))
394 {
395 EmlsrSwitchToListening(*clientIt, delay);
396 // this client is no longer involved in the current TXOP
397 clientIt = m_protectedStas.erase(clientIt);
398 }
399 else
400 {
401 clientIt++;
402 }
403 }
404 }
405 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
406 m_staMac->GetEmlsrManager()->GetInDeviceInterference())
407 {
408 NS_ASSERT(m_staMac->GetEmlsrManager());
409 m_staMac->GetEmlsrManager()->NotifyInDeviceInterferenceStart(m_linkId, txDuration);
410 GenerateInDeviceInterferenceForAll(txDuration, txVector);
411 }
412}
413
414void
416{
417 NS_LOG_FUNCTION(this << psduMap << txVector);
418
419 auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand());
420
422 UpdateTxopEndOnTxStart(txDuration, psduMap.begin()->second->GetDuration());
423
424 if (m_apMac)
425 {
426 // check if this is a BSRP TF used as ICF for some EMLSR client
427 if (IsTrigger(psduMap))
428 {
429 CtrlTriggerHeader trigger;
430 psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
431
432 if (trigger.IsBsrp())
433 {
434 auto recipients = GetTfRecipients(trigger);
435 for (const auto& client : recipients)
436 {
437 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(client))
438 {
439 continue;
440 }
441 auto clientMld = GetWifiRemoteStationManager()->GetMldAddress(client);
442 NS_ASSERT(clientMld);
443
444 // block transmissions on the other EMLSR links of the EMLSR clients
445 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
446 {
447 if (linkId != m_linkId &&
448 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*clientMld))
449 {
450 m_mac->BlockUnicastTxOnLinks(
452 *clientMld,
453 {linkId});
454 }
455 }
456 }
457 }
458 }
459
460 // check if the EMLSR clients shall switch back to listening operation at the end of this
461 // PPDU
462 for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
463 {
464 auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt);
465 const auto psduMapIt = psduMap.find(aid);
466 const auto aidNotFoundAndNotTf = (psduMapIt == psduMap.cend()) && !IsTrigger(psduMap);
467 // the PSDU to process: the one addressed to the given AID (if any) or the unique one
468 const auto psdu = (psduMapIt != psduMap.cend() ? psduMapIt : psduMap.cbegin())->second;
469
470 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) &&
471 (aidNotFoundAndNotTf || GetEmlsrSwitchToListening(psdu, aid, *clientIt)))
472 {
473 EmlsrSwitchToListening(*clientIt, txDuration);
474 // this client is no longer involved in the current TXOP
475 clientIt = m_protectedStas.erase(clientIt);
476 }
477 else
478 {
479 clientIt++;
480 }
481 }
482 }
483 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
484 m_staMac->GetEmlsrManager()->GetInDeviceInterference())
485 {
486 NS_ASSERT(m_staMac->GetEmlsrManager());
487 m_staMac->GetEmlsrManager()->NotifyInDeviceInterferenceStart(m_linkId, txDuration);
488 GenerateInDeviceInterferenceForAll(txDuration, txVector);
489 }
490}
491
492void
494 const WifiTxVector& txVector)
495{
496 NS_LOG_FUNCTION(this << txDuration.As(Time::MS) << txVector);
497
499 NS_ASSERT(m_staMac->GetEmlsrManager());
500
501 for (const auto& phy : m_staMac->GetDevice()->GetPhys())
502 {
503 // generate in-device interference for a PHY provided that:
504 // - the PHY is not the one the client is using to transmit
505 // - either the PHY is not operating on any link or it is operating on an EMLSR link
506 // Interference is generated for the duration of this transmission
507 if (auto id = m_staMac->GetLinkForPhy(phy);
508 phy != m_phy && (!id || m_staMac->IsEmlsrLink(*id)))
509 {
510 const auto txPower = phy->GetPower(txVector.GetTxPowerLevel()) + phy->GetTxGain();
511 GenerateInDeviceInterference(phy, txDuration, DbmToW(txPower));
512 }
513 }
514}
515
516void
518 Time duration,
519 Watt_u txPower)
520{
521 NS_LOG_FUNCTION(this << phy << duration.As(Time::US) << txPower);
522
523 auto rxPhy = DynamicCast<SpectrumWifiPhy>(phy);
524
525 if (!rxPhy)
526 {
527 NS_LOG_DEBUG("No spectrum PHY");
528 return;
529 }
530
532 NS_ASSERT(txPhy);
533
534 for (const auto& [range, interface] : rxPhy->GetSpectrumPhyInterfaces())
535 {
536 if (!interface->GetRxSpectrumModel())
537 {
538 // we may have created a PHY interface but never set a frequency channel comprised
539 // in the frequency range associated with that PHY interface, thus the RX spectrum
540 // model may have not been created
541 continue;
542 }
543
544 auto psd = Create<SpectrumValue>(interface->GetRxSpectrumModel());
545 *psd = txPower;
546
547 auto spectrumSignalParams = Create<SpectrumSignalParameters>();
548 spectrumSignalParams->duration = duration;
549 spectrumSignalParams->txPhy = txPhy->GetCurrentInterface();
550 spectrumSignalParams->txAntenna = txPhy->GetAntenna();
551 spectrumSignalParams->psd = psd;
552
553 rxPhy->StartRx(spectrumSignalParams, interface);
554 }
555}
556
557void
559{
560 NS_LOG_FUNCTION(this);
562 {
563 // the CTS may have been missed because another EMLSR link is being used; do not reset NAV
564 return;
565 }
567}
568
569void
571{
572 NS_LOG_FUNCTION(this);
574 {
575 // the CTS may have been missed because another EMLSR link is being used; do not reset NAV
576 return;
577 }
579}
580
581bool
583{
584 NS_LOG_FUNCTION(this << address << checkThisLink);
585
586 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
587 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
588 NS_ASSERT_MSG(m_apMac, "This function shall only be called by AP MLDs");
589 std::set<uint8_t> linkIds{m_linkId};
590
591 /**
592 * Do nothing if the EMLSR client is involved in a DL or UL TXOP on another EMLSR link. This
593 * may happen, e.g., when the AP MLD sent an MU-RTS to multiple stations on this link, some of
594 * which responded, but this EMLSR client did not, e.g., because it concurrently started an UL
595 * TXOP on another link. The AP MLD then started a (long) DL MU transmission on this link,
596 * during which the EMLSR client completed the UL TXOP and started being involved in another
597 * DL or UL TXOP (note that DL TXOP is possible because the AP MLD considered the EMLSR client
598 * unprotected as soon as it detected the start of the previous UL TXOP). A Block Ack timeout
599 * for the EMLSR client occurs at the end of the DL MU transmission (which brings us here) and
600 * it may occur while the EMLSR client is still involved in a DL or UL TXOP.
601 *
602 * ┌─────────────┐ ┌───────────────┐
603 * │ MU-RTS to │ │ Data to │ BA timeout for us
604 * │us and others│ │ us and others │ |
605 * ────────┴─────────────┴┬────────┬┴───────────────┴┬───────┬──────────────
606 * [this link] │CTS from│ │BA from│
607 * │ others │ │ others│
608 * └────────┘ └───────┘
609 * ┌───────┐
610 * ┌───┐ ┌──┐ │ MU-RTS│ ┌──────┐
611 * [other link] │CTS│ │BA│ │ to us │ │ Data │
612 * ─────────┬────────┬┴───┴┬────┬┴──┴──────┴───────┴┬───┬┴──────┴┬──┬───────
613 * │ RTS │ │Data│ │CTS│ │BA│
614 * │from us │ └────┘ └───┘ └──┘
615 * └────────┘
616 */
617
618 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
619 {
620 if (!m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
621 {
622 continue; // not an EMLSR link
623 }
624
625 auto ehtFem = StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
626
627 if (ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
628 m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(*ehtFem->m_txopHolder) ==
629 mldAddress)
630 {
631 NS_LOG_DEBUG("EMLSR client " << *mldAddress << " is the holder of an UL TXOP on link "
632 << +linkId << ", do not unblock links");
633 return false;
634 }
635
636 if (linkId == m_linkId && !checkThisLink)
637 {
638 continue;
639 }
640
641 linkIds.insert(linkId);
642
643 if (auto linkAddr =
644 m_apMac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(*mldAddress);
645 linkAddr &&
646 (ehtFem->m_sentRtsTo.contains(*linkAddr) || ehtFem->m_sentFrameTo.contains(*linkAddr) ||
647 ehtFem->m_protectedStas.contains(*linkAddr)))
648 {
649 NS_LOG_DEBUG("EMLSR client " << address
650 << " has been sent an ICF, do not unblock links");
651 return false;
652 }
653 }
654
655 // unblock DL transmissions with reason USING_OTHER_EMLSR_LINK
657 *mldAddress,
658 linkIds);
659 return true;
660}
661
662void
664{
665 NS_LOG_FUNCTION(this << address << delay.As(Time::US));
666
667 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
668 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
669 NS_ASSERT_MSG(m_apMac, "This function shall only be called by AP MLDs");
670
671 auto blockLinks = [=, this](bool checkThisLink) {
672 if (!UnblockEmlsrLinksIfAllowed(address, checkThisLink))
673 {
674 NS_LOG_DEBUG("Could not unblock transmissions to " << address);
675 return;
676 }
677
678 // this EMLSR client switches back to listening operation
679 std::set<uint8_t> linkIds;
680 for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
681 {
682 if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
683 {
684 linkIds.insert(linkId);
685 }
686 }
687
688 // block DL transmissions on this link until transition delay elapses
690 *mldAddress,
691 linkIds);
692
693 auto unblockLinks = [=, this]() {
695 *mldAddress,
696 linkIds);
697 };
698
699 // unblock all EMLSR links when the transition delay elapses
700 auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
701 NS_ASSERT(emlCapabilities);
703 emlCapabilities->get().emlsrTransitionDelay);
704
705 endDelay.IsZero() ? unblockLinks()
706 : static_cast<void>(m_transDelayTimer[*mldAddress] =
707 Simulator::Schedule(endDelay, unblockLinks));
708 };
709
710 // it makes sense to check if the EMLSR client is involved in a DL TXOP on this link only if
711 // the transition delay start is scheduled to start after some delay, because the AP MLD may
712 // start another DL TXOP in the meantime. An example is when the AP MLD terminates a TXOP on
713 // this link due to the remaining TXOP time being not enough to send another frame (not even a
714 // CF-End), delays the start of the transition delay to align with the EMLSR client (which is
715 // waiting for a SIFS + slot + PHY RXSTART delay after the last frame to switch to listening
716 // operations), gains channel access on this link again before starting the transition delay
717 // timer and sends an ICF.
718 delay.IsZero() ? blockLinks(false)
719 : static_cast<void>(Simulator::Schedule(delay, [=]() { blockLinks(true); }));
720}
721
722void
724{
725 NS_LOG_FUNCTION(this << phy << linkId << delay.As(Time::US));
726
727 // TODO Shall we assert that there is no ongoing frame exchange sequence? Or is it possible
728 // that there is an ongoing frame exchange sequence (in such a case, we need to force a
729 // timeout, just like it is done in case of a normal channel switch
730
731 NS_ABORT_MSG_IF(!m_staMac, "This method can only be called on a STA");
732
733 // if we receive the notification from a PHY that is not connected to us, it means that
734 // we have been already connected to another PHY operating on this link, hence we do not
735 // have to reset the connected PHY. Similarly, we do not have to reset the connected PHY if
736 // the link does not change (this occurs when changing the channel width of aux PHYs upon
737 // enabling the EMLSR mode).
738 if (phy == m_phy && linkId != m_linkId)
739 {
740 ResetPhy();
741 }
742 m_staMac->NotifySwitchingEmlsrLink(phy, linkId, delay);
743}
744
745void
747{
748 NS_LOG_FUNCTION(this << dest << frame);
749
750 WifiMacHeader hdr;
752 hdr.SetAddr1(dest);
753 hdr.SetAddr2(m_self);
754 hdr.SetAddr3(m_bssid);
755 hdr.SetDsNotTo();
756 hdr.SetDsNotFrom();
757
758 // get the sequence number for the TWT Setup management frame
759 const auto sequence = m_txMiddle->GetNextSequenceNumberFor(&hdr);
760 hdr.SetSequenceNumber(sequence);
761
762 WifiActionHeader actionHdr;
766
767 auto packet = Create<Packet>();
768 packet->AddHeader(frame);
769 packet->AddHeader(actionHdr);
770
771 // Use AC_VO to send management frame addressed to a QoS STA (Sec. 10.2.3.2 of 802.11-2020)
772 m_mac->GetQosTxop(AC_VO)->Queue(Create<WifiMpdu>(packet, hdr));
773}
774
775std::optional<dBm_u>
777{
778 auto optRssi = HeFrameExchangeManager::GetMostRecentRssi(address);
779
780 if (optRssi)
781 {
782 return optRssi;
783 }
784
785 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
786
787 if (!mldAddress)
788 {
789 // not an MLD, nothing else can be done
790 return std::nullopt;
791 }
792
793 for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
794 {
795 std::optional<Mac48Address> linkAddress;
796 if (linkId != m_linkId &&
797 (linkAddress = m_mac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(
798 *mldAddress)) &&
799 (optRssi = m_mac->GetWifiRemoteStationManager(linkId)->GetMostRecentRssi(*linkAddress)))
800 {
801 return optRssi;
802 }
803 }
804
805 return std::nullopt;
806}
807
808void
810 WifiTxVector& txVector) const
811{
812 NS_LOG_FUNCTION(this << trigger << txVector);
813
814 if (!trigger.IsMuRts() && !trigger.IsBsrp())
815 {
816 NS_LOG_INFO("Not an ICF");
817 return;
818 }
819
820 const auto recipients = GetTfRecipients(trigger);
821 uint8_t maxPaddingDelay = 0;
822 bool isUnprotectedEmlsrDst = false;
823
824 for (const auto& address : recipients)
825 {
826 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address) ||
827 m_protectedStas.contains(address))
828 {
829 continue; // not an EMLSR client or EMLSR client already protected
830 }
831
832 isUnprotectedEmlsrDst = true;
833 auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
834 NS_ASSERT(emlCapabilities);
835 maxPaddingDelay = std::max(maxPaddingDelay, emlCapabilities->get().emlsrPaddingDelay);
836 }
837
838 if (isUnprotectedEmlsrDst)
839 {
840 // The initial Control frame of frame exchanges shall be sent in the non-HT PPDU or
841 // non-HT duplicate PPDU format using a rate of 6 Mb/s, 12 Mb/s, or 24 Mb/s.
842 // (Sec. 35.3.17 of 802.11be D3.0)
843 GetWifiRemoteStationManager()->AdjustTxVectorForIcf(txVector);
844 }
845
846 // add padding (if needed)
847 if (maxPaddingDelay > 0)
848 {
849 // see formula (35-1) in Sec. 35.5.2.2.3 of 802.11be D3.0
850 auto rate = txVector.GetMode().GetDataRate(txVector);
851 std::size_t nDbps = rate / 1e6 * 4; // see Table 17-4 of 802.11-2020
852 trigger.SetPaddingSize((1 << (maxPaddingDelay + 2)) * nDbps / 8);
853 }
854}
855
856void
858{
859 NS_LOG_FUNCTION(this << sender);
860
861 // an EMLSR client responding to a BSRP TF must be considered protected
862 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(sender))
863 {
864 m_protectedStas.insert(sender);
865 }
866
868}
869
870bool
872{
874 if (m_staMac->IsEmlsrLink(m_linkId))
875 {
876 auto mainPhy = m_staMac->GetDevice()->GetPhy(m_staMac->GetEmlsrManager()->GetMainPhyId());
877
878 // while an ICF is being received on this link, an aux PHY that is not TX capable may get
879 // a TXOP on another link, release the channel and request the main PHY to switch channel.
880 // It may be decided to have the main PHY start a TXOP on the other link a PIFS after the
881 // channel switch (e.g., MAC header information is not used and AllowUlTxopInRx is true).
882 // Thus, when the ICF is received on this link, it is not dropped but, when the CTS must
883 // be transmitted, the main PHY has already started transmitting on the other link. In
884 // such a case, do not respond to the ICF.
885 if (mainPhy->IsStateSwitching() || m_mac->GetLinkForPhy(mainPhy) != m_linkId)
886 {
887 NS_LOG_DEBUG("Main PHY is switching or operating on another link, abort ICF response");
888 return true;
889 }
890 }
891 return false;
892}
893
894void
896 const CtrlTriggerHeader& trigger,
897 double muRtsSnr)
898{
899 NS_LOG_FUNCTION(this << muRtsHdr << trigger << muRtsSnr);
900
902 {
903 return;
904 }
905 HeFrameExchangeManager::SendCtsAfterMuRts(muRtsHdr, trigger, muRtsSnr);
906}
907
908void
910 const WifiMacHeader& hdr)
911{
912 NS_LOG_FUNCTION(this << trigger << hdr);
913
914 if (trigger.IsBsrp() && EmlsrClientCannotRespondToIcf())
915 {
916 return;
917 }
919}
920
921void
923{
924 NS_LOG_FUNCTION(this);
925
926 for (const auto& address : clients)
927 {
928 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
929 {
930 // EMLSR client switched to listening operations if it was protected, otherwise
931 // simply unblock transmissions
932 m_protectedStas.contains(address) ? EmlsrSwitchToListening(address, Time{0})
933 : (void)(UnblockEmlsrLinksIfAllowed(address, false));
934 m_protectedStas.erase(address);
935 }
936 }
937}
938
939void
946
947bool
949{
950 NS_LOG_FUNCTION(this);
951
952 if (m_apMac)
953 {
954 if (const auto apEmlsrManager = m_apMac->GetApEmlsrManager();
955 apEmlsrManager && IsCrossLinkCollision(m_sentRtsTo))
956 {
957 return apEmlsrManager->UpdateCwAfterFailedIcf();
958 }
959 }
960
962}
963
964bool
966{
967 NS_LOG_FUNCTION(this);
968
969 if (m_apMac)
970 {
971 if (const auto apEmlsrManager = m_apMac->GetApEmlsrManager();
972 apEmlsrManager && IsCrossLinkCollision(m_sentRtsTo))
973 {
974 return apEmlsrManager->ReportFailedIcf();
975 }
976 }
977
979}
980
981void
982EhtFrameExchangeManager::TbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicitedStations)
983{
984 NS_LOG_FUNCTION(this << psduMap << nSolicitedStations);
985
986 const auto& staMissedTbPpduFrom = m_txTimer.GetStasExpectedToRespond();
987 const auto crossLinkCollision = IsCrossLinkCollision(staMissedTbPpduFrom);
988
989 if (staMissedTbPpduFrom.size() != nSolicitedStations)
990 {
991 // some STAs replied, hence the transmission succeeded. EMLSR clients that did not
992 // respond are switching back to listening operations
993 SwitchToListeningOrUnblockLinks(staMissedTbPpduFrom);
994 }
995
996 const auto apEmlsrManager = m_apMac->GetApEmlsrManager();
997 const auto updateFailedCw =
998 crossLinkCollision && apEmlsrManager ? apEmlsrManager->UpdateCwAfterFailedIcf() : true;
999 DoTbPpduTimeout(psduMap, nSolicitedStations, updateFailedCw);
1000}
1001
1002void
1004 std::size_t nSolicitedStations)
1005{
1006 NS_LOG_FUNCTION(this << psduMap << nSolicitedStations);
1007
1008 const auto& staMissedTbPpduFrom = m_txTimer.GetStasExpectedToRespond();
1009
1010 if (staMissedTbPpduFrom.size() != nSolicitedStations)
1011 {
1012 // some STAs replied, hence the transmission succeeded. EMLSR clients that did not
1013 // respond are switching back to listening operations
1014 SwitchToListeningOrUnblockLinks(staMissedTbPpduFrom);
1015 }
1016
1017 HeFrameExchangeManager::BlockAcksInTbPpduTimeout(psduMap, nSolicitedStations);
1018}
1019
1020bool
1022 const std::set<Mac48Address>& staMissedResponseFrom) const
1023{
1024 NS_LOG_FUNCTION(this << staMissedResponseFrom.size());
1025
1026 // check if all the clients that did not respond to the ICF are EMLSR clients that have sent
1027 // (or are sending) a frame to the AP on another link
1028 auto crossLinkCollision = true;
1029
1030 // we blocked transmissions on the other EMLSR links for the EMLSR clients we sent the ICF to.
1031 // For clients that did not respond, we can unblock transmissions if there is no ongoing
1032 // UL TXOP held by that client
1033 for (const auto& address : staMissedResponseFrom)
1034 {
1035 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
1036 {
1037 crossLinkCollision = false;
1038 continue;
1039 }
1040
1041 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
1042 NS_ASSERT(mldAddress);
1043
1044 std::set<uint8_t> linkIds; // all EMLSR links of EMLSR client
1045 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1046 {
1047 if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress) &&
1048 linkId != m_linkId)
1049 {
1050 linkIds.insert(linkId);
1051 }
1052 }
1053
1054 if (std::any_of(linkIds.cbegin(),
1055 linkIds.cend(),
1056 /* lambda returning true if an UL TXOP is ongoing on the given link ID */
1057 [=, this](uint8_t id) {
1058 auto ehtFem = StaticCast<EhtFrameExchangeManager>(
1059 m_mac->GetFrameExchangeManager(id));
1060 return ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
1061 m_mac->GetMldAddress(ehtFem->m_txopHolder.value()) == mldAddress;
1062 }))
1063 {
1064 // an UL TXOP is ongoing on one EMLSR link, do not unblock links
1065 continue;
1066 }
1067
1068 // no UL TXOP is ongoing on any EMLSR link; if the EMLSR client is not transmitting a
1069 // frame to the AP on any EMLSR link, then the lack of response to the MU-RTS was not
1070 // caused by a simultaneous UL transmission
1071 if (std::none_of(linkIds.cbegin(),
1072 linkIds.cend(),
1073 /* lambda returning true if an MPDU from the EMLSR client is being received
1074 on the given link ID */
1075 [=, this](uint8_t id) {
1076 auto macHdr = m_mac->GetFrameExchangeManager(id)->GetReceivedMacHdr();
1077 if (!macHdr.has_value())
1078 {
1079 return false;
1080 }
1081 auto addr2 = macHdr->get().GetAddr2();
1082 return m_mac->GetMldAddress(addr2) == mldAddress;
1083 }))
1084 {
1085 crossLinkCollision = false;
1086 }
1087 }
1088
1089 return crossLinkCollision;
1090}
1091
1092void
1093EhtFrameExchangeManager::SendCtsAfterRts(const WifiMacHeader& rtsHdr,
1094 const WifiTxVector& rtsTxVector,
1095 double rtsSnr)
1096{
1097 NS_LOG_FUNCTION(this << rtsHdr << rtsTxVector << rtsSnr);
1098
1099 auto addr2 = rtsHdr.GetAddr2();
1100
1101 if (m_apMac && GetWifiRemoteStationManager()->GetEmlsrEnabled(addr2))
1102 {
1103 // we are going to send a CTS to an EMLSR client, transmissions to such EMLSR client
1104 // must be blocked on the other EMLSR links
1105
1106 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(addr2);
1107 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << addr2);
1108
1109 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
1110 {
1111 if (linkId != m_linkId &&
1112 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
1113 {
1114 // check that other links are blocked as expected
1116 WifiRcvAddr::UNICAST,
1117 *mldAddress,
1118 0);
1119 auto mask =
1120 m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
1121 NS_ASSERT_MSG(mask, "No mask for client " << *mldAddress << " on link " << +linkId);
1122 if (!mask->test(
1123 static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK)))
1124 {
1125 NS_ASSERT_MSG(false,
1126 "Transmissions to " << *mldAddress << " on link " << +linkId
1127 << " are not blocked");
1128 // in case asserts are disabled, block transmissions on the other links because
1129 // this is what we need
1130 m_mac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1131 *mldAddress,
1132 {linkId});
1133 }
1134 }
1135 }
1136 }
1137
1138 HeFrameExchangeManager::SendCtsAfterRts(rtsHdr, rtsTxVector, rtsSnr);
1139}
1140
1141bool
1142EhtFrameExchangeManager::GetEmlsrSwitchToListening(Ptr<const WifiPsdu> psdu,
1143 uint16_t aid,
1144 const Mac48Address& address) const
1145{
1146 NS_LOG_FUNCTION(this << psdu << aid << address);
1147
1148 // Sec. 35.3.17 of 802.11be D3.0:
1149 // The non-AP MLD shall be switched back to the listening operation on the EMLSR links after
1150 // the EMLSR transition delay time if [...] the non-AP STA affiliated with the non-AP MLD
1151 // does not detect [...] any of the following frames:
1152 // - an individually addressed frame with the RA equal to the MAC address of the non-AP STA
1153 // affiliated with the non-AP MLD
1154 if (psdu->GetAddr1() == address)
1155 {
1156 return false;
1157 }
1158
1159 // - a Trigger frame that has one of the User Info fields addressed to the non-AP STA
1160 // affiliated with the non-AP MLD
1161 for (const auto& mpdu : *PeekPointer(psdu))
1162 {
1163 if (mpdu->GetHeader().IsTrigger())
1164 {
1165 CtrlTriggerHeader trigger;
1166 mpdu->GetPacket()->PeekHeader(trigger);
1167 if (trigger.FindUserInfoWithAid(aid) != trigger.end())
1168 {
1169 return false;
1170 }
1171 }
1172 }
1173
1174 // - a CTS-to-self frame with the RA equal to the MAC address of the AP affiliated with
1175 // the AP MLD
1176 if (psdu->GetHeader(0).IsCts())
1177 {
1178 if (m_apMac && psdu->GetAddr1() == m_self)
1179 {
1180 return false;
1181 }
1182 if (m_staMac && psdu->GetAddr1() == m_bssid)
1183 {
1184 return false;
1185 }
1186 }
1187
1188 // - a Multi-STA BlockAck frame that has one of the Per AID TID Info fields addressed to
1189 // the non-AP STA affiliated with the non-AP MLD
1190 if (psdu->GetHeader(0).IsBlockAck())
1191 {
1192 CtrlBAckResponseHeader blockAck;
1193 psdu->GetPayload(0)->PeekHeader(blockAck);
1194 if (blockAck.IsMultiSta() && !blockAck.FindPerAidTidInfoWithAid(aid).empty())
1195 {
1196 return false;
1197 }
1198 }
1199
1200 // - a NDP Announcement frame that has one of the STA Info fields addressed to the non-AP
1201 // STA affiliated with the non-AP MLD and a sounding NDP
1202 // TODO NDP Announcement frame not supported yet
1203
1204 return true;
1205}
1206
1207void
1208EhtFrameExchangeManager::TransmissionSucceeded()
1209{
1210 NS_LOG_FUNCTION(this);
1211
1212 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1213 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
1214 {
1215 NS_LOG_DEBUG("Reset the counter of TXOP attempts allowed while "
1216 "MediumSyncDelay is running");
1217 m_staMac->GetEmlsrManager()->ResetMediumSyncDelayNTxops(m_linkId);
1218 }
1219
1220 HeFrameExchangeManager::TransmissionSucceeded();
1221}
1222
1223void
1224EhtFrameExchangeManager::TransmissionFailed(bool forceCurrentCw)
1225{
1226 NS_LOG_FUNCTION(this << forceCurrentCw);
1227
1228 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1229 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
1230 {
1231 NS_LOG_DEBUG("Decrement the remaining number of TXOP attempts allowed while "
1232 "MediumSyncDelay is running");
1233 m_staMac->GetEmlsrManager()->DecrementMediumSyncDelayNTxops(m_linkId);
1234 }
1235
1236 HeFrameExchangeManager::TransmissionFailed(forceCurrentCw);
1237}
1238
1239void
1240EhtFrameExchangeManager::NotifyChannelReleased(Ptr<Txop> txop)
1241{
1242 NS_LOG_FUNCTION(this << txop);
1243
1244 if (m_apMac)
1245 {
1246 // the channel has been released; if the TXNAV is still set, it means that there is not
1247 // enough time left to send a CF-End. In this case, EMLSR clients wait for a slot plus the
1248 // PHY RX start delay before switching back to listening operation (in this case, this
1249 // function is called a SIFS after the last frame in the TXOP)
1250 Time delay{0};
1251 if (const auto remTxNav = m_txNav - Simulator::Now(); remTxNav.IsStrictlyPositive())
1252 {
1253 delay = Min(m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY, remTxNav);
1254 }
1255
1256 for (const auto& address : m_protectedStas)
1257 {
1258 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
1259 {
1260 EmlsrSwitchToListening(address, delay);
1261 }
1262 }
1263 }
1264 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
1265 {
1266 // Notify the UL TXOP end to the EMLSR Manager
1267 auto edca = DynamicCast<QosTxop>(txop);
1268 NS_ASSERT(edca);
1269
1270 NS_ASSERT(m_staMac->GetEmlsrManager());
1271 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId, edca);
1272 }
1273
1274 HeFrameExchangeManager::NotifyChannelReleased(txop);
1275}
1276
1277void
1278EhtFrameExchangeManager::PreProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
1279{
1280 NS_LOG_FUNCTION(this << psdu << txVector);
1281
1282 // In addition, the timer resets to zero when any of the following events occur:
1283 // — The STA receives an MPDU
1284 // (Sec. 35.3.16.8.1 of 802.11be D3.1)
1285 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1286 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
1287 {
1288 m_staMac->GetEmlsrManager()->CancelMediumSyncDelayTimer(m_linkId);
1289 }
1290
1291 if (m_apMac)
1292 {
1293 // we iterate over protected STAs to consider only the case when the AP is the TXOP holder.
1294 // The AP received a PSDU from a non-AP STA; given that the AP is the TXOP holder, this
1295 // PSDU has been likely solicited by the AP. In most of the cases, we identify which EMLSR
1296 // clients are no longer involved in the TXOP when the AP transmits the frame soliciting
1297 // response(s) from client(s). This is not the case, for example, for the acknowledgment
1298 // in SU format of a DL MU PPDU, where all the EMLSR clients (but one) switch to listening
1299 // operation after the immediate response (if any) by one of the EMLSR clients.
1300 for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
1301 {
1302 // TB PPDUs are received by the AP at distinct times, so it is difficult to take a
1303 // decision based on one of them. However, clients transmitting TB PPDUs are identified
1304 // by the soliciting Trigger Frame, thus we have already identified (when sending the
1305 // Trigger Frame) which EMLSR clients have switched to listening operation.
1306 // If the PSDU is not carried in a TB PPDU, we can determine whether this EMLSR client
1307 // is switching to listening operation by checking whether the AP is expecting a
1308 // response from it.
1309 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) && !txVector.IsUlMu() &&
1310 !m_txTimer.GetStasExpectedToRespond().contains(*clientIt))
1311 {
1312 EmlsrSwitchToListening(*clientIt, Seconds(0));
1313 // this client is no longer involved in the current TXOP
1314 clientIt = m_protectedStas.erase(clientIt);
1315 }
1316 else
1317 {
1318 clientIt++;
1319 }
1320 }
1321 }
1322
1323 HeFrameExchangeManager::PreProcessFrame(psdu, txVector);
1324}
1325
1326void
1327EhtFrameExchangeManager::PostProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
1328{
1329 NS_LOG_FUNCTION(this << psdu << txVector);
1330
1331 HeFrameExchangeManager::PostProcessFrame(psdu, txVector);
1332
1333 if (m_apMac && m_apMac->GetApEmlsrManager())
1334 {
1335 m_apMac->GetApEmlsrManager()->NotifyPsduRxOk(m_linkId, psdu);
1336 }
1337
1338 if (m_apMac && m_txopHolder == psdu->GetAddr2() &&
1339 GetWifiRemoteStationManager()->GetEmlsrEnabled(*m_txopHolder))
1340 {
1341 const auto unrespondedRts = (psdu->GetHeader(0).IsRts() && !m_sendCtsEvent.IsPending());
1342
1343 if (!m_ongoingTxopEnd.IsPending() && !unrespondedRts)
1344 {
1345 // an EMLSR client has started an UL TXOP. Start the ongoingTxopEnd timer so that
1346 // the next call to UpdateTxopEndOnRxEnd does its job
1347 m_ongoingTxopEnd =
1348 Simulator::ScheduleNow(&EhtFrameExchangeManager::TxopEnd, this, m_txopHolder);
1349 }
1350
1351 UpdateTxopEndOnRxEnd(psdu->GetDuration());
1352 }
1353
1354 if (m_staMac && m_ongoingTxopEnd.IsPending())
1355 {
1356 if (GetEmlsrSwitchToListening(psdu, m_staMac->GetAssociationId(), m_self))
1357 {
1358 // we are no longer involved in the TXOP and switching to listening mode
1359 m_ongoingTxopEnd.Cancel();
1360 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
1361 }
1362 else
1363 {
1364 UpdateTxopEndOnRxEnd(psdu->GetDuration());
1365 }
1366 }
1367
1368 if (m_staMac && m_dlTxopStart)
1369 {
1370 // we just got involved in a DL TXOP. Check if we are still involved in the TXOP in a
1371 // SIFS (we are expected to reply in a SIFS)
1372 m_ongoingTxopEnd.Cancel();
1373 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + m_phy->GetSifs()).As(Time::S));
1374 m_ongoingTxopEnd = Simulator::Schedule(m_phy->GetSifs() + TimeStep(1),
1375 &EhtFrameExchangeManager::TxopEnd,
1376 this,
1377 psdu->GetAddr2());
1378 // notify the EMLSR manager
1379 m_staMac->GetEmlsrManager()->NotifyDlTxopStart(m_linkId);
1380 m_dlTxopStart = false;
1381 }
1382}
1383
1384bool
1385EhtFrameExchangeManager::CheckEmlsrClientStartingTxop(const WifiMacHeader& hdr,
1386 const WifiTxVector& txVector)
1387{
1388 NS_LOG_FUNCTION(this);
1389
1390 auto sender = hdr.GetAddr2();
1391
1392 if (m_ongoingTxopEnd.IsPending())
1393 {
1394 NS_LOG_DEBUG("A TXOP is already ongoing");
1395 return false;
1396 }
1397
1398 if (auto holder = FindTxopHolder(hdr, txVector); holder != sender)
1399 {
1400 NS_LOG_DEBUG("Sender (" << sender << ") differs from the TXOP holder ("
1401 << (holder ? Address(*holder) : Address()) << ")");
1402 return false;
1403 }
1404
1405 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(sender))
1406 {
1407 NS_LOG_DEBUG("Sender (" << sender << ") is not an EMLSR client");
1408 return false;
1409 }
1410
1411 NS_LOG_DEBUG("EMLSR client " << sender << " is starting a TXOP");
1412
1413 // Block transmissions for this EMLSR client on other links
1414 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(sender);
1415 NS_ASSERT(mldAddress);
1416
1417 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
1418 {
1419 if (linkId != m_linkId &&
1420 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
1421 {
1422 m_mac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1423 *mldAddress,
1424 {linkId});
1425
1426 // the AP MLD may have sent an ICF to the EMLSR client on this link while the EMLSR
1427 // client was starting a TXOP on another link. To be safe, besides blocking
1428 // transmissions, remove the EMLSR client from the protected stations on this link
1429 auto linkAddr =
1430 m_mac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(*mldAddress);
1431 NS_ASSERT(linkAddr.has_value());
1432 auto ehtFem =
1433 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
1434 NS_LOG_DEBUG("Remove " << *linkAddr << " from protected STAs");
1435 ehtFem->m_protectedStas.erase(*linkAddr);
1436 ehtFem->m_sentRtsTo.erase(*linkAddr);
1437 ehtFem->m_sentFrameTo.erase(*linkAddr);
1438 }
1439 }
1440
1441 // Make sure that transmissions for this EMLSR client are not blocked on this link
1442 // (the AP MLD may have sent an ICF on another link right before receiving this MPDU,
1443 // thus transmissions on this link may have been blocked)
1444 m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
1445 *mldAddress,
1446 {m_linkId});
1447
1448 // Stop the transition delay timer for this EMLSR client, if any is running
1449 if (auto it = m_transDelayTimer.find(*mldAddress);
1450 it != m_transDelayTimer.end() && it->second.IsPending())
1451 {
1452 it->second.PeekEventImpl()->Invoke();
1453 it->second.Cancel();
1454 }
1455
1456 return true;
1457}
1458
1459EventId&
1460EhtFrameExchangeManager::GetOngoingTxopEndEvent()
1461{
1462 return m_ongoingTxopEnd;
1463}
1464
1465void
1466EhtFrameExchangeManager::PsduRxError(Ptr<const WifiPsdu> psdu)
1467{
1468 NS_LOG_FUNCTION(this << psdu);
1469
1470 if (m_apMac && m_apMac->GetApEmlsrManager())
1471 {
1472 m_apMac->GetApEmlsrManager()->NotifyPsduRxError(m_linkId, psdu);
1473 }
1474}
1475
1476void
1477EhtFrameExchangeManager::ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
1478 RxSignalInfo rxSignalInfo,
1479 const WifiTxVector& txVector,
1480 bool inAmpdu)
1481{
1482 NS_LOG_FUNCTION(this << *mpdu << rxSignalInfo << txVector << inAmpdu);
1483
1484 // The received MPDU is either broadcast or addressed to this station
1485 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
1486
1487 const auto& hdr = mpdu->GetHeader();
1488 auto sender = hdr.GetAddr2();
1489
1490 if (hdr.IsTrigger())
1491 {
1492 if (!m_staMac)
1493 {
1494 return; // Trigger Frames are only processed by STAs
1495 }
1496
1497 CtrlTriggerHeader trigger;
1498 mpdu->GetPacket()->PeekHeader(trigger);
1499
1500 if (hdr.GetAddr1() != m_self &&
1501 (!hdr.GetAddr1().IsBroadcast() || !m_staMac->IsAssociated() ||
1502 sender != m_bssid // not sent by the AP this STA is associated with
1503 || trigger.FindUserInfoWithAid(m_staMac->GetAssociationId()) == trigger.end()))
1504 {
1505 return; // not addressed to us
1506 }
1507
1508 if ((trigger.IsMuRts() || trigger.IsBsrp()) && !m_ongoingTxopEnd.IsPending() &&
1509 m_staMac->IsEmlsrLink(m_linkId))
1510 {
1511 // this is an initial Control frame
1512 if (DropReceivedIcf())
1513 {
1514 return;
1515 }
1516
1517 m_dlTxopStart = true;
1518 }
1519 }
1520 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) && !m_ongoingTxopEnd.IsPending() &&
1521 m_phy->GetPhyId() == m_staMac->GetEmlsrManager()->GetMainPhyId() &&
1522 (hdr.IsRts() || hdr.IsBlockAckReq() || (hdr.IsData() && hdr.GetAddr1() == m_self)))
1523 {
1524 // a frame that is starting a DL TXOP has been received by the main PHY
1525 m_dlTxopStart = true;
1526 }
1527
1528 if (!m_dlTxopStart && ShallDropReceivedMpdu(mpdu))
1529 {
1530 NS_LOG_DEBUG("Drop received MPDU: " << *mpdu);
1531 return;
1532 }
1533
1534 HeFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1535
1536 if (m_apMac && GetWifiRemoteStationManager()->GetEmlsrEnabled(sender))
1537 {
1538 if (hdr.IsRts() && !m_sendCtsEvent.IsPending())
1539 {
1540 // received RTS but did not send CTS (e.g., NAV busy), start transition delay
1541 EmlsrSwitchToListening(sender, Time{0});
1542 return;
1543 }
1544
1545 // if the AP MLD received an MPDU from an EMLSR client that is starting an UL TXOP,
1546 // block transmissions to the EMLSR client on other links
1547 CheckEmlsrClientStartingTxop(hdr, txVector);
1548 }
1549}
1550
1551void
1552EhtFrameExchangeManager::EndReceiveAmpdu(Ptr<const WifiPsdu> psdu,
1553 const RxSignalInfo& rxSignalInfo,
1554 const WifiTxVector& txVector,
1555 const std::vector<bool>& perMpduStatus)
1556{
1558 this << *psdu << rxSignalInfo << txVector << perMpduStatus.size()
1559 << std::all_of(perMpduStatus.begin(), perMpduStatus.end(), [](bool v) { return v; }));
1560
1561 const auto& hdr = psdu->GetHeader(0);
1562 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) && !m_ongoingTxopEnd.IsPending() &&
1563 m_phy->GetPhyId() == m_staMac->GetEmlsrManager()->GetMainPhyId() &&
1564 (hdr.IsData() && hdr.GetAddr1() == m_self))
1565 {
1566 // a frame that is starting a DL TXOP has been received by the main PHY
1567 m_dlTxopStart = true;
1568 }
1569
1570 if (!m_dlTxopStart && ShallDropReceivedMpdu(*psdu->begin()))
1571 {
1572 return;
1573 }
1574
1575 HeFrameExchangeManager::EndReceiveAmpdu(psdu, rxSignalInfo, txVector, perMpduStatus);
1576}
1577
1578bool
1579EhtFrameExchangeManager::ShallDropReceivedMpdu(Ptr<const WifiMpdu> mpdu) const
1580{
1581 NS_LOG_FUNCTION(this << *mpdu);
1582
1583 // this function only checks frames that shall be dropped by an EMLSR client
1584 if (!m_staMac || !m_staMac->IsEmlsrLink(m_linkId))
1585 {
1586 return false;
1587 }
1588
1589 // discard any frame received after scheduling a CTS response. It has been observed that
1590 // an ICF may be received by both the main PHY and an aux PHY (leading to scheduling a
1591 // CTS response twice) if:
1592 // - the main PHY switches to an aux PHY link and completes the switch during the preamble
1593 // detection period for a PPDU (that is not an ICF), hence main PHY connection is postponed
1594 // - right afterwards, an ICF is transmitted on the aux PHY link (collision with the other
1595 // PPDU)
1596 // - the main PHY starts receiving the ICF, and so does the aux PHY because the ICF signal
1597 // is stronger
1598 // - at the end of the ICF reception, the aux PHY notifies the ICF to the FEM, which schedules
1599 // a CTS and connects the main PHY to the link; then, the main PHY notifies the ICF to the
1600 // FEM again
1601 if (m_sendCtsEvent.IsPending())
1602 {
1603 NS_LOG_DEBUG("Dropping " << *mpdu << " received when CTS is scheduled for TX on link "
1604 << +m_linkId);
1605 return true;
1606 }
1607
1608 const auto& hdr = mpdu->GetHeader();
1609
1610 // We impose that an aux PHY is only able to receive an ICF, a CF-End, a CTS or a management
1611 // frame (we are interested in receiving mainly Beacon frames). Note that other frames are
1612 // still post-processed, e.g., used to set the NAV and the TXOP holder.
1613 // The motivation is that, e.g., an AP MLD may send an ICF to EMLSR clients A and B;
1614 // A responds while B does not; the AP MLD sends a DL MU PPDU to both clients followed
1615 // by an MU-BAR to solicit a BlockAck from both clients. If an aux PHY of client B is
1616 // operating on this link, the MU-BAR will be received and a TB PPDU response sent
1617 // through the aux PHY.
1618 if (hdr.IsMgt() || hdr.IsCts() || hdr.IsCfEnd() || (hdr.IsData() && hdr.GetAddr1().IsGroup()))
1619 {
1620 return false;
1621 }
1622
1623 // other frames cannot be received by an aux PHY
1624 if (m_mac->GetLinkForPhy(m_staMac->GetEmlsrManager()->GetMainPhyId()) != m_linkId)
1625 {
1626 NS_LOG_DEBUG("Dropping " << *mpdu << " received by an aux PHY on link " << +m_linkId);
1627 return true;
1628 }
1629
1630 // other frames cannot be received by the main PHY when not involved in any TXOP
1631 if (!m_ongoingTxopEnd.IsPending() &&
1632 std::none_of(wifiAcList.cbegin(), wifiAcList.cend(), [=, this](const auto& aciAcPair) {
1633 return m_mac->GetQosTxop(aciAcPair.first)->GetTxopStartTime(m_linkId).has_value();
1634 }))
1635 {
1636 NS_LOG_DEBUG("Dropping " << *mpdu << " received by main PHY on link " << +m_linkId
1637 << " while no TXOP is ongoing");
1638 return true;
1639 }
1640
1641 // other frames can be received by the main PHY when involved in a TXOP
1642 return false;
1643}
1644
1645bool
1646EhtFrameExchangeManager::DropReceivedIcf()
1647{
1648 NS_LOG_FUNCTION(this);
1649
1650 auto emlsrManager = m_staMac->GetEmlsrManager();
1651 NS_ASSERT(emlsrManager);
1652
1653 if (UsingOtherEmlsrLink())
1654 {
1655 // we received an ICF on a link that is blocked because another EMLSR link is
1656 // being used. Check if there is an ongoing DL TXOP on the other EMLSR link
1657 auto apMldAddress = GetWifiRemoteStationManager()->GetMldAddress(m_bssid);
1658 NS_ASSERT_MSG(apMldAddress, "MLD address not found for " << m_bssid);
1659
1660 if (auto it = std::find_if(
1661 m_staMac->GetLinkIds().cbegin(),
1662 m_staMac->GetLinkIds().cend(),
1663 /* lambda to find an EMLSR link on which there is an ongoing DL TXOP */
1664 [=, this](uint8_t linkId) {
1665 auto ehtFem =
1666 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
1667 return linkId != m_linkId && m_staMac->IsEmlsrLink(linkId) &&
1668 ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
1669 m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(
1670 *ehtFem->m_txopHolder) == apMldAddress;
1671 });
1672 it != m_staMac->GetLinkIds().cend())
1673 {
1674 // AP is not expected to send ICFs on two links. If an ICF
1675 // has been received on this link, it means that the DL TXOP
1676 // on the other link terminated (e.g., the AP did not
1677 // receive our response)
1678 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(*it))
1679 ->m_ongoingTxopEnd.Cancel();
1680 // we are going to start a TXOP on this link; unblock
1681 // transmissions on this link, the other links will be
1682 // blocked subsequently
1683 m_staMac->UnblockTxOnLink({m_linkId}, WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK);
1684 }
1685 else
1686 {
1687 // We get here likely because transmission on the other EMLSR link
1688 // started before the reception of the ICF ended. We drop this ICF and let the
1689 // UL TXOP continue.
1690 NS_LOG_DEBUG("Drop ICF because another EMLSR link is being used");
1691 m_icfDropCallback({WifiIcfDrop::USING_OTHER_LINK, m_linkId, m_bssid});
1692 return true;
1693 }
1694 }
1695 /**
1696 * It might happen that, while the aux PHY is receiving an ICF, the main PHY is
1697 * completing a TXOP on another link or is returning to the primary link after a TXOP
1698 * is completed on another link. In order to respond to the ICF, it is necessary that
1699 * the main PHY has enough time to switch and be ready to operate on this link by the
1700 * end of the ICF padding.
1701 *
1702 * TXOP end
1703 * │
1704 * ┌───┐ another
1705 * AP MLD │ACK│ link
1706 * ───────────┬─────────┬┴───┴───────────────────────────────────────
1707 * EMLSR │ QoS │ │ main PHY
1708 * client │ Data │ │
1709 * └─────────┘ │
1710 * ┌─────┬───┐ this
1711 * AP MLD │ ICF │pad│ link
1712 * ────────────────────┴─────┴───┴───────────────────────────────────
1713 * aux PHY
1714 */
1715 else if (auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId());
1716 mainPhy != m_phy)
1717 {
1718 auto reason = emlsrManager->CheckMainPhyTakesOverDlTxop(m_linkId);
1719
1720 if (reason.has_value())
1721 {
1723 "Drop ICF due to not enough time for the main PHY to switch link; reason = "
1724 << *reason);
1725 m_icfDropCallback({*reason, m_linkId, m_bssid});
1726 return true;
1727 }
1728 }
1729 return false;
1730}
1731
1732void
1733EhtFrameExchangeManager::TxopEnd(const std::optional<Mac48Address>& txopHolder)
1734{
1735 NS_LOG_FUNCTION(this << txopHolder.has_value());
1736
1737 if (m_phy && m_phy->GetInfoIfRxingPhyHeader())
1738 {
1739 // we may get here because the PHY has not issued the PHY-RXSTART.indication before
1740 // the expiration of the timer started to detect new received frames, but the PHY is
1741 // currently decoding the PHY header of a PPDU, so let's wait some more time to check
1742 // if we receive a PHY-RXSTART.indication when the PHY is done decoding the PHY header
1743 NS_LOG_DEBUG("PHY is decoding the PHY header of PPDU, postpone TXOP end");
1744 m_ongoingTxopEnd = Simulator::Schedule(MicroSeconds(WAIT_FOR_RXSTART_DELAY_USEC),
1745 &EhtFrameExchangeManager::TxopEnd,
1746 this,
1747 txopHolder);
1748 return;
1749 }
1750
1751 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
1752 {
1753 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
1754 }
1755 else if (m_apMac && txopHolder && GetWifiRemoteStationManager()->GetEmlsrEnabled(*txopHolder))
1756 {
1757 // EMLSR client terminated its TXOP and is back to listening operation
1758 EmlsrSwitchToListening(*txopHolder, Seconds(0));
1759 }
1760}
1761
1762void
1763EhtFrameExchangeManager::UpdateTxopEndOnTxStart(Time txDuration, Time durationId)
1764{
1765 NS_LOG_FUNCTION(this << txDuration.As(Time::MS) << durationId.As(Time::US));
1766
1767 if (!m_ongoingTxopEnd.IsPending())
1768 {
1769 // nothing to do
1770 return;
1771 }
1772
1773 m_ongoingTxopEnd.Cancel();
1774 Time delay;
1775
1776 if (m_txTimer.IsRunning())
1777 {
1778 // the TX timer is running, hence we are expecting a response. Postpone the TXOP end
1779 // to match the TX timer (which is long enough to get the PHY-RXSTART.indication for
1780 // the response)
1781 delay = m_txTimer.GetDelayLeft();
1782 }
1783 else if (durationId <= m_phy->GetSifs())
1784 {
1785 // the TX timer is not running, hence no response is expected, and the Duration/ID value
1786 // is less than or equal to a SIFS; the TXOP will end after this transmission
1787 NS_LOG_DEBUG("Assume TXOP will end based on Duration/ID value");
1788 delay = txDuration;
1789 }
1790 else
1791 {
1792 // the TX Timer is not running, hence no response is expected (e.g., we are
1793 // transmitting a CTS after ICS). The TXOP holder may transmit a frame a SIFS
1794 // after the end of this PPDU, hence we need to postpone the TXOP end in order to
1795 // get the PHY-RXSTART.indication
1796 delay = txDuration + m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY;
1797 // TXOP end cannot be beyond the period protected via Duration/ID
1798 delay = Min(delay, txDuration + durationId);
1799 }
1800
1801 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
1802 m_ongoingTxopEnd =
1803 Simulator::Schedule(delay, &EhtFrameExchangeManager::TxopEnd, this, m_txopHolder);
1804}
1805
1806void
1807EhtFrameExchangeManager::UpdateTxopEndOnRxStartIndication(Time psduDuration)
1808{
1809 NS_LOG_FUNCTION(this << psduDuration.As(Time::MS));
1810
1811 if (!m_ongoingTxopEnd.IsPending() || !psduDuration.IsStrictlyPositive())
1812 {
1813 // nothing to do
1814 return;
1815 }
1816
1817 // postpone the TXOP end until after the reception of the PSDU is completed
1818 m_ongoingTxopEnd.Cancel();
1819
1820 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + psduDuration).As(Time::S));
1821 m_ongoingTxopEnd = Simulator::Schedule(psduDuration + NanoSeconds(1),
1822 &EhtFrameExchangeManager::TxopEnd,
1823 this,
1824 m_txopHolder);
1825}
1826
1827void
1828EhtFrameExchangeManager::UpdateTxopEndOnRxEnd(Time durationId)
1829{
1830 NS_LOG_FUNCTION(this << durationId.As(Time::US));
1831
1832 if (!m_ongoingTxopEnd.IsPending())
1833 {
1834 // nothing to do
1835 return;
1836 }
1837
1838 m_ongoingTxopEnd.Cancel();
1839
1840 // if the Duration/ID of the received frame is less than a SIFS, the TXOP
1841 // is terminated
1842 if (durationId <= m_phy->GetSifs())
1843 {
1844 NS_LOG_DEBUG("Assume TXOP ended based on Duration/ID value");
1845 TxopEnd(m_txopHolder);
1846 return;
1847 }
1848
1849 // we may send a response after a SIFS or we may receive another frame after a SIFS.
1850 // Postpone the TXOP end by considering the latter (which takes longer)
1851 auto delay = m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY;
1852 // TXOP end cannot be beyond the period protected via Duration/ID
1853 delay = Min(delay, durationId);
1854 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
1855 m_ongoingTxopEnd =
1856 Simulator::Schedule(delay, &EhtFrameExchangeManager::TxopEnd, this, m_txopHolder);
1857}
1858
1859} // namespace ns3
#define Min(a, b)
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...
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.
void SetPaddingSize(std::size_t size)
Set the size in bytes of the Padding field.
bool IsBsrp() const
Check if this is a Buffer Status Report Poll 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 GenerateInDeviceInterferenceForAll(const Time &txDuration, const WifiTxVector &txVector)
Generate in-device interference caused by a transmission on this link for all the other PHYs of this ...
void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector &txVector) override
Forward a map of PSDUs down to the PHY layer.
void ReceivedQosNullAfterBsrpTf(Mac48Address sender) override
Perform the actions required when receiving QoS Null frame(s) from the given sender after a BSRP Trig...
void SetIcfPaddingAndTxVector(CtrlTriggerHeader &trigger, WifiTxVector &txVector) const
Set the padding and the TXVECTOR of the given Trigger Frame, in case it is an Initial Control Frame f...
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 EmlsrSwitchToListening(Mac48Address address, const Time &delay)
This method is intended to be called when an AP MLD detects that an EMLSR client previously involved ...
void BlockAcksInTbPpduTimeout(WifiPsduMap *psduMap, std::size_t nSolicitedStations) override
Take the necessary actions after that some BlockAck frames are missing in response to a DL MU PPDU.
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 ProtectionCompleted() override
Transmit prepared frame immediately, if no protection was used, or in a SIFS, if protection was compl...
void TbPpduTimeout(WifiPsduMap *psduMap, std::size_t nSolicitedStations) override
Take the necessary actions after that some TB PPDUs are missing in response to Trigger Frame.
void IntraBssNavResetTimeout() override
Reset the intra-BSS NAV upon expiration of the intra-BSS NAV reset timer.
void GenerateInDeviceInterference(Ptr< WifiPhy > phy, Time duration, Watt_u txPower)
Generate an in-device interference of the given power for the given duration for the given PHY.
void SendCtsAfterMuRts(const WifiMacHeader &muRtsHdr, const CtrlTriggerHeader &trigger, double muRtsSnr) override
Send CTS after receiving an MU-RTS.
void SwitchToListeningOrUnblockLinks(const std::set< Mac48Address > &clients)
For each EMLSR client in the given set of clients that did not respond to a frame requesting a respon...
bool IsCrossLinkCollision(const std::set< Mac48Address > &staMissedResponseFrom) const
Check whether all the stations that did not respond (to a certain frame) are EMLSR clients trying to ...
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.
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 UpdateTxopEndOnTxStart(Time txDuration, Time durationId)
Update the TXOP end timer when starting a frame transmission.
void SendQosNullFramesInTbPpdu(const CtrlTriggerHeader &trigger, const WifiMacHeader &hdr) override
Send QoS Null frames in response to a Basic or BSRP Trigger Frame.
void UpdateTxopEndOnRxStartIndication(Time psduDuration)
Update the TXOP end timer when receiving a PHY-RXSTART.indication.
bool UnblockEmlsrLinksIfAllowed(Mac48Address address, bool checkThisLink)
Unblock transmissions on all the links of the given EMLSR client, provided that the latter is not inv...
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
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
virtual bool GetReportRtsFailed() const
Mac48Address m_self
the MAC address of this device
WifiTxTimer m_txTimer
the timer set upon frame transmission
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.
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
Ptr< ApWifiMac > m_apMac
AP MAC layer pointer (null if not an AP)
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
Ptr< StaWifiMac > m_staMac
STA MAC layer pointer (null if not a STA)
virtual bool GetUpdateCwOnCtsTimeout() const
HeFrameExchangeManager handles the frame exchange sequences for HE stations.
virtual void SendQosNullFramesInTbPpdu(const CtrlTriggerHeader &trigger, const WifiMacHeader &hdr)
Send QoS Null frames in response to a Basic or BSRP Trigger Frame.
void DoDispose() override
Destructor implementation.
virtual void IntraBssNavResetTimeout()
Reset the intra-BSS NAV upon expiration of the intra-BSS NAV reset timer.
virtual void ReceivedQosNullAfterBsrpTf(Mac48Address sender)
Perform the actions required when receiving QoS Null frame(s) from the given sender after a BSRP Trig...
void RxStartIndication(WifiTxVector txVector, Time psduDuration) override
virtual void CtsAfterMuRtsTimeout(Ptr< WifiMpdu > muRts, const WifiTxVector &txVector)
Called when no CTS frame is received after an MU-RTS.
void NavResetTimeout() override
Reset the NAV upon expiration of the NAV reset timer.
void ProtectionCompleted() override
Transmit prepared frame immediately, if no protection was used, or in a SIFS, if protection was compl...
virtual void SendCtsAfterMuRts(const WifiMacHeader &muRtsHdr, const CtrlTriggerHeader &trigger, double muRtsSnr)
Send CTS after receiving an MU-RTS.
virtual void BlockAcksInTbPpduTimeout(WifiPsduMap *psduMap, std::size_t nSolicitedStations)
Take the necessary actions after that some BlockAck frames are missing in response to a DL MU PPDU.
virtual void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector &txVector)
Forward a map of PSDUs down to the PHY layer.
std::set< Mac48Address > GetTfRecipients(const CtrlTriggerHeader &trigger) const
Get the (link) address of the non-AP stations solicited by the given Trigger Frame.
void DoTbPpduTimeout(WifiPsduMap *psduMap, std::size_t nSolicitedStations, bool updateFailedCw)
Take the necessary actions after that some TB PPDUs are missing in response to Trigger Frame.
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.
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.
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:561
Simulation virtual time values and global simulation resolution.
Definition nstime.h:96
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:409
bool IsStrictlyPositive() const
Exactly equivalent to t > 0.
Definition nstime.h:342
@ US
microsecond
Definition nstime.h:109
@ MS
millisecond
Definition nstime.h:108
bool IsZero() const
Exactly equivalent to t == 0.
Definition nstime.h:306
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:712
static constexpr bool DIDNT_HAVE_FRAMES_TO_TRANSMIT
no packet available for transmission was in the queue
Definition txop.h:411
static constexpr bool CHECK_MEDIUM_BUSY
generation of backoff (also) depends on the busy/idle state of the medium
Definition txop.h:413
static constexpr bool DONT_CHECK_MEDIUM_BUSY
generation of backoff is independent of the busy/idle state of the medium
Definition txop.h:415
a unique identifier for an interface.
Definition type-id.h:49
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.
uint64_t GetDataRate(MHz_u channelWidth, Time guardInterval, uint8_t nss) const
Definition wifi-mode.cc:110
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1563
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition wifi-phy.cc:1057
Ptr< PhyEntity > GetPhyEntity(WifiModulationClass modulation) const
Get the supported PHY entity corresponding to the modulation class.
Definition wifi-phy.cc:769
const std::set< Mac48Address > & GetStasExpectedToRespond() const
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
WifiMode GetMode(uint16_t staId=SU_STA_ID) const
If this TX vector is associated with an SU PPDU, return the selected payload transmission mode.
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_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:264
#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:439
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1393
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1405
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1369
@ 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:448
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...
bool IsTrigger(const WifiPsduMap &psduMap)
Ptr< T1 > DynamicCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:585
std::unordered_map< uint16_t, Ptr< WifiPsdu > > WifiPsduMap
Map of PSDUs indexed by STA-ID.
Definition wifi-mac.h:78
const std::map< AcIndex, WifiAc > wifiAcList
Map containing the four ACs in increasing order of priority (according to Table 10-1 "UP-to-AC Mappin...
Definition qos-utils.cc:115
@ WIFI_MAC_MGT_ACTION
Watt_u DbmToW(dBm_u val)
Convert from dBm to Watts.
Definition wifi-utils.cc:32
std::tuple< WifiContainerQueueType, WifiRcvAddr, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, receiver address type, Address, TID) identifying a container queue.
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:592
std::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
Definition wifi-ppdu.h:38
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:79
typedef for union of different ActionValues