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