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