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 << psdu << txVector);
325
326 // EHT-SIG, the equivalent of HE-SIG-B, is present in EHT SU transmissions, too
327 if (txVector.GetPreambleType() == WIFI_PREAMBLE_EHT_MU)
328 {
330 auto sigBMode = phy->GetSigBMode(txVector);
331 txVector.SetSigBMode(sigBMode);
332 }
333
334 auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
335
336 if (m_apMac && psdu->GetHeader(0).IsTrigger())
337 {
338 for (const auto& client : m_sentRtsTo)
339 {
340 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(client))
341 {
342 continue;
343 }
344 auto clientMld = GetWifiRemoteStationManager()->GetMldAddress(client);
345 NS_ASSERT(clientMld);
346
347 // block transmissions on the other EMLSR links of the EMLSR clients
348 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
349 {
350 if (linkId != m_linkId &&
351 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*clientMld))
352 {
354 *clientMld,
355 {linkId});
356 }
357 }
358 }
359 }
360
362 UpdateTxopEndOnTxStart(txDuration, psdu->GetDuration());
363
364 if (m_apMac && m_apMac->GetApEmlsrManager())
365 {
366 auto delay = m_apMac->GetApEmlsrManager()->GetDelayOnTxPsduNotForEmlsr(psdu,
367 txVector,
368 m_phy->GetPhyBand());
369
370 // check if the EMLSR clients shall switch back to listening operation
371 for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
372 {
373 auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt);
374
375 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) &&
376 GetEmlsrSwitchToListening(psdu, aid, *clientIt))
377 {
378 EmlsrSwitchToListening(*clientIt, delay);
379 // this client is no longer involved in the current TXOP
380 clientIt = m_protectedStas.erase(clientIt);
381 }
382 else
383 {
384 clientIt++;
385 }
386 }
387 }
388 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
389 m_staMac->GetEmlsrManager()->GetInDeviceInterference())
390 {
391 for (const auto linkId : m_staMac->GetLinkIds())
392 {
393 if (auto phy = m_mac->GetWifiPhy(linkId);
394 phy && linkId != m_linkId && m_staMac->IsEmlsrLink(linkId))
395 {
396 auto txPowerDbm = phy->GetPower(txVector.GetTxPowerLevel()) + phy->GetTxGain();
397 // generate in-device interference on the other EMLSR link for the duration of this
398 // transmission
399 GenerateInDeviceInterference(linkId, txDuration, DbmToW(txPowerDbm));
400 }
401 }
402 }
403}
404
405void
407{
408 NS_LOG_FUNCTION(this << psduMap << txVector);
409
410 auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand());
411
413 UpdateTxopEndOnTxStart(txDuration, psduMap.begin()->second->GetDuration());
414
415 if (m_apMac)
416 {
417 // check if this is a BSRP TF used as ICF for some EMLSR client
418 if (IsTrigger(psduMap))
419 {
420 CtrlTriggerHeader trigger;
421 psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
422
423 if (trigger.IsBsrp())
424 {
425 auto recipients = GetTfRecipients(trigger);
426 for (const auto& client : recipients)
427 {
428 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(client))
429 {
430 continue;
431 }
432 auto clientMld = GetWifiRemoteStationManager()->GetMldAddress(client);
433 NS_ASSERT(clientMld);
434
435 // block transmissions on the other EMLSR links of the EMLSR clients
436 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
437 {
438 if (linkId != m_linkId &&
439 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*clientMld))
440 {
441 m_mac->BlockUnicastTxOnLinks(
443 *clientMld,
444 {linkId});
445 }
446 }
447 }
448 }
449 }
450
451 // check if the EMLSR clients shall switch back to listening operation at the end of this
452 // PPDU
453 for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
454 {
455 auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt);
456 const auto psduMapIt = psduMap.find(aid);
457 const auto aidNotFoundAndNotTf = (psduMapIt == psduMap.cend()) && !IsTrigger(psduMap);
458 // the PSDU to process: the one addressed to the given AID (if any) or the unique one
459 const auto psdu = (psduMapIt != psduMap.cend() ? psduMapIt : psduMap.cbegin())->second;
460
461 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) &&
462 (aidNotFoundAndNotTf || GetEmlsrSwitchToListening(psdu, aid, *clientIt)))
463 {
464 EmlsrSwitchToListening(*clientIt, txDuration);
465 // this client is no longer involved in the current TXOP
466 clientIt = m_protectedStas.erase(clientIt);
467 }
468 else
469 {
470 clientIt++;
471 }
472 }
473 }
474 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
475 m_staMac->GetEmlsrManager()->GetInDeviceInterference())
476 {
477 for (const auto linkId : m_staMac->GetLinkIds())
478 {
479 if (auto phy = m_mac->GetWifiPhy(linkId);
480 phy && linkId != m_linkId && m_staMac->IsEmlsrLink(linkId))
481 {
482 auto txPowerDbm = phy->GetPower(txVector.GetTxPowerLevel()) + phy->GetTxGain();
483 // generate in-device interference on the other EMLSR link for the duration of this
484 // transmission
485 GenerateInDeviceInterference(linkId, txDuration, DbmToW(txPowerDbm));
486 }
487 }
488 }
489}
490
491void
493{
494 NS_LOG_FUNCTION(this << linkId << duration.As(Time::US) << txPower);
495
496 auto rxPhy = DynamicCast<SpectrumWifiPhy>(m_mac->GetWifiPhy(linkId));
497
498 if (!rxPhy)
499 {
500 NS_LOG_DEBUG("No spectrum PHY operating on link " << +linkId);
501 return;
502 }
503
505 NS_ASSERT(txPhy);
506
507 auto psd = Create<SpectrumValue>(rxPhy->GetCurrentInterface()->GetRxSpectrumModel());
508 *psd = txPower;
509
510 auto spectrumSignalParams = Create<SpectrumSignalParameters>();
511 spectrumSignalParams->duration = duration;
512 spectrumSignalParams->txPhy = txPhy->GetCurrentInterface();
513 spectrumSignalParams->txAntenna = txPhy->GetAntenna();
514 spectrumSignalParams->psd = psd;
515
516 rxPhy->StartRx(spectrumSignalParams, rxPhy->GetCurrentInterface());
517}
518
519void
521{
522 NS_LOG_FUNCTION(this);
524 {
525 // the CTS may have been missed because another EMLSR link is being used; do not reset NAV
526 return;
527 }
529}
530
531void
533{
534 NS_LOG_FUNCTION(this);
536 {
537 // the CTS may have been missed because another EMLSR link is being used; do not reset NAV
538 return;
539 }
541}
542
543bool
545{
546 NS_LOG_FUNCTION(this << address);
547
548 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
549 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
550 NS_ASSERT_MSG(m_apMac, "This function shall only be called by AP MLDs");
551 std::set<uint8_t> linkIds{m_linkId};
552
553 /**
554 * Do nothing if the EMLSR client is involved in a DL or UL TXOP on another EMLSR link. This
555 * may happen, e.g., when the AP MLD sent an MU-RTS to multiple stations on this link, some of
556 * which responded, but this EMLSR client did not, e.g., because it concurrently started an UL
557 * TXOP on another link. The AP MLD then started a (long) DL MU transmission on this link,
558 * during which the EMLSR client completed the UL TXOP and started being involved in another
559 * DL or UL TXOP (note that DL TXOP is possible because the AP MLD considered the EMLSR client
560 * unprotected as soon as it detected the start of the previous UL TXOP). A Block Ack timeout
561 * for the EMLSR client occurs at the end of the DL MU transmission (which brings us here) and
562 * it may occur while the EMLSR client is still involved in a DL or UL TXOP.
563 *
564 * ┌─────────────┐ ┌───────────────┐
565 * │ MU-RTS to │ │ Data to │ BA timeout for us
566 * │us and others│ │ us and others │ |
567 * ────────┴─────────────┴┬────────┬┴───────────────┴┬───────┬──────────────
568 * [this link] │CTS from│ │BA from│
569 * │ others │ │ others│
570 * └────────┘ └───────┘
571 * ┌───────┐
572 * ┌───┐ ┌──┐ │ MU-RTS│ ┌──────┐
573 * [other link] │CTS│ │BA│ │ to us │ │ Data │
574 * ─────────┬────────┬┴───┴┬────┬┴──┴──────┴───────┴┬───┬┴──────┴┬──┬───────
575 * │ RTS │ │Data│ │CTS│ │BA│
576 * │from us │ └────┘ └───┘ └──┘
577 * └────────┘
578 */
579
580 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
581 {
582 if (!m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
583 {
584 continue; // not an EMLSR link
585 }
586
587 auto ehtFem = StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
588
589 if (ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
590 m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(*ehtFem->m_txopHolder) ==
591 mldAddress)
592 {
593 NS_LOG_DEBUG("EMLSR client " << *mldAddress << " is the holder of an UL TXOP on link "
594 << +linkId << ", do not unblock links");
595 return false;
596 }
597
598 if (linkId == m_linkId)
599 {
600 // no need to check if the EMLSR client is involved in a DL TXOP on this link
601 continue;
602 }
603
604 linkIds.insert(linkId);
605
606 if (auto linkAddr =
607 m_apMac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(*mldAddress);
608 linkAddr &&
609 (ehtFem->m_sentRtsTo.contains(*linkAddr) || ehtFem->m_sentFrameTo.contains(*linkAddr) ||
610 ehtFem->m_protectedStas.contains(*linkAddr)))
611 {
612 NS_LOG_DEBUG("EMLSR client " << address
613 << " has been sent an ICF, do not unblock links");
614 return false;
615 }
616 }
617
618 // unblock DL transmissions with reason USING_OTHER_EMLSR_LINK
620 *mldAddress,
621 linkIds);
622 return true;
623}
624
625void
627{
628 NS_LOG_FUNCTION(this << address << delay.As(Time::US));
629
630 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
631 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << address);
632 NS_ASSERT_MSG(m_apMac, "This function shall only be called by AP MLDs");
633
634 auto blockLinks = [=, this]() {
635 if (!UnblockEmlsrLinksIfAllowed(address))
636 {
637 NS_LOG_DEBUG("Could not unblock transmissions to " << address);
638 return;
639 }
640
641 // this EMLSR client switches back to listening operation
642 std::set<uint8_t> linkIds;
643 for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
644 {
645 if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
646 {
647 linkIds.insert(linkId);
648 }
649 }
650
651 // block DL transmissions on this link until transition delay elapses
653 *mldAddress,
654 linkIds);
655
656 auto unblockLinks = [=, this]() {
658 *mldAddress,
659 linkIds);
660 };
661
662 // unblock all EMLSR links when the transition delay elapses
663 auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
664 NS_ASSERT(emlCapabilities);
666 emlCapabilities->get().emlsrTransitionDelay);
667
668 endDelay.IsZero() ? unblockLinks()
669 : static_cast<void>(m_transDelayTimer[*mldAddress] =
670 Simulator::Schedule(endDelay, unblockLinks));
671 };
672
673 delay.IsZero() ? blockLinks() : static_cast<void>(Simulator::Schedule(delay, blockLinks));
674}
675
676void
678{
679 NS_LOG_FUNCTION(this << phy << linkId << delay.As(Time::US));
680
681 // TODO Shall we assert that there is no ongoing frame exchange sequence? Or is it possible
682 // that there is an ongoing frame exchange sequence (in such a case, we need to force a
683 // timeout, just like it is done in case of a normal channel switch
684
685 NS_ABORT_MSG_IF(!m_staMac, "This method can only be called on a STA");
686
687 // if we receive the notification from a PHY that is not connected to us, it means that
688 // we have been already connected to another PHY operating on this link, hence we do not
689 // have to reset the connected PHY. Similarly, we do not have to reset the connected PHY if
690 // the link does not change (this occurs when changing the channel width of aux PHYs upon
691 // enabling the EMLSR mode).
692 if (phy == m_phy && linkId != m_linkId)
693 {
694 ResetPhy();
695 }
696 m_staMac->NotifySwitchingEmlsrLink(phy, linkId, delay);
697}
698
699void
701{
702 NS_LOG_FUNCTION(this << dest << frame);
703
704 WifiMacHeader hdr;
706 hdr.SetAddr1(dest);
707 hdr.SetAddr2(m_self);
708 hdr.SetAddr3(m_bssid);
709 hdr.SetDsNotTo();
710 hdr.SetDsNotFrom();
711
712 // get the sequence number for the TWT Setup management frame
713 const auto sequence = m_txMiddle->GetNextSequenceNumberFor(&hdr);
714 hdr.SetSequenceNumber(sequence);
715
716 WifiActionHeader actionHdr;
720
721 auto packet = Create<Packet>();
722 packet->AddHeader(frame);
723 packet->AddHeader(actionHdr);
724
725 // Use AC_VO to send management frame addressed to a QoS STA (Sec. 10.2.3.2 of 802.11-2020)
726 m_mac->GetQosTxop(AC_VO)->Queue(Create<WifiMpdu>(packet, hdr));
727}
728
729std::optional<dBm_u>
731{
732 auto optRssi = HeFrameExchangeManager::GetMostRecentRssi(address);
733
734 if (optRssi)
735 {
736 return optRssi;
737 }
738
739 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
740
741 if (!mldAddress)
742 {
743 // not an MLD, nothing else can be done
744 return std::nullopt;
745 }
746
747 for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
748 {
749 std::optional<Mac48Address> linkAddress;
750 if (linkId != m_linkId &&
751 (linkAddress = m_mac->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(
752 *mldAddress)) &&
753 (optRssi = m_mac->GetWifiRemoteStationManager(linkId)->GetMostRecentRssi(*linkAddress)))
754 {
755 return optRssi;
756 }
757 }
758
759 return std::nullopt;
760}
761
762void
764 WifiTxVector& txVector) const
765{
766 NS_LOG_FUNCTION(this << trigger << txVector);
767
768 if (!trigger.IsMuRts() && !trigger.IsBsrp())
769 {
770 NS_LOG_INFO("Not an ICF");
771 return;
772 }
773
774 const auto recipients = GetTfRecipients(trigger);
775 uint8_t maxPaddingDelay = 0;
776 bool isUnprotectedEmlsrDst = false;
777
778 for (const auto& address : recipients)
779 {
780 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address) ||
781 m_protectedStas.contains(address))
782 {
783 continue; // not an EMLSR client or EMLSR client already protected
784 }
785
786 isUnprotectedEmlsrDst = true;
787 auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
788 NS_ASSERT(emlCapabilities);
789 maxPaddingDelay = std::max(maxPaddingDelay, emlCapabilities->get().emlsrPaddingDelay);
790 }
791
792 if (isUnprotectedEmlsrDst)
793 {
794 // The initial Control frame of frame exchanges shall be sent in the non-HT PPDU or
795 // non-HT duplicate PPDU format using a rate of 6 Mb/s, 12 Mb/s, or 24 Mb/s.
796 // (Sec. 35.3.17 of 802.11be D3.0)
797 GetWifiRemoteStationManager()->AdjustTxVectorForIcf(txVector);
798 }
799
800 // add padding (if needed)
801 if (maxPaddingDelay > 0)
802 {
803 // see formula (35-1) in Sec. 35.5.2.2.3 of 802.11be D3.0
804 auto rate = txVector.GetMode().GetDataRate(txVector);
805 std::size_t nDbps = rate / 1e6 * 4; // see Table 17-4 of 802.11-2020
806 trigger.SetPaddingSize((1 << (maxPaddingDelay + 2)) * nDbps / 8);
807 }
808}
809
810void
812{
813 NS_LOG_FUNCTION(this << sender);
814
815 // an EMLSR client responding to a BSRP TF must be considered protected
816 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(sender))
817 {
818 m_protectedStas.insert(sender);
819 }
820
822}
823
824bool
826{
828 if (m_staMac->IsEmlsrLink(m_linkId))
829 {
830 auto mainPhy = m_staMac->GetDevice()->GetPhy(m_staMac->GetEmlsrManager()->GetMainPhyId());
831
832 // while an ICF is being received on this link, an aux PHY that is not TX capable may get
833 // a TXOP on another link, release the channel and request the main PHY to switch channel.
834 // It may be decided to have the main PHY start a TXOP on the other link a PIFS after the
835 // channel switch (e.g., MAC header information is not used and AllowUlTxopInRx is true).
836 // Thus, when the ICF is received on this link, it is not dropped but, when the CTS must
837 // be transmitted, the main PHY has already started transmitting on the other link. In
838 // such a case, do not respond to the ICF.
839 if (mainPhy->IsStateSwitching() || m_mac->GetLinkForPhy(mainPhy) != m_linkId)
840 {
841 NS_LOG_DEBUG("Main PHY is switching or operating on another link, abort ICF response");
842 return true;
843 }
844 }
845 return false;
846}
847
848void
850 const CtrlTriggerHeader& trigger,
851 double muRtsSnr)
852{
853 NS_LOG_FUNCTION(this << muRtsHdr << trigger << muRtsSnr);
854
856 {
857 return;
858 }
859 HeFrameExchangeManager::SendCtsAfterMuRts(muRtsHdr, trigger, muRtsSnr);
860}
861
862void
864 const WifiMacHeader& hdr)
865{
866 NS_LOG_FUNCTION(this << trigger << hdr);
867
868 if (trigger.IsBsrp() && EmlsrClientCannotRespondToIcf())
869 {
870 return;
871 }
873}
874
875void
877{
878 NS_LOG_FUNCTION(this);
879
880 for (const auto& address : clients)
881 {
882 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
883 {
884 // EMLSR client switched to listening operations if it was protected, otherwise
885 // simply unblock transmissions
886 m_protectedStas.contains(address) ? EmlsrSwitchToListening(address, Seconds(0))
887 : (void)(UnblockEmlsrLinksIfAllowed(address));
888 m_protectedStas.erase(address);
889 }
890 }
891}
892
893void
895{
896 NS_LOG_FUNCTION(this << *muRts << txVector);
897
898 const auto crossLinkCollision = IsCrossLinkCollision(m_sentRtsTo);
899
901
902 const auto apEmlsrManager = m_apMac->GetApEmlsrManager();
903 const auto updateFailedCw =
904 crossLinkCollision && apEmlsrManager ? apEmlsrManager->UpdateCwAfterFailedIcf() : true;
905 DoCtsAfterMuRtsTimeout(muRts, txVector, updateFailedCw);
906}
907
908void
909EhtFrameExchangeManager::TbPpduTimeout(WifiPsduMap* psduMap, std::size_t nSolicitedStations)
910{
911 NS_LOG_FUNCTION(this << psduMap << nSolicitedStations);
912
913 const auto& staMissedTbPpduFrom = m_txTimer.GetStasExpectedToRespond();
914 const auto crossLinkCollision = IsCrossLinkCollision(staMissedTbPpduFrom);
915
916 if (staMissedTbPpduFrom.size() != nSolicitedStations)
917 {
918 // some STAs replied, hence the transmission succeeded. EMLSR clients that did not
919 // respond are switching back to listening operations
920 SwitchToListeningOrUnblockLinks(staMissedTbPpduFrom);
921 }
922
923 const auto apEmlsrManager = m_apMac->GetApEmlsrManager();
924 const auto updateFailedCw =
925 crossLinkCollision && apEmlsrManager ? apEmlsrManager->UpdateCwAfterFailedIcf() : true;
926 DoTbPpduTimeout(psduMap, nSolicitedStations, updateFailedCw);
927}
928
929void
931 std::size_t nSolicitedStations)
932{
933 NS_LOG_FUNCTION(this << psduMap << nSolicitedStations);
934
935 const auto& staMissedTbPpduFrom = m_txTimer.GetStasExpectedToRespond();
936
937 if (staMissedTbPpduFrom.size() != nSolicitedStations)
938 {
939 // some STAs replied, hence the transmission succeeded. EMLSR clients that did not
940 // respond are switching back to listening operations
941 SwitchToListeningOrUnblockLinks(staMissedTbPpduFrom);
942 }
943
944 HeFrameExchangeManager::BlockAcksInTbPpduTimeout(psduMap, nSolicitedStations);
945}
946
947bool
948EhtFrameExchangeManager::IsCrossLinkCollision(const std::set<Mac48Address>& staMissedResponseFrom)
949{
950 NS_LOG_FUNCTION(this << staMissedResponseFrom.size());
951
952 // check if all the clients that did not respond to the ICF are EMLSR clients that have sent
953 // (or are sending) a frame to the AP
954 auto crossLinkCollision = true;
955
956 // we blocked transmissions on the other EMLSR links for the EMLSR clients we sent the ICF to.
957 // For clients that did not respond, we can unblock transmissions if there is no ongoing
958 // UL TXOP held by that client
959 for (const auto& address : staMissedResponseFrom)
960 {
961 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
962 {
963 crossLinkCollision = false;
964 continue;
965 }
966
967 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
968 NS_ASSERT(mldAddress);
969
970 std::set<uint8_t> linkIds; // all EMLSR links of EMLSR client
971 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
972 {
973 if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
974 {
975 linkIds.insert(linkId);
976 }
977 }
978
979 if (std::any_of(linkIds.cbegin(),
980 linkIds.cend(),
981 /* lambda returning true if an UL TXOP is ongoing on the given link ID */
982 [=, this](uint8_t id) {
983 auto ehtFem = StaticCast<EhtFrameExchangeManager>(
984 m_mac->GetFrameExchangeManager(id));
985 return ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
986 m_mac->GetMldAddress(ehtFem->m_txopHolder.value()) == mldAddress;
987 }))
988 {
989 // an UL TXOP is ongoing on one EMLSR link, do not unblock links
990 continue;
991 }
992
993 // no UL TXOP is ongoing on any EMLSR link; if the EMLSR client is not transmitting a
994 // frame to the AP on any EMLSR link, then the lack of response to the MU-RTS was not
995 // caused by a simultaneous UL transmission
996 if (std::none_of(linkIds.cbegin(),
997 linkIds.cend(),
998 /* lambda returning true if an MPDU from the EMLSR client is being received
999 on the given link ID */
1000 [=, this](uint8_t id) {
1001 auto macHdr = m_mac->GetFrameExchangeManager(id)->GetReceivedMacHdr();
1002 return macHdr.has_value() &&
1003 m_mac->GetMldAddress(macHdr->get().GetAddr2()) == mldAddress;
1004 }))
1005 {
1006 crossLinkCollision = false;
1007 }
1008 }
1009
1010 return crossLinkCollision;
1011}
1012
1013void
1015 WifiMode rtsTxMode,
1016 double rtsSnr)
1017{
1018 NS_LOG_FUNCTION(this << rtsHdr << rtsTxMode << rtsSnr);
1019
1020 if (m_apMac && GetWifiRemoteStationManager()->GetEmlsrEnabled(rtsHdr.GetAddr2()))
1021 {
1022 // we are going to send a CTS to an EMLSR client, transmissions to such EMLSR client
1023 // must be blocked on the other EMLSR links
1024
1025 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(rtsHdr.GetAddr2());
1026 NS_ASSERT_MSG(mldAddress, "MLD address not found for " << rtsHdr.GetAddr2());
1027
1028 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
1029 {
1030 if (linkId != m_linkId &&
1031 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
1032 {
1033 // check that other links are blocked as expected
1034 WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *mldAddress, 0);
1035 auto mask =
1036 m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
1037 NS_ASSERT_MSG(mask, "No mask for client " << *mldAddress << " on link " << +linkId);
1038 if (!mask->test(
1039 static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK)))
1040 {
1041 NS_ASSERT_MSG(false,
1042 "Transmissions to " << *mldAddress << " on link " << +linkId
1043 << " are not blocked");
1044 // in case asserts are disabled, block transmissions on the other links because
1045 // this is what we need
1047 *mldAddress,
1048 {linkId});
1049 }
1050 }
1051 }
1052 }
1053
1054 HeFrameExchangeManager::SendCtsAfterRts(rtsHdr, rtsTxMode, rtsSnr);
1055}
1056
1057bool
1059 uint16_t aid,
1060 const Mac48Address& address) const
1061{
1062 NS_LOG_FUNCTION(this << psdu << aid << address);
1063
1064 // Sec. 35.3.17 of 802.11be D3.0:
1065 // The non-AP MLD shall be switched back to the listening operation on the EMLSR links after
1066 // the EMLSR transition delay time if [...] the non-AP STA affiliated with the non-AP MLD
1067 // does not detect [...] any of the following frames:
1068 // - an individually addressed frame with the RA equal to the MAC address of the non-AP STA
1069 // affiliated with the non-AP MLD
1070 if (psdu->GetAddr1() == address)
1071 {
1072 return false;
1073 }
1074
1075 // - a Trigger frame that has one of the User Info fields addressed to the non-AP STA
1076 // affiliated with the non-AP MLD
1077 for (const auto& mpdu : *PeekPointer(psdu))
1078 {
1079 if (mpdu->GetHeader().IsTrigger())
1080 {
1081 CtrlTriggerHeader trigger;
1082 mpdu->GetPacket()->PeekHeader(trigger);
1083 if (trigger.FindUserInfoWithAid(aid) != trigger.end())
1084 {
1085 return false;
1086 }
1087 }
1088 }
1089
1090 // - a CTS-to-self frame with the RA equal to the MAC address of the AP affiliated with
1091 // the AP MLD
1092 if (psdu->GetHeader(0).IsCts())
1093 {
1094 if (m_apMac && psdu->GetAddr1() == m_self)
1095 {
1096 return false;
1097 }
1098 if (m_staMac && psdu->GetAddr1() == m_bssid)
1099 {
1100 return false;
1101 }
1102 }
1103
1104 // - a Multi-STA BlockAck frame that has one of the Per AID TID Info fields addressed to
1105 // the non-AP STA affiliated with the non-AP MLD
1106 if (psdu->GetHeader(0).IsBlockAck())
1107 {
1108 CtrlBAckResponseHeader blockAck;
1109 psdu->GetPayload(0)->PeekHeader(blockAck);
1110 if (blockAck.IsMultiSta() && !blockAck.FindPerAidTidInfoWithAid(aid).empty())
1111 {
1112 return false;
1113 }
1114 }
1115
1116 // - a NDP Announcement frame that has one of the STA Info fields addressed to the non-AP
1117 // STA affiliated with the non-AP MLD and a sounding NDP
1118 // TODO NDP Announcement frame not supported yet
1119
1120 return true;
1121}
1122
1123void
1125{
1126 NS_LOG_FUNCTION(this);
1127
1128 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1129 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
1130 {
1131 NS_LOG_DEBUG("Reset the counter of TXOP attempts allowed while "
1132 "MediumSyncDelay is running");
1133 m_staMac->GetEmlsrManager()->ResetMediumSyncDelayNTxops(m_linkId);
1134 }
1135
1137}
1138
1139void
1141{
1142 NS_LOG_FUNCTION(this);
1143
1144 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1145 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
1146 {
1147 NS_LOG_DEBUG("Decrement the remaining number of TXOP attempts allowed while "
1148 "MediumSyncDelay is running");
1149 m_staMac->GetEmlsrManager()->DecrementMediumSyncDelayNTxops(m_linkId);
1150 }
1151
1153}
1154
1155void
1157{
1158 NS_LOG_FUNCTION(this << txop);
1159
1160 if (m_apMac)
1161 {
1162 // the channel has been released; all EMLSR clients are switching back to
1163 // listening operation
1164 for (const auto& address : m_protectedStas)
1165 {
1166 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
1167 {
1168 EmlsrSwitchToListening(address, Seconds(0));
1169 }
1170 }
1171 }
1172 else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
1173 {
1174 // Notify the UL TXOP end to the EMLSR Manager
1175 auto edca = DynamicCast<QosTxop>(txop);
1176 NS_ASSERT(edca);
1177 auto txopStart = edca->GetTxopStartTime(m_linkId);
1178
1179 NS_ASSERT(m_staMac->GetEmlsrManager());
1180 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId,
1181 (!txopStart || *txopStart == Simulator::Now()),
1183 }
1184
1186}
1187
1188void
1190{
1191 NS_LOG_FUNCTION(this << psdu << txVector);
1192
1193 // In addition, the timer resets to zero when any of the following events occur:
1194 // — The STA receives an MPDU
1195 // (Sec. 35.3.16.8.1 of 802.11be D3.1)
1196 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1197 m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
1198 {
1199 m_staMac->GetEmlsrManager()->CancelMediumSyncDelayTimer(m_linkId);
1200 }
1201
1202 if (m_apMac)
1203 {
1204 // we iterate over protected STAs to consider only the case when the AP is the TXOP holder.
1205 // The AP received a PSDU from a non-AP STA; given that the AP is the TXOP holder, this
1206 // PSDU has been likely solicited by the AP. In most of the cases, we identify which EMLSR
1207 // clients are no longer involved in the TXOP when the AP transmits the frame soliciting
1208 // response(s) from client(s). This is not the case, for example, for the acknowledgment
1209 // in SU format of a DL MU PPDU, where all the EMLSR clients (but one) switch to listening
1210 // operation after the immediate response (if any) by one of the EMLSR clients.
1211 for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
1212 {
1213 // TB PPDUs are received by the AP at distinct times, so it is difficult to take a
1214 // decision based on one of them. However, clients transmitting TB PPDUs are identified
1215 // by the soliciting Trigger Frame, thus we have already identified (when sending the
1216 // Trigger Frame) which EMLSR clients have switched to listening operation.
1217 // If the PSDU is not carried in a TB PPDU, we can determine whether this EMLSR client
1218 // is switching to listening operation by checking whether the AP is expecting a
1219 // response from it.
1220 if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) && !txVector.IsUlMu() &&
1221 !m_txTimer.GetStasExpectedToRespond().contains(*clientIt))
1222 {
1223 EmlsrSwitchToListening(*clientIt, Seconds(0));
1224 // this client is no longer involved in the current TXOP
1225 clientIt = m_protectedStas.erase(clientIt);
1226 }
1227 else
1228 {
1229 clientIt++;
1230 }
1231 }
1232 }
1233
1235}
1236
1237void
1239{
1240 NS_LOG_FUNCTION(this << psdu << txVector);
1241
1243
1244 if (m_apMac && m_apMac->GetApEmlsrManager())
1245 {
1246 m_apMac->GetApEmlsrManager()->NotifyPsduRxOk(m_linkId, psdu);
1247 }
1248
1249 if (m_apMac && m_txopHolder == psdu->GetAddr2() &&
1250 GetWifiRemoteStationManager()->GetEmlsrEnabled(*m_txopHolder))
1251 {
1253 {
1254 // an EMLSR client has started an UL TXOP. Start the ongoingTxopEnd timer so that
1255 // the next call to UpdateTxopEndOnRxEnd does its job
1258 }
1259
1260 UpdateTxopEndOnRxEnd(psdu->GetDuration());
1261 }
1262
1264 {
1265 if (GetEmlsrSwitchToListening(psdu, m_staMac->GetAssociationId(), m_self))
1266 {
1267 // we are no longer involved in the TXOP and switching to listening mode
1269 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
1270 }
1271 else
1272 {
1273 UpdateTxopEndOnRxEnd(psdu->GetDuration());
1274 }
1275 }
1276}
1277
1278bool
1280 const WifiTxVector& txVector)
1281{
1282 NS_LOG_FUNCTION(this);
1283
1284 auto sender = hdr.GetAddr2();
1285
1287 {
1288 NS_LOG_DEBUG("A TXOP is already ongoing");
1289 return false;
1290 }
1291
1292 if (auto holder = FindTxopHolder(hdr, txVector); holder != sender)
1293 {
1294 NS_LOG_DEBUG("Sender (" << sender << ") differs from the TXOP holder ("
1295 << (holder ? Address(*holder) : Address()) << ")");
1296 return false;
1297 }
1298
1299 if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(sender))
1300 {
1301 NS_LOG_DEBUG("Sender (" << sender << ") is not an EMLSR client");
1302 return false;
1303 }
1304
1305 NS_LOG_DEBUG("EMLSR client " << sender << " is starting a TXOP");
1306
1307 // Block transmissions for this EMLSR client on other links
1308 auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(sender);
1309 NS_ASSERT(mldAddress);
1310
1311 for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); ++linkId)
1312 {
1313 if (linkId != m_linkId &&
1314 m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
1315 {
1317 *mldAddress,
1318 {linkId});
1319 }
1320 }
1321
1322 // Make sure that transmissions for this EMLSR client are not blocked on this link
1323 // (the AP MLD may have sent an ICF on another link right before receiving this MPDU,
1324 // thus transmissions on this link may have been blocked)
1326 *mldAddress,
1327 {m_linkId});
1328
1329 // Stop the transition delay timer for this EMLSR client, if any is running
1330 if (auto it = m_transDelayTimer.find(*mldAddress);
1331 it != m_transDelayTimer.end() && it->second.IsPending())
1332 {
1333 it->second.PeekEventImpl()->Invoke();
1334 it->second.Cancel();
1335 }
1336
1337 return true;
1338}
1339
1340EventId&
1345
1346void
1348{
1349 NS_LOG_FUNCTION(this << psdu);
1350
1351 if (m_apMac && m_apMac->GetApEmlsrManager())
1352 {
1353 m_apMac->GetApEmlsrManager()->NotifyPsduRxError(m_linkId, psdu);
1354 }
1355}
1356
1357void
1359 RxSignalInfo rxSignalInfo,
1360 const WifiTxVector& txVector,
1361 bool inAmpdu)
1362{
1363 // The received MPDU is either broadcast or addressed to this station
1364 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
1365
1366 const auto& hdr = mpdu->GetHeader();
1367
1368 if (m_apMac)
1369 {
1370 // if the AP MLD received an MPDU from an EMLSR client that is starting an UL TXOP,
1371 // block transmissions to the EMLSR client on other links
1372 CheckEmlsrClientStartingTxop(hdr, txVector);
1373 }
1374
1375 bool icfReceived = false;
1376
1377 if (hdr.IsTrigger())
1378 {
1379 if (!m_staMac)
1380 {
1381 return; // Trigger Frames are only processed by STAs
1382 }
1383
1384 CtrlTriggerHeader trigger;
1385 mpdu->GetPacket()->PeekHeader(trigger);
1386
1387 if (hdr.GetAddr1() != m_self &&
1388 (!hdr.GetAddr1().IsBroadcast() || !m_staMac->IsAssociated() ||
1389 hdr.GetAddr2() != m_bssid // not sent by the AP this STA is associated with
1390 || trigger.FindUserInfoWithAid(m_staMac->GetAssociationId()) == trigger.end()))
1391 {
1392 return; // not addressed to us
1393 }
1394
1395 if ((trigger.IsMuRts() || trigger.IsBsrp()) && !m_ongoingTxopEnd.IsPending() &&
1396 m_staMac->IsEmlsrLink(m_linkId))
1397 {
1398 // this is an initial Control frame
1399 if (DropReceivedIcf())
1400 {
1401 return;
1402 }
1403
1404 auto emlsrManager = m_staMac->GetEmlsrManager();
1405 NS_ASSERT(emlsrManager);
1406
1407 icfReceived = true;
1408
1409 // we just got involved in a DL TXOP. Check if we are still involved in the TXOP in a
1410 // SIFS (we are expected to reply by sending a CTS frame)
1412 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + m_phy->GetSifs()).As(Time::S));
1415 this,
1416 hdr.GetAddr2());
1417 }
1418 }
1419
1420 // We impose that an aux PHY is only able to receive an ICF, a CTS or a management frame
1421 // (we are interested in receiving mainly Beacon frames). Note that other frames are still
1422 // post-processed, e.g., used to set the NAV and the TXOP holder.
1423 // The motivation is that, e.g., an AP MLD may send an ICF to EMLSR clients A and B;
1424 // A responds while B does not; the AP MLD sends a DL MU PPDU to both clients followed
1425 // by an MU-BAR to solicit a BlockAck from both clients. If an aux PHY of client B is
1426 // operating on this link, the MU-BAR will be received and a TB PPDU response sent
1427 // through the aux PHY.
1428 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1429 m_mac->GetLinkForPhy(m_staMac->GetEmlsrManager()->GetMainPhyId()) != m_linkId &&
1430 !icfReceived && !mpdu->GetHeader().IsCts() && !mpdu->GetHeader().IsMgt())
1431 {
1432 NS_LOG_DEBUG("Dropping " << *mpdu << " received by an aux PHY on link " << +m_linkId);
1433 return;
1434 }
1435
1436 HeFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1437
1438 if (icfReceived)
1439 {
1440 m_staMac->GetEmlsrManager()->NotifyIcfReceived(m_linkId);
1441 }
1442}
1443
1444void
1446 const RxSignalInfo& rxSignalInfo,
1447 const WifiTxVector& txVector,
1448 const std::vector<bool>& perMpduStatus)
1449{
1451 this << *psdu << rxSignalInfo << txVector << perMpduStatus.size()
1452 << std::all_of(perMpduStatus.begin(), perMpduStatus.end(), [](bool v) { return v; }));
1453
1454 // In our model, we make the assumption that an aux PHY is not able to receive an A-MPDU
1455 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
1456 m_mac->GetLinkForPhy(m_staMac->GetEmlsrManager()->GetMainPhyId()) != m_linkId)
1457 {
1458 NS_LOG_DEBUG("Dropping " << *psdu << " received by an aux PHY on link " << +m_linkId);
1459 return;
1460 }
1461
1462 HeFrameExchangeManager::EndReceiveAmpdu(psdu, rxSignalInfo, txVector, perMpduStatus);
1463}
1464
1465bool
1467{
1468 NS_LOG_FUNCTION(this);
1469
1470 auto emlsrManager = m_staMac->GetEmlsrManager();
1471 NS_ASSERT(emlsrManager);
1472
1473 if (UsingOtherEmlsrLink())
1474 {
1475 // we received an ICF on a link that is blocked because another EMLSR link is
1476 // being used. Check if there is an ongoing DL TXOP on the other EMLSR link
1477 auto apMldAddress = GetWifiRemoteStationManager()->GetMldAddress(m_bssid);
1478 NS_ASSERT_MSG(apMldAddress, "MLD address not found for " << m_bssid);
1479
1480 if (auto it = std::find_if(
1481 m_staMac->GetLinkIds().cbegin(),
1482 m_staMac->GetLinkIds().cend(),
1483 /* lambda to find an EMLSR link on which there is an ongoing DL TXOP */
1484 [=, this](uint8_t linkId) {
1485 auto ehtFem =
1486 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(linkId));
1487 return linkId != m_linkId && m_staMac->IsEmlsrLink(linkId) &&
1488 ehtFem->m_ongoingTxopEnd.IsPending() && ehtFem->m_txopHolder &&
1489 m_mac->GetWifiRemoteStationManager(linkId)->GetMldAddress(
1490 *ehtFem->m_txopHolder) == apMldAddress;
1491 });
1492 it != m_staMac->GetLinkIds().cend())
1493 {
1494 // AP is not expected to send ICFs on two links. If an ICF
1495 // has been received on this link, it means that the DL TXOP
1496 // on the other link terminated (e.g., the AP did not
1497 // receive our response)
1498 StaticCast<EhtFrameExchangeManager>(m_mac->GetFrameExchangeManager(*it))
1499 ->m_ongoingTxopEnd.Cancel();
1500 // we are going to start a TXOP on this link; unblock
1501 // transmissions on this link, the other links will be
1502 // blocked subsequently
1504 }
1505 else
1506 {
1507 // We get here likely because transmission on the other EMLSR link
1508 // started before the reception of the ICF ended. We drop this ICF and let the
1509 // UL TXOP continue.
1510 NS_LOG_DEBUG("Drop ICF because another EMLSR link is being used");
1512 return true;
1513 }
1514 }
1515 /**
1516 * It might happen that, while the aux PHY is receiving an ICF, the main PHY is
1517 * completing a TXOP on another link or is returning to the primary link after a TXOP
1518 * is completed on another link. In order to respond to the ICF, it is necessary that
1519 * the main PHY has enough time to switch and be ready to operate on this link by the
1520 * end of the ICF padding.
1521 *
1522 * TXOP end
1523 * │
1524 * ┌───┐ another
1525 * AP MLD │ACK│ link
1526 * ───────────┬─────────┬┴───┴───────────────────────────────────────
1527 * EMLSR │ QoS │ │ main PHY
1528 * client │ Data │ │
1529 * └─────────┘ │
1530 * ┌─────┬───┐ this
1531 * AP MLD │ ICF │pad│ link
1532 * ────────────────────┴─────┴───┴───────────────────────────────────
1533 * aux PHY
1534 */
1535 else if (auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId());
1536 mainPhy != m_phy)
1537 {
1538 const auto delay = mainPhy->GetChannelSwitchDelay();
1539 auto lastTime = mainPhy->GetState()->GetLastTime({WifiPhyState::TX});
1540 auto reason = WifiIcfDrop::NOT_ENOUGH_TIME_TX;
1541
1542 if (auto lastSwitch = mainPhy->GetState()->GetLastTime({WifiPhyState::SWITCHING});
1543 lastSwitch > lastTime)
1544 {
1545 lastTime = lastSwitch;
1547 }
1548 if (auto lastSleep = mainPhy->GetState()->GetLastTime({WifiPhyState::SLEEP});
1549 lastSleep > lastTime)
1550 {
1551 lastTime = lastSleep;
1553 }
1554 // ignore RX state for now
1555
1556 if (lastTime > Simulator::Now() - delay)
1557 {
1559 "Drop ICF due to not enough time for the main PHY to switch link; reason = "
1560 << reason);
1561 m_icfDropCallback(reason, m_linkId);
1562 return true;
1563 }
1564 }
1565 return false;
1566}
1567
1568void
1569EhtFrameExchangeManager::TxopEnd(const std::optional<Mac48Address>& txopHolder)
1570{
1571 NS_LOG_FUNCTION(this << txopHolder.has_value());
1572
1574 {
1575 // we may get here because the PHY has not issued the PHY-RXSTART.indication before
1576 // the expiration of the timer started to detect new received frames, but the PHY is
1577 // currently decoding the PHY header of a PPDU, so let's wait some more time to check
1578 // if we receive a PHY-RXSTART.indication when the PHY is done decoding the PHY header
1579 NS_LOG_DEBUG("PHY is decoding the PHY header of PPDU, postpone TXOP end");
1582 this,
1583 txopHolder);
1584 return;
1585 }
1586
1587 if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
1588 {
1589 m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
1590 }
1591 else if (m_apMac && txopHolder && GetWifiRemoteStationManager()->GetEmlsrEnabled(*txopHolder))
1592 {
1593 // EMLSR client terminated its TXOP and is back to listening operation
1594 EmlsrSwitchToListening(*txopHolder, Seconds(0));
1595 }
1596}
1597
1598void
1600{
1601 NS_LOG_FUNCTION(this << txDuration.As(Time::MS) << durationId.As(Time::US));
1602
1604 {
1605 // nothing to do
1606 return;
1607 }
1608
1610 Time delay;
1611
1612 if (m_txTimer.IsRunning())
1613 {
1614 // the TX timer is running, hence we are expecting a response. Postpone the TXOP end
1615 // to match the TX timer (which is long enough to get the PHY-RXSTART.indication for
1616 // the response)
1617 delay = m_txTimer.GetDelayLeft();
1618 }
1619 else if (durationId <= m_phy->GetSifs())
1620 {
1621 // the TX timer is not running, hence no response is expected, and the Duration/ID value
1622 // is less than or equal to a SIFS; the TXOP will end after this transmission
1623 NS_LOG_DEBUG("Assume TXOP will end based on Duration/ID value");
1624 delay = txDuration;
1625 }
1626 else
1627 {
1628 // the TX Timer is not running, hence no response is expected (e.g., we are
1629 // transmitting a CTS after ICS). The TXOP holder may transmit a frame a SIFS
1630 // after the end of this PPDU, hence we need to postpone the TXOP end in order to
1631 // get the PHY-RXSTART.indication
1632 delay = txDuration + m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY;
1633 }
1634
1635 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
1638}
1639
1640void
1642{
1643 NS_LOG_FUNCTION(this << psduDuration.As(Time::MS));
1644
1645 if (!m_ongoingTxopEnd.IsPending() || !psduDuration.IsStrictlyPositive())
1646 {
1647 // nothing to do
1648 return;
1649 }
1650
1651 // postpone the TXOP end until after the reception of the PSDU is completed
1653
1654 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + psduDuration).As(Time::S));
1657 this,
1658 m_txopHolder);
1659}
1660
1661void
1663{
1664 NS_LOG_FUNCTION(this << durationId.As(Time::US));
1665
1667 {
1668 // nothing to do
1669 return;
1670 }
1671
1673
1674 // if the Duration/ID of the received frame is less than a SIFS, the TXOP
1675 // is terminated
1676 if (durationId <= m_phy->GetSifs())
1677 {
1678 NS_LOG_DEBUG("Assume TXOP ended based on Duration/ID value");
1680 return;
1681 }
1682
1683 // we may send a response after a SIFS or we may receive another frame after a SIFS.
1684 // Postpone the TXOP end by considering the latter (which takes longer)
1685 auto delay = m_phy->GetSifs() + m_phy->GetSlot() + EMLSR_RX_PHY_START_DELAY;
1686 NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
1689}
1690
1691} // 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...
bool IsMultiSta() const
Check if the BlockAck frame variant is Multi-STA Block Ack.
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 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 TransmissionFailed() override
Take necessary actions upon a transmission failure.
void TbPpduTimeout(WifiPsduMap *psduMap, std::size_t nSolicitedStations) override
Take the necessary actions after that some TB PPDUs are missing in response to Trigger Frame.
void IntraBssNavResetTimeout() override
Reset the intra-BSS NAV upon expiration of the intra-BSS NAV reset timer.
void 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)
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.
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
virtual void TransmissionFailed()
Take necessary actions upon a transmission failure.
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.
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...
Mac48Address m_bssid
BSSID address (Mac48Address)
virtual bool StartTransmission(Ptr< Txop > dcf, MHz_u allowedWidth)
Request the FrameExchangeManager to start a frame exchange sequence.
MHz_u m_allowedWidth
the allowed width for the current transmission
HeFrameExchangeManager handles the frame exchange sequences for HE stations.
Ptr< ApWifiMac > m_apMac
MAC pointer (null if not an AP)
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
void DoCtsAfterMuRtsTimeout(Ptr< WifiMpdu > muRts, const WifiTxVector &txVector, bool updateFailedCw)
Called when no CTS frame is received after an MU-RTS.
std::optional< Mac48Address > FindTxopHolder(const WifiMacHeader &hdr, const WifiTxVector &txVector) override
Determine the holder of the TXOP, if possible, based on the received frame.
void NavResetTimeout() override
Reset the NAV upon expiration of the NAV reset timer.
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.
Ptr< StaWifiMac > m_staMac
MAC pointer (null if not a STA)
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:560
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:594
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:404
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:682
static constexpr bool DIDNT_HAVE_FRAMES_TO_TRANSMIT
no packet available for transmission was in the queue
Definition txop.h:390
static constexpr bool CHECK_MEDIUM_BUSY
generation of backoff (also) depends on the busy/idle state of the medium
Definition txop.h:392
static constexpr bool DONT_CHECK_MEDIUM_BUSY
generation of backoff is independent of the busy/idle state of the medium
Definition txop.h:394
a unique identifier for an interface.
Definition type-id.h:48
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:1578
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition wifi-phy.cc:1069
bool IsReceivingPhyHeader() const
Definition wifi-phy.cc:1992
Ptr< PhyEntity > GetPhyEntity(WifiModulationClass modulation) const
Get the supported PHY entity corresponding to the modulation class.
Definition wifi-phy.cc:760
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:1332
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1344
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1308
@ 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::unordered_map< uint16_t, Ptr< const WifiPsdu > > WifiConstPsduMap
Map of const PSDUs indexed by STA-ID.
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
@ WIFI_MAC_MGT_ACTION
Watt_u DbmToW(dBm_u val)
Convert from dBm to Watts.
Definition wifi-utils.cc:31
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:587
const Time EMLSR_RX_PHY_START_DELAY
aRxPHYStartDelay value to use when waiting for a new frame in the context of EMLSR operations (Sec.
std::unordered_map< uint16_t, Ptr< WifiPsdu > > WifiPsduMap
Map of PSDUs indexed by STA-ID.
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