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