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