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