A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
eht-frame-exchange-manager.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2022 Universita' degli Studi di Napoli Federico II
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Stefano Avallone <stavallo@unina.it>
7 */
8
10
11#include "ap-emlsr-manager.h"
12#include "eht-phy.h"
13#include "emlsr-manager.h"
14
15#include "ns3/abort.h"
16#include "ns3/ap-wifi-mac.h"
17#include "ns3/log.h"
18#include "ns3/mgt-action-headers.h"
19#include "ns3/spectrum-signal-parameters.h"
20#include "ns3/sta-wifi-mac.h"
21#include "ns3/wifi-mac-queue.h"
22#include "ns3/wifi-net-device.h"
23#include "ns3/wifi-spectrum-phy-interface.h"
24
25#include <algorithm>
26
27#undef NS_LOG_APPEND_CONTEXT
28#define NS_LOG_APPEND_CONTEXT WIFI_FEM_NS_LOG_APPEND_CONTEXT
29
30namespace ns3
31{
32
34
35/**
36 * Additional time (exceeding 20 us) to wait for a PHY-RXSTART.indication when the PHY is
37 * decoding a PHY header.
38 *
39 * Values for aRxPHYStartDelay:
40 * - OFDM : 20 us (for 20 MHz) [Table 17-21 of 802.11-2020]
41 * - ERP-OFDM : 20 us [Table 18-5 of 802.11-2020]
42 * - HT : 28 us (HT-mixed), 24 us (HT-greenfield) [Table 19-25 of 802.11-2020]
43 * - VHT : 36 + 4 * max N_VHT-LTF + 4 = 72 us [Table 21-28 of 802.11-2020]
44 * - HE : 32 us (for HE SU and HE TB PPDUs)
45 * 32 + 4 * N_HE-SIG-B us (for HE MU PPDUs) [Table 27-54 of 802.11ax-2021]
46 * - EHT : 32 us (for EHT TB PPDUs)
47 * 32 + 4 * N_EHT-SIG us (for EHT MU PPDUs) [Table 36-70 of 802.11be D3.2]
48 */
49static constexpr uint8_t WAIT_FOR_RXSTART_DELAY_USEC = 52;
50
51NS_LOG_COMPONENT_DEFINE("EhtFrameExchangeManager");
52
54
57{
58 static TypeId tid = TypeId("ns3::EhtFrameExchangeManager")
60 .AddConstructor<EhtFrameExchangeManager>()
61 .SetGroupName("Wifi");
62 return tid;
63}
64
69
74
75void
82
83void
85{
86 NS_LOG_FUNCTION(this << txVector << psduDuration.As(Time::MS));
87
88 HeFrameExchangeManager::RxStartIndication(txVector, psduDuration);
90}
91
92void
94{
95 if (auto protectionManager = GetProtectionManager())
96 {
97 protectionManager->SetLinkId(linkId);
98 }
99 if (auto ackManager = GetAckManager())
100 {
101 ackManager->SetLinkId(linkId);
102 }
103 m_msduAggregator->SetLinkId(linkId);
104 m_mpduAggregator->SetLinkId(linkId);
106}
107
110{
111 NS_LOG_FUNCTION(this << *mpdu);
112
113 // alias needs only be created for non-broadcast QoS data frames exchanged between two MLDs
114 if (!mpdu->GetHeader().IsQosData() || m_mac->GetNLinks() == 1 ||
115 mpdu->GetHeader().GetAddr1().IsGroup() ||
116 !GetWifiRemoteStationManager()->GetMldAddress(mpdu->GetHeader().GetAddr1()))
117 {
119 }
120
121 mpdu = mpdu->CreateAlias(m_linkId);
122 auto& hdr = mpdu->GetHeader();
123 hdr.SetAddr2(GetAddress());
124 auto address = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(hdr.GetAddr1());
125 NS_ASSERT(address);
126 hdr.SetAddr1(*address);
127 /*
128 * Set Address3 according to Table 9-30 of 802.11-2020 and Section 35.3.3 of
129 * 802.11be D2.0 ["the value of the Address 3 field and the Address 4 field (if present)
130 * in the MAC header of a data frame shall be set based on Table 9-30 (Address field
131 * contents) and the settings of the To DS and From DS bits, where the BSSID is the
132 * MAC address of the AP affiliated with the AP MLD corresponding to that link"].
133 */
134 if (hdr.IsQosAmsdu())
135 {
136 if (hdr.IsToDs() && !hdr.IsFromDs())
137 {
138 // from STA to AP: BSSID is in Address1
139 hdr.SetAddr3(hdr.GetAddr1());
140 }
141 else if (!hdr.IsToDs() && hdr.IsFromDs())
142 {
143 // from AP to STA: BSSID is in Address2
144 hdr.SetAddr3(hdr.GetAddr2());
145 }
146 }
147
148 return mpdu;
149}
150
151bool
153{
154 if (!m_staMac || !m_staMac->IsEmlsrLink(m_linkId))
155 {
156 return false;
157 }
158 auto apAddress = GetWifiRemoteStationManager()->GetMldAddress(m_bssid);
159 NS_ASSERT_MSG(apAddress, "MLD address not found for BSSID " << m_bssid);
160 // when EMLSR links are blocked, all TIDs are blocked (we test TID 0 here)
162 auto mask = m_staMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, m_linkId);
163 NS_ASSERT_MSG(mask, "No mask for AP " << *apAddress << " on link " << m_linkId);
164 return mask->test(static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK));
165}
166
167bool
169{
170 NS_LOG_FUNCTION(this << edca << allowedWidth);
171
172 m_allowedWidth = allowedWidth;
173
174 if (m_apMac)
175 {
176 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
177 {
178 if (linkId == m_linkId)
179 {
180 continue;
181 }
182
183 // EMLSR clients involved in a DL or UL TXOP on another link
184 std::set<Mac48Address> emlsrClients;
185 auto ehtFem =
186 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
187
188 // check if an EMLSR client is the holder of an UL TXOP on the other link
189 if (ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
190 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(
191 ehtFem->m_txopHolder.value()))
192 {
193 NS_LOG_DEBUG("Involved in UL TXOP: " << ehtFem->m_txopHolder.value());
194 emlsrClients.insert(ehtFem->m_txopHolder.value());
195 }
196
197 // check if EMLSR clients are involved in a DL TXOP on another link
198 for (const auto& address : ehtFem->m_protectedStas)
199 {
200 if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(address))
201 {
202 NS_LOG_DEBUG("Involved in DL TXOP: " << address);
203 emlsrClients.insert(address);
204 }
205 }
206
207 for (const auto& address : emlsrClients)
208 {
209 auto mldAddress =
210 m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(address);
211 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
212
213 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(*mldAddress))
214 {
215 // EMLSR client did not enable EMLSR mode on this link, we can transmit to it
216 continue;
217 }
218
219 // check that this link is blocked as expected
220 WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *mldAddress, 0);
221 auto mask =
222 m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, m_linkId);
223 NS_ASSERT_MSG(mask,
224 "No mask for client " << *mldAddress << " on link " << +m_linkId);
225 if (!mask->test(
226 static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK)))
227 {
228 NS_ASSERT_MSG(false,
229 "Transmissions to " << *mldAddress << " on link " << +m_linkId
230 << " are not blocked");
231 // in case asserts are disabled, block transmissions on the other links because
232 // this is what we need
234 *mldAddress,
235 {m_linkId});
236 }
237 }
238 }
239 }
240
241 std::optional<Time> timeToCtsEnd;
242
243 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
244 {
245 // Cannot start a transmission on a link blocked because another EMLSR link is being used
247 {
248 NS_LOG_DEBUG("StartTransmission called while another EMLSR link is being used");
250 return false;
251 }
252
253 auto emlsrManager = m_staMac->GetEmlsrManager();
254
255 if (auto elapsed = emlsrManager->GetElapsedMediumSyncDelayTimer(m_linkId);
256 elapsed && emlsrManager->MediumSyncDelayNTxopsExceeded(m_linkId))
257 {
258 NS_LOG_DEBUG("No new TXOP attempts allowed while MediumSyncDelay is running");
259 // request channel access if needed when the MediumSyncDelay timer expires; in the
260 // meantime no queued packet can be transmitted
262 emlsrManager->GetMediumSyncDuration() - *elapsed,
264 edca,
265 m_linkId,
266 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT, // queued frames cannot be transmitted until
267 // MSD expires
268 Txop::DONT_CHECK_MEDIUM_BUSY); // generate backoff regardless of medium busy
270 return false;
271 }
272
273 if (!m_phy)
274 {
275 NS_LOG_DEBUG("No PHY is currently operating on EMLSR link " << +m_linkId);
277 return false;
278 }
279
280 // let EMLSR manager decide whether to prevent or allow this UL TXOP
281 if (const auto [startTxop, delay] = emlsrManager->GetDelayUntilAccessRequest(
282 m_linkId,
283 DynamicCast<QosTxop>(edca)->GetAccessCategory());
284 !startTxop)
285
286 {
287 if (delay.IsStrictlyPositive())
288 {
291 delay,
293 edca,
294 m_linkId,
295 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT, // queued frames cannot be
296 // transmitted until RX ends
297 Txop::CHECK_MEDIUM_BUSY); // generate backoff if medium busy
298 }
299 return false;
300 }
301 }
302
303 auto started = HeFrameExchangeManager::StartTransmission(edca, allowedWidth);
304
305 if (started && m_staMac && m_staMac->IsEmlsrLink(m_linkId))
306 {
307 // notify the EMLSR Manager of the UL TXOP start on an EMLSR link
308 NS_ASSERT(m_staMac->GetEmlsrManager());
309 m_staMac->GetEmlsrManager()->NotifyUlTxopStart(m_linkId);
310 }
311
312 if (started)
313 {
314 // we are starting a new TXOP, hence consider the previous ongoing TXOP as terminated
316 }
317
318 return started;
319}
320
321void
323{
324 NS_LOG_FUNCTION(this);
325
326 if (m_staMac && m_staMac->GetEmlsrManager())
327 {
328 m_staMac->GetEmlsrManager()->NotifyProtectionCompleted(m_linkId);
329 }
330
332}
333
334void
336{
337 NS_LOG_FUNCTION(this << psdu << txVector);
338
339 // EHT-SIG, the equivalent of HE-SIG-B, is present in EHT SU transmissions, too
340 if (txVector.GetPreambleType() == WIFI_PREAMBLE_EHT_MU)
341 {
343 auto sigBMode = phy->GetSigBMode(txVector);
344 txVector.SetSigBMode(sigBMode);
345 }
346
347 auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
348
349 if (m_apMac && psdu->GetHeader(0).IsTrigger())
350 {
351 for (const auto& client : m_sentRtsTo)
352 {
353 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(client))
354 {
355 continue;
356 }
357 auto clientMld = GetWifiRemoteStationManager()->GetMldAddress(client);
358 NS_ASSERT(clientMld);
359
360 // block transmissions on the other EMLSR links of the EMLSR clients
361 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
362 {
363 if (linkId != m_linkId &&
364 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*clientMld))
365 {
367 *clientMld,
368 {linkId});
369 }
370 }
371 }
372 }
373
374 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) && psdu->GetAddr1() == m_bssid &&
375 psdu->GetHeader(0).IsRts())
376 {
377 NS_ASSERT(m_staMac->GetEmlsrManager());
378 m_staMac->GetEmlsrManager()->NotifyRtsSent(m_linkId, psdu, txVector);
379 }
380
382 UpdateTxopEndOnTxStart(txDuration, psdu->GetDuration());
383
384 if (m_apMac && m_apMac->GetApEmlsrManager())
385 {
386 auto delay = m_apMac->GetApEmlsrManager()->GetDelayOnTxPsduNotForEmlsr(psdu,
387 txVector,
388 m_phy->GetPhyBand());
389
390 // check if the EMLSR clients shall switch back to listening operation
391 for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
392 {
393 auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt);
394
395 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) &&
396 GetEmlsrSwitchToListening(psdu, aid, *clientIt))
397 {
398 EmlsrSwitchToListening(*clientIt, delay);
399 // this client is no longer involved in the current TXOP
400 clientIt = m_protectedStas.erase(clientIt);
401 }
402 else
403 {
404 clientIt++;
405 }
406 }
407 }
408 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
409 m_staMac->GetEmlsrManager()->GetInDeviceInterference())
410 {
411 NS_ASSERT(m_staMac->GetEmlsrManager());
412 m_staMac->GetEmlsrManager()->NotifyInDeviceInterferenceStart(m_linkId, txDuration);
413
414 for (const auto linkId : m_staMac->GetLinkIds())
415 {
416 if (auto phy = m_mac->GetWifiPhy(linkId);
417 phy && linkId != m_linkId && m_staMac->IsEmlsrLink(linkId))
418 {
419 const auto txPower = phy->GetPower(txVector.GetTxPowerLevel()) + phy->GetTxGain();
420 // generate in-device interference on the other EMLSR link for the duration of this
421 // transmission
422 GenerateInDeviceInterference(linkId, txDuration, DbmToW(txPower));
423 }
424 }
425 }
426}
427
428void
430{
431 NS_LOG_FUNCTION(this << psduMap << txVector);
432
433 auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand());
434
436 UpdateTxopEndOnTxStart(txDuration, psduMap.begin()->second->GetDuration());
437
438 if (m_apMac)
439 {
440 // check if this is a BSRP TF used as ICF for some EMLSR client
441 if (IsTrigger(psduMap))
442 {
443 CtrlTriggerHeader trigger;
444 psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
445
446 if (trigger.IsBsrp())
447 {
448 auto recipients = GetTfRecipients(trigger);
449 for (const auto& client : recipients)
450 {
451 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(client))
452 {
453 continue;
454 }
455 auto clientMld = GetWifiRemoteStationManager()->GetMldAddress(client);
456 NS_ASSERT(clientMld);
457
458 // block transmissions on the other EMLSR links of the EMLSR clients
459 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
460 {
461 if (linkId != m_linkId &&
462 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*clientMld))
463 {
464 m_mac->BlockUnicastTxOnLinks(
466 *clientMld,
467 {linkId});
468 }
469 }
470 }
471 }
472 }
473
474 // check if the EMLSR clients shall switch back to listening operation at the end of this
475 // PPDU
476 for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
477 {
478 auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt);
479 const auto psduMapIt = psduMap.find(aid);
480 const auto aidNotFoundAndNotTf = (psduMapIt == psduMap.cend()) && !IsTrigger(psduMap);
481 // the PSDU to process: the one addressed to the given AID (if any) or the unique one
482 const auto psdu = (psduMapIt != psduMap.cend() ? psduMapIt : psduMap.cbegin())->second;
483
484 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) &&
485 (aidNotFoundAndNotTf || GetEmlsrSwitchToListening(psdu, aid, *clientIt)))
486 {
487 EmlsrSwitchToListening(*clientIt, txDuration);
488 // this client is no longer involved in the current TXOP
489 clientIt = m_protectedStas.erase(clientIt);
490 }
491 else
492 {
493 clientIt++;
494 }
495 }
496 }
497 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
498 m_staMac->GetEmlsrManager()->GetInDeviceInterference())
499 {
500 NS_ASSERT(m_staMac->GetEmlsrManager());
501 m_staMac->GetEmlsrManager()->NotifyInDeviceInterferenceStart(m_linkId, txDuration);
502
503 for (const auto linkId : m_staMac->GetLinkIds())
504 {
505 if (auto phy = m_mac->GetWifiPhy(linkId);
506 phy && linkId != m_linkId && m_staMac->IsEmlsrLink(linkId))
507 {
508 const auto txPower = phy->GetPower(txVector.GetTxPowerLevel()) + phy->GetTxGain();
509 // generate in-device interference on the other EMLSR link for the duration of this
510 // transmission
511 GenerateInDeviceInterference(linkId, txDuration, DbmToW(txPower));
512 }
513 }
514 }
515}
516
517void
519{
520 NS_LOG_FUNCTION(this << linkId << duration.As(Time::US) << txPower);
521
522 auto rxPhy = DynamicCast<SpectrumWifiPhy>(m_mac->GetWifiPhy(linkId));
523
524 if (!rxPhy)
525 {
526 NS_LOG_DEBUG("No spectrum PHY operating on link " << +linkId);
527 return;
528 }
529
531 NS_ASSERT(txPhy);
532
533 auto psd = Create<SpectrumValue>(rxPhy->GetCurrentInterface()->GetRxSpectrumModel());
534 *psd = txPower;
535
536 auto spectrumSignalParams = Create<SpectrumSignalParameters>();
537 spectrumSignalParams->duration = duration;
538 spectrumSignalParams->txPhy = txPhy->GetCurrentInterface();
539 spectrumSignalParams->txAntenna = txPhy->GetAntenna();
540 spectrumSignalParams->psd = psd;
541
542 rxPhy->StartRx(spectrumSignalParams, rxPhy->GetCurrentInterface());
543}
544
545void
547{
548 NS_LOG_FUNCTION(this);
550 {
551 // the CTS may have been missed because another EMLSR link is being used; do not reset NAV
552 return;
553 }
555}
556
557void
559{
560 NS_LOG_FUNCTION(this);
562 {
563 // the CTS may have been missed because another EMLSR link is being used; do not reset NAV
564 return;
565 }
567}
568
569bool
571{
572 NS_LOG_FUNCTION(this << address);
573
574 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
575 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
576 NS_ASSERT_MSG(m_apMac, "This function shall only be called by AP MLDs");
577 std::set<uint8_t> linkIds{m_linkId};
578
579 /**
580 * Do nothing if the EMLSR client is involved in a DL or UL TXOP on another EMLSR link. This
581 * may happen, e.g., when the AP MLD sent an MU-RTS to multiple stations on this link, some of
582 * which responded, but this EMLSR client did not, e.g., because it concurrently started an UL
583 * TXOP on another link. The AP MLD then started a (long) DL MU transmission on this link,
584 * during which the EMLSR client completed the UL TXOP and started being involved in another
585 * DL or UL TXOP (note that DL TXOP is possible because the AP MLD considered the EMLSR client
586 * unprotected as soon as it detected the start of the previous UL TXOP). A Block Ack timeout
587 * for the EMLSR client occurs at the end of the DL MU transmission (which brings us here) and
588 * it may occur while the EMLSR client is still involved in a DL or UL TXOP.
589 *
590 * ┌─────────────┐ ┌───────────────┐
591 * │ MU-RTS to │ │ Data to │ BA timeout for us
592 * │us and others│ │ us and others │ |
593 * ────────┴─────────────┴┬────────┬┴───────────────┴┬───────┬──────────────
594 * [this link] │CTS from│ │BA from│
595 * │ others │ │ others│
596 * └────────┘ └───────┘
597 * ┌───────┐
598 * ┌───┐ ┌──┐ │ MU-RTS│ ┌──────┐
599 * [other link] │CTS│ │BA│ │ to us │ │ Data │
600 * ─────────┬────────┬┴───┴┬────┬┴──┴──────┴───────┴┬───┬┴──────┴┬──┬───────
601 * │ RTS │ │Data│ │CTS│ │BA│
602 * │from us │ └────┘ └───┘ └──┘
603 * └────────┘
604 */
605
606 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
607 {
608 if (!m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
609 {
610 continue; // not an EMLSR link
611 }
612
613 auto ehtFem = StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
614
615 if (ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
616 m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(*ehtFem->m_txopHolder) ==
617 mldAddress)
618 {
619 NS_LOG_DEBUG("EMLSR client " << *mldAddress << " is the holder of an UL TXOP on link "
620 << +linkId << ", do not unblock links");
621 return false;
622 }
623
624 if (linkId == m_linkId)
625 {
626 // no need to check if the EMLSR client is involved in a DL TXOP on this link
627 continue;
628 }
629
630 linkIds.insert(linkId);
631
632 if (auto linkAddr =
633 m_apMac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(*mldAddress);
634 linkAddr &&
635 (ehtFem->m_sentRtsTo.contains(*linkAddr) || ehtFem->m_sentFrameTo.contains(*linkAddr) ||
636 ehtFem->m_protectedStas.contains(*linkAddr)))
637 {
638 NS_LOG_DEBUG("EMLSR client " << address
639 << " has been sent an ICF, do not unblock links");
640 return false;
641 }
642 }
643
644 // unblock DL transmissions with reason USING_OTHER_EMLSR_LINK
646 *mldAddress,
647 linkIds);
648 return true;
649}
650
651void
653{
654 NS_LOG_FUNCTION(this << address << delay.As(Time::US));
655
656 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
657 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
658 NS_ASSERT_MSG(m_apMac, "This function shall only be called by AP MLDs");
659
660 auto blockLinks = [=, this]() {
661 if (!UnblockEmlsrLinksIfAllowed(address))
662 {
663 NS_LOG_DEBUG("Could not unblock transmissions to " << address);
664 return;
665 }
666
667 // this EMLSR client switches back to listening operation
668 std::set<uint8_t> linkIds;
669 for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
670 {
671 if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
672 {
673 linkIds.insert(linkId);
674 }
675 }
676
677 // block DL transmissions on this link until transition delay elapses
679 *mldAddress,
680 linkIds);
681
682 auto unblockLinks = [=, this]() {
684 *mldAddress,
685 linkIds);
686 };
687
688 // unblock all EMLSR links when the transition delay elapses
689 auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
690 NS_ASSERT(emlCapabilities);
692 emlCapabilities->get().emlsrTransitionDelay);
693
694 endDelay.IsZero() ? unblockLinks()
695 : static_cast<void>(m_transDelayTimer[*mldAddress] =
696 Simulator::Schedule(endDelay, unblockLinks));
697 };
698
699 delay.IsZero() ? blockLinks() : static_cast<void>(Simulator::Schedule(delay, blockLinks));
700}
701
702void
704{
705 NS_LOG_FUNCTION(this << phy << linkId << delay.As(Time::US));
706
707 // TODO Shall we assert that there is no ongoing frame exchange sequence? Or is it possible
708 // that there is an ongoing frame exchange sequence (in such a case, we need to force a
709 // timeout, just like it is done in case of a normal channel switch
710
711 NS_ABORT_MSG_IF(!m_staMac, "This method can only be called on a STA");
712
713 // if we receive the notification from a PHY that is not connected to us, it means that
714 // we have been already connected to another PHY operating on this link, hence we do not
715 // have to reset the connected PHY. Similarly, we do not have to reset the connected PHY if
716 // the link does not change (this occurs when changing the channel width of aux PHYs upon
717 // enabling the EMLSR mode).
718 if (phy == m_phy && linkId != m_linkId)
719 {
720 ResetPhy();
721 }
722 m_staMac->NotifySwitchingEmlsrLink(phy, linkId, delay);
723}
724
725void
727{
728 NS_LOG_FUNCTION(this << dest << frame);
729
730 WifiMacHeader hdr;
732 hdr.SetAddr1(dest);
733 hdr.SetAddr2(m_self);
734 hdr.SetAddr3(m_bssid);
735 hdr.SetDsNotTo();
736 hdr.SetDsNotFrom();
737
738 // get the sequence number for the TWT Setup management frame
739 const auto sequence = m_txMiddle->GetNextSequenceNumberFor(&hdr);
740 hdr.SetSequenceNumber(sequence);
741
742 WifiActionHeader actionHdr;
746
747 auto packet = Create<Packet>();
748 packet->AddHeader(frame);
749 packet->AddHeader(actionHdr);
750
751 // Use AC_VO to send management frame addressed to a QoS STA (Sec. 10.2.3.2 of 802.11-2020)
752 m_mac->GetQosTxop(AC_VO)->Queue(Create<WifiMpdu>(packet, hdr));
753}
754
755std::optional<dBm_u>
757{
758 auto optRssi = HeFrameExchangeManager::GetMostRecentRssi(address);
759
760 if (optRssi)
761 {
762 return optRssi;
763 }
764
765 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
766
767 if (!mldAddress)
768 {
769 // not an MLD, nothing else can be done
770 return std::nullopt;
771 }
772
773 for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
774 {
775 std::optional<Mac48Address> linkAddress;
776 if (linkId != m_linkId &&
777 (linkAddress = m_mac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(
778 *mldAddress)) &&
779 (optRssi = m_mac->GetWifiRemoteStationManager(linkId)->GetMostRecentRssi(*linkAddress)))
780 {
781 return optRssi;
782 }
783 }
784
785 return std::nullopt;
786}
787
788void
790 WifiTxVector& txVector) const
791{
792 NS_LOG_FUNCTION(this << trigger << txVector);
793
794 if (!trigger.IsMuRts() && !trigger.IsBsrp())
795 {
796 NS_LOG_INFO("Not an ICF");
797 return;
798 }
799
800 const auto recipients = GetTfRecipients(trigger);
801 uint8_t maxPaddingDelay = 0;
802 bool isUnprotectedEmlsrDst = false;
803
804 for (const auto& address : recipients)
805 {
806 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address) ||
807 m_protectedStas.contains(address))
808 {
809 continue; // not an EMLSR client or EMLSR client already protected
810 }
811
812 isUnprotectedEmlsrDst = true;
813 auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
814 NS_ASSERT(emlCapabilities);
815 maxPaddingDelay = std::max(maxPaddingDelay, emlCapabilities->get().emlsrPaddingDelay);
816 }
817
818 if (isUnprotectedEmlsrDst)
819 {
820 // The initial Control frame of frame exchanges shall be sent in the non-HT PPDU or
821 // non-HT duplicate PPDU format using a rate of 6 Mb/s, 12 Mb/s, or 24 Mb/s.
822 // (Sec. 35.3.17 of 802.11be D3.0)
823 GetWifiRemoteStationManager()->AdjustTxVectorForIcf(txVector);
824 }
825
826 // add padding (if needed)
827 if (maxPaddingDelay > 0)
828 {
829 // see formula (35-1) in Sec. 35.5.2.2.3 of 802.11be D3.0
830 auto rate = txVector.GetMode().GetDataRate(txVector);
831 std::size_t nDbps = rate / 1e6 * 4; // see Table 17-4 of 802.11-2020
832 trigger.SetPaddingSize((1 << (maxPaddingDelay + 2)) * nDbps / 8);
833 }
834}
835
836void
838{
839 NS_LOG_FUNCTION(this << sender);
840
841 // an EMLSR client responding to a BSRP TF must be considered protected
842 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(sender))
843 {
844 m_protectedStas.insert(sender);
845 }
846
848}
849
850bool
852{
854 if (m_staMac->IsEmlsrLink(m_linkId))
855 {
856 auto mainPhy = m_staMac->GetDevice()->GetPhy(m_staMac->GetEmlsrManager()->GetMainPhyId());
857
858 // while an ICF is being received on this link, an aux PHY that is not TX capable may get
859 // a TXOP on another link, release the channel and request the main PHY to switch channel.
860 // It may be decided to have the main PHY start a TXOP on the other link a PIFS after the
861 // channel switch (e.g., MAC header information is not used and AllowUlTxopInRx is true).
862 // Thus, when the ICF is received on this link, it is not dropped but, when the CTS must
863 // be transmitted, the main PHY has already started transmitting on the other link. In
864 // such a case, do not respond to the ICF.
865 if (mainPhy->IsStateSwitching() || m_mac->GetLinkForPhy(mainPhy) != m_linkId)
866 {
867 NS_LOG_DEBUG("Main PHY is switching or operating on another link, abort ICF response");
868 return true;
869 }
870 }
871 return false;
872}
873
874void
876 const CtrlTriggerHeader& trigger,
877 double muRtsSnr)
878{
879 NS_LOG_FUNCTION(this << muRtsHdr << trigger << muRtsSnr);
880
882 {
883 return;
884 }
885 HeFrameExchangeManager::SendCtsAfterMuRts(muRtsHdr, trigger, muRtsSnr);
886}
887
888void
890 const WifiMacHeader& hdr)
891{
892 NS_LOG_FUNCTION(this << trigger << hdr);
893
894 if (trigger.IsBsrp() && EmlsrClientCannotRespondToIcf())
895 {
896 return;
897 }
899}
900
901void
903{
904 NS_LOG_FUNCTION(this);
905
906 for (const auto& address : clients)
907 {
908 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
909 {
910 // EMLSR client switched to listening operations if it was protected, otherwise
911 // simply unblock transmissions
912 m_protectedStas.contains(address) ? EmlsrSwitchToListening(address, Seconds(0))
913 : (void)(UnblockEmlsrLinksIfAllowed(address));
914 m_protectedStas.erase(address);
915 }
916 }
917}
918
919void
926
927bool
929{
930 NS_LOG_FUNCTION(this);
931
932 if (m_apMac)
933 {
934 if (const auto apEmlsrManager = m_apMac->GetApEmlsrManager();
935 apEmlsrManager && IsCrossLinkCollision(m_sentRtsTo))
936 {
937 return apEmlsrManager->UpdateCwAfterFailedIcf();
938 }
939 }
940
942}
943
944void
945EhtFrameExchangeManager::TbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicitedStations)
946{
947 NS_LOG_FUNCTION(this << psduMap << nSolicitedStations);
948
949 const auto& staMissedTbPpduFrom = m_txTimer.GetStasExpectedToRespond();
950 const auto crossLinkCollision = IsCrossLinkCollision(staMissedTbPpduFrom);
951
952 if (staMissedTbPpduFrom.size() != nSolicitedStations)
953 {
954 // some STAs replied, hence the transmission succeeded. EMLSR clients that did not
955 // respond are switching back to listening operations
956 SwitchToListeningOrUnblockLinks(staMissedTbPpduFrom);
957 }
958
959 const auto apEmlsrManager = m_apMac->GetApEmlsrManager();
960 const auto updateFailedCw =
961 crossLinkCollision && apEmlsrManager ? apEmlsrManager->UpdateCwAfterFailedIcf() : true;
962 DoTbPpduTimeout(psduMap, nSolicitedStations, updateFailedCw);
963}
964
965void
967 std::size_t nSolicitedStations)
968{
969 NS_LOG_FUNCTION(this << psduMap << nSolicitedStations);
970
971 const auto& staMissedTbPpduFrom = m_txTimer.GetStasExpectedToRespond();
972
973 if (staMissedTbPpduFrom.size() != nSolicitedStations)
974 {
975 // some STAs replied, hence the transmission succeeded. EMLSR clients that did not
976 // respond are switching back to listening operations
977 SwitchToListeningOrUnblockLinks(staMissedTbPpduFrom);
978 }
979
980 HeFrameExchangeManager::BlockAcksInTbPpduTimeout(psduMap, nSolicitedStations);
981}
982
983bool
985 const std::set<Mac48Address>& staMissedResponseFrom) const
986{
987 NS_LOG_FUNCTION(this << staMissedResponseFrom.size());
988
989 // check if all the clients that did not respond to the ICF are EMLSR clients that have sent
990 // (or are sending) a frame to the AP on another link
991 auto crossLinkCollision = true;
992
993 // we blocked transmissions on the other EMLSR links for the EMLSR clients we sent the ICF to.
994 // For clients that did not respond, we can unblock transmissions if there is no ongoing
995 // UL TXOP held by that client
996 for (const auto& address : staMissedResponseFrom)
997 {
998 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
999 {
1000 crossLinkCollision = false;
1001 continue;
1002 }
1003
1004 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
1005 NS_ASSERT(mldAddress);
1006
1007 std::set<uint8_t> linkIds; // all EMLSR links of EMLSR client
1008 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
1009 {
1010 if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress) &&
1011 linkId != m_linkId)
1012 {
1013 linkIds.insert(linkId);
1014 }
1015 }
1016
1017 if (std::any_of(linkIds.cbegin(),
1018 linkIds.cend(),
1019 /* lambda returning true if an UL TXOP is ongoing on the given link ID */
1020 [=, this](uint8_t id) {
1021 auto ehtFem = StaticCast<EhtFrameExchangeManager>(
1022 m_mac->GetFrameExchangeManager(id));
1023 return ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
1024 m_mac->GetMldAddress(ehtFem->m_txopHolder.value()) == mldAddress;
1025 }))
1026 {
1027 // an UL TXOP is ongoing on one EMLSR link, do not unblock links
1028 continue;
1029 }
1030
1031 // no UL TXOP is ongoing on any EMLSR link; if the EMLSR client is not transmitting a
1032 // frame to the AP on any EMLSR link, then the lack of response to the MU-RTS was not
1033 // caused by a simultaneous UL transmission
1034 if (std::none_of(linkIds.cbegin(),
1035 linkIds.cend(),
1036 /* lambda returning true if an MPDU from the EMLSR client is being received
1037 on the given link ID */
1038 [=, this](uint8_t id) {
1039 auto macHdr = m_mac->GetFrameExchangeManager(id)->GetReceivedMacHdr();
1040 return macHdr.has_value() &&
1041 m_mac->GetMldAddress(macHdr->get().GetAddr2()) == mldAddress;
1042 }))
1043 {
1044 crossLinkCollision = false;
1045 }
1046 }
1047
1048 return crossLinkCollision;
1049}
1050
1051void
1053 WifiMode rtsTxMode,
1054 double rtsSnr)
1055{
1056 NS_LOG_FUNCTION(this << rtsHdr << rtsTxMode << rtsSnr);
1057
1058 if (m_apMac && GetWifiRemoteStationManager()->GetEmlsrEnabled(rtsHdr.GetAddr2()))
1059 {
1060 // we are going to send a CTS to an EMLSR client, transmissions to such EMLSR client
1061 // must be blocked on the other EMLSR links
1062
1063 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(rtsHdr.GetAddr2());
1064 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << rtsHdr.GetAddr2());
1065
1066 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
1067 {
1068 if (linkId != m_linkId &&
1069 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
1070 {
1071 // check that other links are blocked as expected
1072 WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *mldAddress, 0);
1073 auto mask =
1074 m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
1075 NS_ASSERT_MSG(mask, "No mask for client " << *mldAddress << " on link " << +linkId);
1076 if (!mask->test(
1077 static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK)))
1078 {
1079 NS_ASSERT_MSG(false,
1080 "Transmissions to " << *mldAddress << " on link " << +linkId
1081 << " are not blocked");
1082 // in case asserts are disabled, block transmissions on the other links because
1083 // this is what we need
1085 *mldAddress,
1086 {linkId});
1087 }
1088 }
1089 }
1090 }
1091
1092 HeFrameExchangeManager::SendCtsAfterRts(rtsHdr, rtsTxMode, rtsSnr);
1093}
1094
1095bool
1097 uint16_t aid,
1098 const Mac48Address& address) const
1099{
1100 NS_LOG_FUNCTION(this << psdu << aid << address);
1101
1102 // Sec. 35.3.17 of 802.11be D3.0:
1103 // The non-AP MLD shall be switched back to the listening operation on the EMLSR links after
1104 // the EMLSR transition delay time if [...] the non-AP STA affiliated with the non-AP MLD
1105 // does not detect [...] any of the following frames:
1106 // - an individually addressed frame with the RA equal to the MAC address of the non-AP STA
1107 // affiliated with the non-AP MLD
1108 if (psdu->GetAddr1() == address)
1109 {
1110 return false;
1111 }
1112
1113 // - a Trigger frame that has one of the User Info fields addressed to the non-AP STA
1114 // affiliated with the non-AP MLD
1115 for (const auto& mpdu : *PeekPointer(psdu))
1116 {
1117 if (mpdu->GetHeader().IsTrigger())
1118 {
1119 CtrlTriggerHeader trigger;
1120 mpdu->GetPacket()->PeekHeader(trigger);
1121 if (trigger.FindUserInfoWithAid(aid) != trigger.end())
1122 {
1123 return false;
1124 }
1125 }
1126 }
1127
1128 // - a CTS-to-self frame with the RA equal to the MAC address of the AP affiliated with
1129 // the AP MLD
1130 if (psdu->GetHeader(0).IsCts())
1131 {
1132 if (m_apMac && psdu->GetAddr1() == m_self)
1133 {
1134 return false;
1135 }
1136 if (m_staMac && psdu->GetAddr1() == m_bssid)
1137 {
1138 return false;
1139 }
1140 }
1141
1142 // - a Multi-STA BlockAck frame that has one of the Per AID TID Info fields addressed to
1143 // the non-AP STA affiliated with the non-AP MLD
1144 if (psdu->GetHeader(0).IsBlockAck())
1145 {
1146 CtrlBAckResponseHeader blockAck;
1147 psdu->GetPayload(0)->PeekHeader(blockAck);
1148 if (blockAck.IsMultiSta() && !blockAck.FindPerAidTidInfoWithAid(aid).empty())
1149 {
1150 return false;
1151 }
1152 }
1153
1154 // - a NDP Announcement frame that has one of the STA Info fields addressed to the non-AP
1155 // STA affiliated with the non-AP MLD and a sounding NDP
1156 // TODO NDP Announcement frame not supported yet
1157
1158 return true;
1159}
1160
1161void
1163{
1164 NS_LOG_FUNCTION(this);
1165
1166 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1167 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
1168 {
1169 NS_LOG_DEBUG("Reset the counter of TXOP attempts allowed while "
1170 "MediumSyncDelay is running");
1171 m_staMac->GetEmlsrManager()->ResetMediumSyncDelayNTxops(m_linkId);
1172 }
1173
1175}
1176
1177void
1179{
1180 NS_LOG_FUNCTION(this << forceCurrentCw);
1181
1182 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1183 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
1184 {
1185 NS_LOG_DEBUG("Decrement the remaining number of TXOP attempts allowed while "
1186 "MediumSyncDelay is running");
1187 m_staMac->GetEmlsrManager()->DecrementMediumSyncDelayNTxops(m_linkId);
1188 }
1189
1191}
1192
1193void
1195{
1196 NS_LOG_FUNCTION(this << txop);
1197
1198 if (m_apMac)
1199 {
1200 // the channel has been released; all EMLSR clients are switching back to
1201 // listening operation
1202 for (const auto& address : m_protectedStas)
1203 {
1204 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
1205 {
1206 EmlsrSwitchToListening(address, Seconds(0));
1207 }
1208 }
1209 }
1210 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
1211 {
1212 // Notify the UL TXOP end to the EMLSR Manager
1213 auto edca = DynamicCast<QosTxop>(txop);
1214 NS_ASSERT(edca);
1215 auto txopStart = edca->GetTxopStartTime(m_linkId);
1216
1217 NS_ASSERT(m_staMac->GetEmlsrManager());
1218 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId,
1219 (!txopStart || *txopStart == Simulator::Now()),
1221 }
1222
1224}
1225
1226void
1228{
1229 NS_LOG_FUNCTION(this << psdu << txVector);
1230
1231 // In addition, the timer resets to zero when any of the following events occur:
1232 // — The STA receives an MPDU
1233 // (Sec. 35.3.16.8.1 of 802.11be D3.1)
1234 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1235 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
1236 {
1237 m_staMac->GetEmlsrManager()->CancelMediumSyncDelayTimer(m_linkId);
1238 }
1239
1240 if (m_apMac)
1241 {
1242 // we iterate over protected STAs to consider only the case when the AP is the TXOP holder.
1243 // The AP received a PSDU from a non-AP STA; given that the AP is the TXOP holder, this
1244 // PSDU has been likely solicited by the AP. In most of the cases, we identify which EMLSR
1245 // clients are no longer involved in the TXOP when the AP transmits the frame soliciting
1246 // response(s) from client(s). This is not the case, for example, for the acknowledgment
1247 // in SU format of a DL MU PPDU, where all the EMLSR clients (but one) switch to listening
1248 // operation after the immediate response (if any) by one of the EMLSR clients.
1249 for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
1250 {
1251 // TB PPDUs are received by the AP at distinct times, so it is difficult to take a
1252 // decision based on one of them. However, clients transmitting TB PPDUs are identified
1253 // by the soliciting Trigger Frame, thus we have already identified (when sending the
1254 // Trigger Frame) which EMLSR clients have switched to listening operation.
1255 // If the PSDU is not carried in a TB PPDU, we can determine whether this EMLSR client
1256 // is switching to listening operation by checking whether the AP is expecting a
1257 // response from it.
1258 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) && !txVector.IsUlMu() &&
1259 !m_txTimer.GetStasExpectedToRespond().contains(*clientIt))
1260 {
1261 EmlsrSwitchToListening(*clientIt, Seconds(0));
1262 // this client is no longer involved in the current TXOP
1263 clientIt = m_protectedStas.erase(clientIt);
1264 }
1265 else
1266 {
1267 clientIt++;
1268 }
1269 }
1270 }
1271
1273}
1274
1275void
1277{
1278 NS_LOG_FUNCTION(this << psdu << txVector);
1279
1281
1282 if (m_apMac && m_apMac->GetApEmlsrManager())
1283 {
1284 m_apMac->GetApEmlsrManager()->NotifyPsduRxOk(m_linkId, psdu);
1285 }
1286
1287 if (m_apMac && m_txopHolder == psdu->GetAddr2() &&
1288 GetWifiRemoteStationManager()->GetEmlsrEnabled(*m_txopHolder))
1289 {
1291 {
1292 // an EMLSR client has started an UL TXOP. Start the ongoingTxopEnd timer so that
1293 // the next call to UpdateTxopEndOnRxEnd does its job
1296 }
1297
1298 UpdateTxopEndOnRxEnd(psdu->GetDuration());
1299 }
1300
1302 {
1303 if (GetEmlsrSwitchToListening(psdu, m_staMac->GetAssociationId(), m_self))
1304 {
1305 // we are no longer involved in the TXOP and switching to listening mode
1307 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
1308 }
1309 else
1310 {
1311 UpdateTxopEndOnRxEnd(psdu->GetDuration());
1312 }
1313 }
1314
1315 if (m_staMac && m_icfReceived)
1316 {
1317 // notify the EMLSR manager
1318 m_staMac->GetEmlsrManager()->NotifyIcfReceived(m_linkId);
1319 m_icfReceived = false;
1320 }
1321}
1322
1323bool
1325 const WifiTxVector& txVector)
1326{
1327 NS_LOG_FUNCTION(this);
1328
1329 auto sender = hdr.GetAddr2();
1330
1332 {
1333 NS_LOG_DEBUG("A TXOP is already ongoing");
1334 return false;
1335 }
1336
1337 if (auto holder = FindTxopHolder(hdr, txVector); holder != sender)
1338 {
1339 NS_LOG_DEBUG("Sender (" << sender << ") differs from the TXOP holder ("
1340 << (holder ? Address(*holder) : Address()) << ")");
1341 return false;
1342 }
1343
1344 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(sender))
1345 {
1346 NS_LOG_DEBUG("Sender (" << sender << ") is not an EMLSR client");
1347 return false;
1348 }
1349
1350 NS_LOG_DEBUG("EMLSR client " << sender << " is starting a TXOP");
1351
1352 // Block transmissions for this EMLSR client on other links
1353 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(sender);
1354 NS_ASSERT(mldAddress);
1355
1356 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
1357 {
1358 if (linkId != m_linkId &&
1359 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
1360 {
1362 *mldAddress,
1363 {linkId});
1364
1365 // the AP MLD may have sent an ICF to the EMLSR client on this link while the EMLSR
1366 // client was starting a TXOP on another link. To be safe, besides blocking
1367 // transmissions, remove the EMLSR client from the protected stations on this link
1368 auto linkAddr =
1369 m_mac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(*mldAddress);
1370 NS_ASSERT(linkAddr.has_value());
1371 auto ehtFem =
1372 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
1373 NS_LOG_DEBUG("Remove " << *linkAddr << " from protected STAs");
1374 ehtFem->m_protectedStas.erase(*linkAddr);
1375 ehtFem->m_sentRtsTo.erase(*linkAddr);
1376 ehtFem->m_sentFrameTo.erase(*linkAddr);
1377 }
1378 }
1379
1380 // Make sure that transmissions for this EMLSR client are not blocked on this link
1381 // (the AP MLD may have sent an ICF on another link right before receiving this MPDU,
1382 // thus transmissions on this link may have been blocked)
1384 *mldAddress,
1385 {m_linkId});
1386
1387 // Stop the transition delay timer for this EMLSR client, if any is running
1388 if (auto it = m_transDelayTimer.find(*mldAddress);
1389 it != m_transDelayTimer.end() && it->second.IsPending())
1390 {
1391 it->second.PeekEventImpl()->Invoke();
1392 it->second.Cancel();
1393 }
1394
1395 return true;
1396}
1397
1398EventId&
1403
1404void
1406{
1407 NS_LOG_FUNCTION(this << psdu);
1408
1409 if (m_apMac && m_apMac->GetApEmlsrManager())
1410 {
1411 m_apMac->GetApEmlsrManager()->NotifyPsduRxError(m_linkId, psdu);
1412 }
1413}
1414
1415void
1417 RxSignalInfo rxSignalInfo,
1418 const WifiTxVector& txVector,
1419 bool inAmpdu)
1420{
1421 NS_LOG_FUNCTION(this << *mpdu << rxSignalInfo << txVector << inAmpdu);
1422
1423 // The received MPDU is either broadcast or addressed to this station
1424 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
1425
1426 const auto& hdr = mpdu->GetHeader();
1427 const auto sender = hdr.GetAddr2();
1428
1429 if (hdr.IsTrigger())
1430 {
1431 if (!m_staMac)
1432 {
1433 return; // Trigger Frames are only processed by STAs
1434 }
1435
1436 CtrlTriggerHeader trigger;
1437 mpdu->GetPacket()->PeekHeader(trigger);
1438
1439 if (hdr.GetAddr1() != m_self &&
1440 (!hdr.GetAddr1().IsBroadcast() || !m_staMac->IsAssociated() ||
1441 sender != m_bssid // not sent by the AP this STA is associated with
1442 || trigger.FindUserInfoWithAid(m_staMac->GetAssociationId()) == trigger.end()))
1443 {
1444 return; // not addressed to us
1445 }
1446
1447 if ((trigger.IsMuRts() || trigger.IsBsrp()) && !m_ongoingTxopEnd.IsPending() &&
1448 m_staMac->IsEmlsrLink(m_linkId))
1449 {
1450 // this is an initial Control frame
1451 if (DropReceivedIcf())
1452 {
1453 return;
1454 }
1455
1456 auto emlsrManager = m_staMac->GetEmlsrManager();
1457 NS_ASSERT(emlsrManager);
1458
1459 m_icfReceived = true;
1460
1461 // we just got involved in a DL TXOP. Check if we are still involved in the TXOP in a
1462 // SIFS (we are expected to reply by sending a CTS frame)
1464 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + m_phy->GetSifs()).As(Time::S));
1467 this,
1468 sender);
1469 }
1470 }
1471
1473 {
1474 NS_LOG_DEBUG("Drop received MPDU: " << *mpdu);
1475 return;
1476 }
1477
1478 HeFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1479
1480 if (m_apMac && GetWifiRemoteStationManager()->GetEmlsrEnabled(sender))
1481 {
1482 if (hdr.IsRts() && !m_sendCtsEvent.IsPending())
1483 {
1484 // received RTS but did not send CTS (e.g., NAV busy), start transition delay
1485 EmlsrSwitchToListening(sender, Time{0});
1486 return;
1487 }
1488
1489 // if the AP MLD received an MPDU from an EMLSR client that is starting an UL TXOP,
1490 // block transmissions to the EMLSR client on other links
1491 CheckEmlsrClientStartingTxop(hdr, txVector);
1492 }
1493}
1494
1495void
1497 const RxSignalInfo& rxSignalInfo,
1498 const WifiTxVector& txVector,
1499 const std::vector<bool>& perMpduStatus)
1500{
1502 this << *psdu << rxSignalInfo << txVector << perMpduStatus.size()
1503 << std::all_of(perMpduStatus.begin(), perMpduStatus.end(), [](bool v) { return v; }));
1504
1505 if (ShallDropReceivedMpdu(*psdu->begin()))
1506 {
1507 return;
1508 }
1509
1510 HeFrameExchangeManager::EndReceiveAmpdu(psdu, rxSignalInfo, txVector, perMpduStatus);
1511}
1512
1513bool
1515{
1516 NS_LOG_FUNCTION(this << *mpdu);
1517
1518 // this function only checks frames that shall be dropped by an EMLSR client
1519 if (!m_staMac || !m_staMac->IsEmlsrLink(m_linkId))
1520 {
1521 return false;
1522 }
1523
1524 const auto& hdr = mpdu->GetHeader();
1525
1526 // We impose that an aux PHY is only able to receive an ICF, a CF-End, a CTS or a management
1527 // frame (we are interested in receiving mainly Beacon frames). Note that other frames are
1528 // still post-processed, e.g., used to set the NAV and the TXOP holder.
1529 // The motivation is that, e.g., an AP MLD may send an ICF to EMLSR clients A and B;
1530 // A responds while B does not; the AP MLD sends a DL MU PPDU to both clients followed
1531 // by an MU-BAR to solicit a BlockAck from both clients. If an aux PHY of client B is
1532 // operating on this link, the MU-BAR will be received and a TB PPDU response sent
1533 // through the aux PHY.
1534 if (hdr.IsMgt() || hdr.IsCts() || hdr.IsCfEnd() || (hdr.IsData() && hdr.GetAddr1().IsGroup()))
1535 {
1536 return false;
1537 }
1538
1539 // other frames cannot be received by an aux PHY
1540 if (m_mac->GetLinkForPhy(m_staMac->GetEmlsrManager()->GetMainPhyId()) != m_linkId)
1541 {
1542 NS_LOG_DEBUG("Dropping " << *mpdu << " received by an aux PHY on link " << +m_linkId);
1543 return true;
1544 }
1545
1546 // other frames cannot be received by the main PHY when not involved in any TXOP
1547 if (!m_ongoingTxopEnd.IsPending() &&
1548 std::none_of(wifiAcList.cbegin(), wifiAcList.cend(), [=, this](const auto& aciAcPair) {
1549 return m_mac->GetQosTxop(aciAcPair.first)->GetTxopStartTime(m_linkId).has_value();
1550 }))
1551 {
1552 NS_LOG_DEBUG("Dropping " << *mpdu << " received by main PHY on link " << +m_linkId
1553 << " while no TXOP is ongoing");
1554 return true;
1555 }
1556
1557 // other frames can be received by the main PHY when involved in a TXOP
1558 return false;
1559}
1560
1561bool
1563{
1564 NS_LOG_FUNCTION(this);
1565
1566 auto emlsrManager = m_staMac->GetEmlsrManager();
1567 NS_ASSERT(emlsrManager);
1568
1569 if (UsingOtherEmlsrLink())
1570 {
1571 // we received an ICF on a link that is blocked because another EMLSR link is
1572 // being used. Check if there is an ongoing DL TXOP on the other EMLSR link
1573 auto apMldAddress = GetWifiRemoteStationManager()->GetMldAddress(m_bssid);
1574 NS_ASSERT_MSG(apMldAddress, "MLD address not found for " << m_bssid);
1575
1576 if (auto it = std::find_if(
1577 m_staMac->GetLinkIds().cbegin(),
1578 m_staMac->GetLinkIds().cend(),
1579 /* lambda to find an EMLSR link on which there is an ongoing DL TXOP */
1580 [=, this](uint8_t linkId) {
1581 auto ehtFem =
1582 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
1583 return linkId != m_linkId && m_staMac->IsEmlsrLink(linkId) &&
1584 ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
1585 m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(
1586 *ehtFem->m_txopHolder) == apMldAddress;
1587 });
1588 it != m_staMac->GetLinkIds().cend())
1589 {
1590 // AP is not expected to send ICFs on two links. If an ICF
1591 // has been received on this link, it means that the DL TXOP
1592 // on the other link terminated (e.g., the AP did not
1593 // receive our response)
1594 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(*it))
1595 ->m_ongoingTxopEnd.Cancel();
1596 // we are going to start a TXOP on this link; unblock
1597 // transmissions on this link, the other links will be
1598 // blocked subsequently
1600 }
1601 else
1602 {
1603 // We get here likely because transmission on the other EMLSR link
1604 // started before the reception of the ICF ended. We drop this ICF and let the
1605 // UL TXOP continue.
1606 NS_LOG_DEBUG("Drop ICF because another EMLSR link is being used");
1608 return true;
1609 }
1610 }
1611 /**
1612 * It might happen that, while the aux PHY is receiving an ICF, the main PHY is
1613 * completing a TXOP on another link or is returning to the primary link after a TXOP
1614 * is completed on another link. In order to respond to the ICF, it is necessary that
1615 * the main PHY has enough time to switch and be ready to operate on this link by the
1616 * end of the ICF padding.
1617 *
1618 * TXOP end
1619 * │
1620 * ┌───┐ another
1621 * AP MLD │ACK│ link
1622 * ───────────┬─────────┬┴───┴───────────────────────────────────────
1623 * EMLSR │ QoS │ │ main PHY
1624 * client │ Data │ │
1625 * └─────────┘ │
1626 * ┌─────┬───┐ this
1627 * AP MLD │ ICF │pad│ link
1628 * ────────────────────┴─────┴───┴───────────────────────────────────
1629 * aux PHY
1630 */
1631 else if (auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId());
1632 mainPhy != m_phy)
1633 {
1634 const auto delay = mainPhy->GetChannelSwitchDelay();
1635 auto lastTime = mainPhy->GetState()->GetLastTime({WifiPhyState::TX});
1636 auto reason = WifiIcfDrop::NOT_ENOUGH_TIME_TX;
1637
1638 if (auto lastSwitch = mainPhy->GetState()->GetLastTime({WifiPhyState::SWITCHING});
1639 lastSwitch > lastTime)
1640 {
1641 lastTime = lastSwitch;
1643 }
1644 if (auto lastSleep = mainPhy->GetState()->GetLastTime({WifiPhyState::SLEEP});
1645 lastSleep > lastTime)
1646 {
1647 lastTime = lastSleep;
1649 }
1650 // ignore RX state for now
1651
1652 if (lastTime > Simulator::Now() - delay)
1653 {
1655 "Drop ICF due to not enough time for the main PHY to switch link; reason = "
1656 << reason);
1657 m_icfDropCallback(reason, m_linkId);
1658 return true;
1659 }
1660 }
1661 return false;
1662}
1663
1664void
1665EhtFrameExchangeManager::TxopEnd(const std::optional<Mac48Address>& txopHolder)
1666{
1667 NS_LOG_FUNCTION(this << txopHolder.has_value());
1668
1670 {
1671 // we may get here because the PHY has not issued the PHY-RXSTART.indication before
1672 // the expiration of the timer started to detect new received frames, but the PHY is
1673 // currently decoding the PHY header of a PPDU, so let's wait some more time to check
1674 // if we receive a PHY-RXSTART.indication when the PHY is done decoding the PHY header
1675 NS_LOG_DEBUG("PHY is decoding the PHY header of PPDU, postpone TXOP end");
1678 this,
1679 txopHolder);
1680 return;
1681 }
1682
1683 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
1684 {
1685 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
1686 }
1687 else if (m_apMac && txopHolder && GetWifiRemoteStationManager()->GetEmlsrEnabled(*txopHolder))
1688 {
1689 // EMLSR client terminated its TXOP and is back to listening operation
1690 EmlsrSwitchToListening(*txopHolder, Seconds(0));
1691 }
1692}
1693
1694void
1696{
1697 NS_LOG_FUNCTION(this << txDuration.As(Time::MS) << durationId.As(Time::US));
1698
1700 {
1701 // nothing to do
1702 return;
1703 }
1704
1706 Time delay;
1707
1708 if (m_txTimer.IsRunning())
1709 {
1710 // the TX timer is running, hence we are expecting a response. Postpone the TXOP end
1711 // to match the TX timer (which is long enough to get the PHY-RXSTART.indication for
1712 // the response)
1713 delay = m_txTimer.GetDelayLeft();
1714 }
1715 else if (durationId <= m_phy->GetSifs())
1716 {
1717 // the TX timer is not running, hence no response is expected, and the Duration/ID value
1718 // is less than or equal to a SIFS; the TXOP will end after this transmission
1719 NS_LOG_DEBUG("Assume TXOP will end based on Duration/ID value");
1720 delay = txDuration;
1721 }
1722 else
1723 {
1724 // the TX Timer is not running, hence no response is expected (e.g., we are
1725 // transmitting a CTS after ICS). The TXOP holder may transmit a frame a SIFS
1726 // after the end of this PPDU, hence we need to postpone the TXOP end in order to
1727 // get the PHY-RXSTART.indication
1728 delay = txDuration + m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY;
1729 }
1730
1731 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
1734}
1735
1736void
1738{
1739 NS_LOG_FUNCTION(this << psduDuration.As(Time::MS));
1740
1741 if (!m_ongoingTxopEnd.IsPending() || !psduDuration.IsStrictlyPositive())
1742 {
1743 // nothing to do
1744 return;
1745 }
1746
1747 // postpone the TXOP end until after the reception of the PSDU is completed
1749
1750 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + psduDuration).As(Time::S));
1753 this,
1754 m_txopHolder);
1755}
1756
1757void
1759{
1760 NS_LOG_FUNCTION(this << durationId.As(Time::US));
1761
1763 {
1764 // nothing to do
1765 return;
1766 }
1767
1769
1770 // if the Duration/ID of the received frame is less than a SIFS, the TXOP
1771 // is terminated
1772 if (durationId <= m_phy->GetSifs())
1773 {
1774 NS_LOG_DEBUG("Assume TXOP ended based on Duration/ID value");
1776 return;
1777 }
1778
1779 // we may send a response after a SIFS or we may receive another frame after a SIFS.
1780 // Postpone the TXOP end by considering the latter (which takes longer)
1781 auto delay = m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY;
1782 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
1785}
1786
1787} // namespace ns3
a polymophic address class
Definition address.h:90
Headers for BlockAck response.
std::vector< uint32_t > FindPerAidTidInfoWithAid(uint16_t aid) const
For Multi-STA Block Acks, get the indices of the Per AID TID Info subfields carrying the given AID in...
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 PostProcessFrame(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector) override
Perform actions that are possibly needed after receiving any frame, independently of whether the fram...
void TransmissionFailed(bool forceCurrentCw=false) override
Take necessary actions upon a transmission failure.
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...
TracedCallback< WifiIcfDrop, uint8_t > m_icfDropCallback
ICF drop reason traced callback (WifiMac exposes this trace source)
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 PreProcessFrame(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector) override
Perform actions that are possibly needed when receiving any frame, independently of whether the frame...
void NavResetTimeout() override
Reset the NAV upon expiration of the NAV reset timer.
void ForwardPsduDown(Ptr< const WifiPsdu > psdu, WifiTxVector &txVector) override
Forward a PSDU down to the PHY layer.
void PsduRxError(Ptr< const WifiPsdu > psdu) override
This method is called when the reception of a PSDU fails.
void 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.
bool UnblockEmlsrLinksIfAllowed(Mac48Address address)
Unblock transmissions on all the links of the given EMLSR client, provided that the latter is not inv...
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.
bool ShallDropReceivedMpdu(Ptr< const WifiMpdu > mpdu) const
Check if the MPDU that has been received on this link shall be dropped.
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.
void UpdateTxopEndOnRxEnd(Time durationId)
Update the TXOP end timer when a frame reception ends.
void EndReceiveAmpdu(Ptr< const WifiPsdu > psdu, const RxSignalInfo &rxSignalInfo, const WifiTxVector &txVector, const std::vector< bool > &perMpduStatus) override
This method is called when the reception of an A-MPDU including multiple MPDUs is completed.
bool CheckEmlsrClientStartingTxop(const WifiMacHeader &hdr, const WifiTxVector &txVector)
Check if the frame received (or being received) is sent by an EMLSR client to start an UL TXOP.
void TransmissionSucceeded() override
Take necessary actions upon a transmission success.
bool GetEmlsrSwitchToListening(Ptr< const WifiPsdu > psdu, uint16_t aid, const Mac48Address &address) const
static TypeId GetTypeId()
Get the type ID.
void DoDispose() override
Destructor implementation.
std::unordered_map< Mac48Address, EventId, WifiAddressHash > m_transDelayTimer
MLD address-indexed map of transition delay timers.
bool m_icfReceived
whether an ICF has been received and needs to be notified to the EMLSR manager after post-processing ...
void NotifyChannelReleased(Ptr< Txop > txop) override
Notify the given Txop that channel has been released.
EventId m_ongoingTxopEnd
event indicating the possible end of the current TXOP (of which we are not the holder)
void RxStartIndication(WifiTxVector txVector, Time psduDuration) override
void ReceiveMpdu(Ptr< const WifiMpdu > mpdu, RxSignalInfo rxSignalInfo, const WifiTxVector &txVector, bool inAmpdu) override
This method handles the reception of an MPDU (possibly included in an A-MPDU)
void UpdateTxopEndOnTxStart(Time txDuration, Time durationId)
Update the TXOP end timer when starting a frame transmission.
void GenerateInDeviceInterference(uint8_t linkId, Time duration, Watt_u txPower)
Generate an in-device interference of the given power on the given link for the given duration.
void SendQosNullFramesInTbPpdu(const CtrlTriggerHeader &trigger, const WifiMacHeader &hdr) override
Send QoS Null frames in response to a Basic or BSRP Trigger Frame.
void SendCtsAfterRts(const WifiMacHeader &rtsHdr, WifiMode rtsTxMode, double rtsSnr) override
Send CTS after receiving RTS.
void UpdateTxopEndOnRxStartIndication(Time psduDuration)
Update the TXOP end timer when receiving a PHY-RXSTART.indication.
void TxopEnd(const std::optional< Mac48Address > &txopHolder)
Take actions when a TXOP (of which we are not the holder) ends.
void CtsAfterMuRtsTimeout(Ptr< WifiMpdu > muRts, const WifiTxVector &txVector) override
Called when no CTS frame is received after an MU-RTS.
void NotifySwitchingEmlsrLink(Ptr< WifiPhy > phy, uint8_t linkId, Time delay)
Notify that the given PHY will switch channel to operate on another EMLSR link after the given delay.
bool StartTransmission(Ptr< Txop > edca, MHz_u allowedWidth) override
Request the FrameExchangeManager to start a frame exchange sequence.
void SetLinkId(uint8_t linkId) override
Set the ID of the link this Frame Exchange Manager is associated with.
An identifier for simulation events.
Definition event-id.h:45
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition event-id.cc:44
bool IsPending() const
This method is syntactic sugar for !IsExpired().
Definition event-id.cc:65
std::set< Mac48Address > m_sentRtsTo
the STA(s) which we sent an RTS to (waiting for CTS)
uint8_t m_linkId
the ID of the link this object is associated with
Ptr< WifiMac > m_mac
the MAC layer on this station
virtual void ResetPhy()
Remove WifiPhy associated with this FrameExchangeManager.
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager() const
Ptr< MacTxMiddle > m_txMiddle
the MAC TX Middle on this station
Mac48Address m_self
the MAC address of this device
WifiTxTimer m_txTimer
the timer set upon frame transmission
virtual void SendCtsAfterRts(const WifiMacHeader &rtsHdr, WifiMode rtsTxMode, double rtsSnr)
Send CTS after receiving RTS.
std::set< Mac48Address > m_protectedStas
STAs that have replied to an RTS in this TXOP.
Mac48Address GetAddress() const
Get the MAC address.
virtual void SetLinkId(uint8_t linkId)
Set the ID of the link this Frame Exchange Manager is associated with.
virtual void NotifyChannelReleased(Ptr< Txop > txop)
Notify the given Txop that channel has been released.
Ptr< WifiAckManager > GetAckManager() const
Get the Acknowledgment Manager used by this node.
Ptr< WifiProtectionManager > GetProtectionManager() const
Get the Protection Manager used by this node.
EventId m_sendCtsEvent
the event to send a CTS after an (MU-)RTS
Ptr< WifiPhy > m_phy
the PHY layer on this station
virtual void PreProcessFrame(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector)
Perform actions that are possibly needed when receiving any frame, independently of whether the frame...
Ptr< ApWifiMac > m_apMac
AP MAC layer pointer (null if not an AP)
Mac48Address m_bssid
BSSID address (Mac48Address)
virtual void TransmissionFailed(bool forceCurrentCw=false)
Take necessary actions upon a transmission failure.
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
std::optional< Mac48Address > FindTxopHolder(const WifiMacHeader &hdr, const WifiTxVector &txVector) override
Determine the holder of the TXOP, if possible, based on the received frame.
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.
void EndReceiveAmpdu(Ptr< const WifiPsdu > psdu, const RxSignalInfo &rxSignalInfo, const WifiTxVector &txVector, const std::vector< bool > &perMpduStatus) override
This method is called when the reception of an A-MPDU including multiple MPDUs is completed.
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.
void ReceiveMpdu(Ptr< const WifiMpdu > mpdu, RxSignalInfo rxSignalInfo, const WifiTxVector &txVector, bool inAmpdu) override
This method handles the reception of an MPDU (possibly included in an A-MPDU)
virtual void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector &txVector)
Forward a map of PSDUs down to the PHY layer.
void PostProcessFrame(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector) override
Perform actions that are possibly needed after receiving any frame, independently of whether the fram...
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.
void TransmissionSucceeded() override
Take necessary actions upon a transmission success.
Ptr< MpduAggregator > m_mpduAggregator
A-MPDU aggregator.
virtual void ForwardPsduDown(Ptr< const WifiPsdu > psdu, WifiTxVector &txVector)
Forward a PSDU down to the PHY layer.
Ptr< MsduAggregator > m_msduAggregator
A-MSDU aggregator.
an EUI-48 address
Implement the header for Action frames of type EML Operating Mode Notification.
Smart pointer class similar to boost::intrusive_ptr.
std::optional< Mac48Address > m_txopHolder
MAC address of the TXOP holder.
virtual Ptr< WifiMpdu > CreateAliasIfNeeded(Ptr< WifiMpdu > mpdu) const
Create an alias of the given MPDU for transmission by this Frame Exchange Manager.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:561
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition simulator.h:595
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
@ S
second
Definition nstime.h:105
bool IsZero() const
Exactly equivalent to t == 0.
Definition nstime.h:304
void StartAccessAfterEvent(uint8_t linkId, bool hadFramesToTransmit, bool checkMediumBusy)
Request channel access on the given link after the occurrence of an event that possibly requires to g...
Definition txop.cc:709
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.
represent a single transmission mode
Definition wifi-mode.h:40
uint64_t GetDataRate(MHz_u channelWidth, Time guardInterval, uint8_t nss) const
Definition wifi-mode.cc:111
Time GetSlot() const
Return the slot duration for this PHY.
Definition wifi-phy.cc:841
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition wifi-phy.cc:829
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1587
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition wifi-phy.cc:1069
Ptr< PhyEntity > GetPhyEntity(WifiModulationClass modulation) const
Get the supported PHY entity corresponding to the modulation class.
Definition wifi-phy.cc:760
std::optional< std::reference_wrapper< const WifiTxVector > > GetInfoIfRxingPhyHeader() const
Definition wifi-phy.cc:2014
bool IsRunning() const
Return true if the timer is running.
const std::set< Mac48Address > & GetStasExpectedToRespond() const
Time GetDelayLeft() const
Get the remaining time until the timer will expire.
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
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...
@ SWITCHING
The PHY layer is switching to other channel.
@ TX
The PHY layer is sending a packet.
@ SLEEP
The PHY layer is sleeping.
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:33
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:72
typedef for union of different ActionValues