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