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