A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
emlsr-manager.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2023 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
9#include "emlsr-manager.h"
10
11#include "eht-configuration.h"
13
14#include "ns3/abort.h"
15#include "ns3/assert.h"
16#include "ns3/attribute-container.h"
17#include "ns3/log.h"
18#include "ns3/mgt-action-headers.h"
19#include "ns3/wifi-mpdu.h"
20#include "ns3/wifi-net-device.h"
21#include "ns3/wifi-phy-state-helper.h"
22
23#include <iterator>
24#include <sstream>
25
26namespace ns3
27{
28
29NS_LOG_COMPONENT_DEFINE("EmlsrManager");
30
31NS_OBJECT_ENSURE_REGISTERED(EmlsrManager);
32
33TypeId
35{
36 static TypeId tid =
37 TypeId("ns3::EmlsrManager")
39 .SetGroupName("Wifi")
40 .AddAttribute("EmlsrPaddingDelay",
41 "The EMLSR Paddind Delay (not used by AP MLDs). "
42 "Possible values are 0 us, 32 us, 64 us, 128 us or 256 us.",
46 .AddAttribute("EmlsrTransitionDelay",
47 "The EMLSR Transition Delay (not used by AP MLDs). "
48 "Possible values are 0 us, 16 us, 32 us, 64 us, 128 us or 256 us.",
52 .AddAttribute(
53 "MainPhyId",
54 "The ID of the main PHY (position in the vector of PHYs held by "
55 "WifiNetDevice). This attribute cannot be set after construction.",
56 TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, // prevent setting after construction
60 .AddAttribute("AuxPhyChannelWidth",
61 "The maximum channel width (MHz) supported by Aux PHYs. Note that the "
62 "maximum channel width is capped to the maximum channel width supported "
63 "by the configured maximum modulation class supported.",
65 TypeId::ATTR_CONSTRUCT, // prevent setting after construction
66 UintegerValue(20),
69 .AddAttribute("AuxPhyMaxModClass",
70 "The maximum modulation class supported by Aux PHYs. Use "
71 "WIFI_MOD_CLASS_OFDM for non-HT.",
73 TypeId::ATTR_CONSTRUCT, // prevent setting after construction
77 "HR-DSSS",
79 "ERP-OFDM",
81 "OFDM",
83 "HT",
85 "VHT",
87 "HE",
89 "EHT"))
90 .AddAttribute("AuxPhyTxCapable",
91 "Whether Aux PHYs are capable of transmitting PPDUs.",
92 BooleanValue(true),
96 .AddAttribute("InDeviceInterference",
97 "Whether in-device interference is such that a PHY cannot decode "
98 "anything and cannot decrease the backoff counter when another PHY "
99 "of the same device is transmitting.",
100 BooleanValue(false),
104 .AddAttribute("PutAuxPhyToSleep",
105 "Whether Aux PHYs should be put into sleep mode while the Main PHY "
106 "is carrying out a (DL or UL) TXOP. Specifically, for DL TXOPs, aux "
107 "PHYs are put to sleep after receiving the ICF; for UL TXOPs, aux PHYs "
108 "are put to sleep when the CTS frame is received, if RTS/CTS is used, "
109 "or when the transmission of the data frame starts, otherwise. "
110 "Aux PHYs are resumed from sleep when the TXOP ends.",
111 BooleanValue(false),
114 .AddAttribute(
115 "EmlsrLinkSet",
116 "IDs of the links on which EMLSR mode will be enabled. An empty set "
117 "indicates to disable EMLSR.",
121 .AddAttribute("ResetCamState",
122 "Whether to reset the state of the ChannelAccessManager associated with "
123 "the link on which the main PHY has just switched to.",
124 BooleanValue(false),
128 return tid;
129}
130
132 // The STA initializes dot11MSDTimerDuration to aPPDUMaxTime defined in Table 36-70
133 // (Sec. 35.3.16.8.1 of 802.11be D3.1)
134 : m_mediumSyncDuration(MicroSeconds(DEFAULT_MSD_DURATION_USEC)),
135 // The default value of dot11MSDOFDMEDthreshold is –72 dBm and the default value of
136 // dot11MSDTXOPMax is 1, respectively (Sec. 35.3.16.8.1 of 802.11be D3.1)
137 m_msdOfdmEdThreshold(DEFAULT_MSD_OFDM_ED_THRESH),
138 m_msdMaxNTxops(DEFAULT_MSD_MAX_N_TXOPS)
139{
140 NS_LOG_FUNCTION(this);
141}
142
147
148void
150{
151 NS_LOG_FUNCTION(this);
152 m_staMac->TraceDisconnectWithoutContext("AckedMpdu", MakeCallback(&EmlsrManager::TxOk, this));
153 m_staMac->TraceDisconnectWithoutContext("DroppedMpdu",
155 m_staMac = nullptr;
157 for (auto& [id, status] : m_mediumSyncDelayStatus)
158 {
159 status.timer.Cancel();
160 }
162}
163
164void
166{
167 NS_LOG_FUNCTION(this << mac);
168 NS_ASSERT(mac);
169 m_staMac = mac;
170
171 NS_ABORT_MSG_IF(!m_staMac->GetEhtConfiguration(), "EmlsrManager requires EHT support");
172 NS_ABORT_MSG_IF(m_staMac->GetNLinks() <= 1, "EmlsrManager can only be installed on MLDs");
173 NS_ABORT_MSG_IF(m_staMac->GetTypeOfStation() != STA,
174 "EmlsrManager can only be installed on non-AP MLDs");
175
176 m_staMac->TraceConnectWithoutContext("AckedMpdu", MakeCallback(&EmlsrManager::TxOk, this));
177 m_staMac->TraceConnectWithoutContext("DroppedMpdu",
179 m_staMac->TraceConnectWithoutContext(
180 "EmlsrLinkSwitch",
182 DoSetWifiMac(mac);
183}
184
185void
190
191void
193{
194 NS_LOG_FUNCTION(this << linkId << phy);
195
196 if (!phy)
197 {
198 NS_ASSERT(!m_noPhySince.contains(linkId));
199 NS_LOG_DEBUG("Record that no PHY is operating on link " << +linkId);
200 m_noPhySince[linkId] = Simulator::Now();
201 return;
202 }
203
204 // phy switched to operate on the link with ID equal to linkId
205 auto it = m_noPhySince.find(linkId);
206
207 if (it == m_noPhySince.end())
208 {
209 // phy switched to a link on which another PHY was operating, do nothing
210 return;
211 }
212
213 auto duration = Simulator::Now() - it->second;
214 NS_ASSERT_MSG(duration.IsPositive(), "Interval duration should not be negative");
215
216 NS_LOG_DEBUG("PHY " << +phy->GetPhyId() << " switched to link " << +linkId << " after "
217 << duration.As(Time::US)
218 << " since last time a PHY was operating on this link");
220 {
222 }
223
224 m_noPhySince.erase(it);
225}
226
227void
229{
230 NS_LOG_FUNCTION(this << mainPhyId);
231 NS_ABORT_MSG_IF(IsInitialized(), "Cannot be called once this object has been initialized");
232 m_mainPhyId = mainPhyId;
233}
234
235uint8_t
237{
238 return m_mainPhyId;
239}
240
241void
243{
244 m_resetCamState = enable;
245}
246
247bool
252
253void
255{
256 m_auxPhyTxCapable = capable;
257}
258
259bool
264
265void
270
271bool
276
277const std::set<uint8_t>&
279{
280 return m_emlsrLinks;
281}
282
285{
286 return m_staMac;
287}
288
290EmlsrManager::GetEhtFem(uint8_t linkId) const
291{
292 return StaticCast<EhtFrameExchangeManager>(m_staMac->GetFrameExchangeManager(linkId));
293}
294
295std::optional<Time>
297{
298 if (const auto statusIt = m_mediumSyncDelayStatus.find(linkId);
299 statusIt != m_mediumSyncDelayStatus.cend() && statusIt->second.timer.IsPending())
300 {
301 return m_mediumSyncDuration - Simulator::GetDelayLeft(statusIt->second.timer);
302 }
303 return std::nullopt;
304}
305
306void
312
313std::optional<Time>
318
319void
321{
322 NS_LOG_FUNCTION(this << duration.As(Time::US));
323 m_mediumSyncDuration = duration;
324}
325
326Time
331
332void
334{
335 NS_LOG_FUNCTION(this << threshold);
336 m_msdOfdmEdThreshold = threshold;
337}
338
339int8_t
344
345void
346EmlsrManager::SetMediumSyncMaxNTxops(std::optional<uint8_t> nTxops)
347{
348 NS_LOG_FUNCTION(this << nTxops.has_value());
349 m_msdMaxNTxops = nTxops;
350}
351
352std::optional<uint8_t>
357
358void
359EmlsrManager::SetEmlsrLinks(const std::set<uint8_t>& linkIds)
360{
361 std::stringstream ss;
362 if (g_log.IsEnabled(ns3::LOG_FUNCTION))
363 {
364 std::copy(linkIds.cbegin(), linkIds.cend(), std::ostream_iterator<uint16_t>(ss, " "));
365 }
366 NS_LOG_FUNCTION(this << ss.str());
367 NS_ABORT_MSG_IF(linkIds.size() == 1, "Cannot enable EMLSR mode on a single link");
368
369 if (linkIds != m_emlsrLinks)
370 {
371 m_nextEmlsrLinks = linkIds;
372 }
373
374 if (GetStaMac() && GetStaMac()->IsAssociated() && GetTransitionTimeout() && m_nextEmlsrLinks)
375 {
376 // Request to enable EMLSR mode on the given links, provided that they have been setup
377 SendEmlOmn();
378 }
379}
380
381void
383{
384 NS_LOG_FUNCTION(this << *mpdu << linkId);
385
386 const auto& hdr = mpdu->GetHeader();
387
388 DoNotifyMgtFrameReceived(mpdu, linkId);
389
390 if (hdr.IsAssocResp() && GetStaMac()->IsAssociated() && GetTransitionTimeout())
391 {
392 // we just completed ML setup with an AP MLD that supports EMLSR
394
395 if (m_nextEmlsrLinks && !m_nextEmlsrLinks->empty())
396 {
397 // a non-empty set of EMLSR links have been configured, hence enable EMLSR mode
398 // on those links
399 SendEmlOmn();
400 }
401 }
402
403 if (hdr.IsAction() && hdr.GetAddr2() == m_staMac->GetBssid(linkId))
404 {
405 // this is an action frame sent by an AP of the AP MLD we are associated with
406 auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
407 if (category == WifiActionHeader::PROTECTED_EHT &&
408 action.protectedEhtAction ==
410 {
412 {
413 // no need to wait until the expiration of the transition timeout
416 }
417 }
418 }
419}
420
421void
423{
424 NS_LOG_FUNCTION(this << linkId);
425
426 NS_ASSERT(m_staMac->IsEmlsrLink(linkId));
427
428 // block transmissions and suspend medium access on all other EMLSR links
429 for (auto id : m_staMac->GetLinkIds())
430 {
431 if (id != linkId && m_staMac->IsEmlsrLink(id))
432 {
434 }
435 }
436
437 auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
438 auto auxPhy = m_staMac->GetWifiPhy(linkId);
439
440 if (m_staMac->GetWifiPhy(linkId) != mainPhy)
441 {
442 // an aux PHY received the ICF
443 SwitchMainPhy(linkId,
444 true, // channel switch should occur instantaneously
447
448 // aux PHY received the ICF but main PHY will send the response
449 auto uid = auxPhy->GetPreviouslyRxPpduUid();
450 mainPhy->SetPreviouslyRxPpduUid(uid);
451 }
452
453 // a DL TXOP started, set all aux PHYs to sleep
454 if (m_auxPhyToSleep)
455 {
457 }
458
459 DoNotifyIcfReceived(linkId);
460}
461
462std::pair<bool, Time>
464{
465 auto phy = m_staMac->GetWifiPhy(linkId);
466 NS_ASSERT_MSG(phy, "No PHY operating on link " << +linkId);
467
468 auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
469
470 // check possible reasons to give up the TXOP that apply to both main PHY and aux PHYs
471 if (const auto [startTxop, delay] = DoGetDelayUntilAccessRequest(linkId); !startTxop)
472 {
473 return {false, delay};
474 }
475
476 if (phy == mainPhy)
477 {
478 // no more constraints to check if medium was gained by main PHY
479 return {true, Time{0}};
480 }
481
482 // an aux PHY is operating on the given link; call the appropriate method depending on
483 // whether the aux PHY is TX capable or not
485 {
487 // if the aux PHY is not TX capable, we don't have to request channel access: if the main
488 // PHY switches link, the UL TXOP will be started; if the main PHY does not switch, it is
489 // because it is going to start an UL TXOP on another link and this link will be restarted
490 // at the end of that UL TXOP when this link will be unblocked
491 NS_LOG_DEBUG("Aux PHY is not capable of transmitting a PPDU");
492 return {false, Time{0}};
493 }
494
496}
497
498void
500{
501 NS_LOG_FUNCTION(this << linkId);
502
503 if (!m_staMac->IsEmlsrLink(linkId))
504 {
505 NS_LOG_DEBUG("EMLSR is not enabled on link " << +linkId);
506 return;
507 }
508
509 // block transmissions and suspend medium access on all other EMLSR links
510 for (auto id : m_staMac->GetLinkIds())
511 {
512 if (id != linkId && m_staMac->IsEmlsrLink(id))
513 {
515 }
516 }
517
518 DoNotifyUlTxopStart(linkId);
519}
520
521void
523{
524 NS_LOG_FUNCTION(this << *rts << txVector);
525}
526
527void
529{
530 NS_LOG_FUNCTION(this << linkId);
531
532 if (m_auxPhyToSleep && m_staMac->IsEmlsrLink(linkId))
533 {
534 if (auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId); mainPhy->IsStateSwitching())
535 {
536 // main PHY is switching to this link to take over the UL TXOP. Postpone aux PHY
537 // sleeping until after the main PHY has completed switching
538 Simulator::Schedule(mainPhy->GetDelayUntilIdle() + TimeStep(1),
540 this,
541 true);
542 }
543 else
544 {
545 // put aux PHYs to sleep
547 }
548 }
549}
550
551void
552EmlsrManager::NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted, bool ongoingDlTxop)
553{
554 NS_LOG_FUNCTION(this << linkId << ulTxopNotStarted << ongoingDlTxop);
555
556 if (!m_staMac->IsEmlsrLink(linkId))
557 {
558 NS_LOG_DEBUG("EMLSR is not enabled on link " << +linkId);
559 return;
560 }
561
562 // If the main PHY has been scheduled to switch to this link, cancel the channel switch.
563 // This happens, e.g., when an aux PHY sent an RTS to start an UL TXOP but it did not
564 // receive a CTS response.
565 if (auto it = m_ulMainPhySwitch.find(linkId); it != m_ulMainPhySwitch.end())
566 {
567 if (it->second.IsPending())
568 {
569 NS_LOG_DEBUG("Cancelling main PHY channel switch event on link " << +linkId);
570 it->second.Cancel();
571 }
572 m_ulMainPhySwitch.erase(it);
573 }
574
575 // Unblock the other EMLSR links and start the MediumSyncDelay timer, provided that the TXOP
576 // included the transmission of at least a frame and there is no ongoing DL TXOP on this link.
577 // Indeed, the UL TXOP may have ended because the transmission of a frame failed and the
578 // corresponding TX timeout (leading to this call) may have occurred after the reception on
579 // this link of an ICF starting a DL TXOP. If the EMLSR Manager unblocked the other EMLSR
580 // links, another TXOP could be started on another EMLSR link (possibly leading to a crash)
581 // while the DL TXOP on this link is ongoing.
582 if (ongoingDlTxop)
583 {
584 NS_LOG_DEBUG("DL TXOP ongoing");
585 return;
586 }
587 if (ulTxopNotStarted)
588 {
589 NS_LOG_DEBUG("TXOP did not even start");
590 return;
591 }
592
593 if (m_auxPhyToSleep)
594 {
595 // TXOP ended, resume all aux PHYs from sleep
597 }
598
599 DoNotifyTxopEnd(linkId);
600
601 Simulator::ScheduleNow([=, this]() {
602 // unblock transmissions and resume medium access on other EMLSR links
603 std::set<uint8_t> linkIds;
604 for (auto id : m_staMac->GetLinkIds())
605 {
606 if ((id != linkId) && m_staMac->IsEmlsrLink(id))
607 {
608 linkIds.insert(id);
609 }
610 }
612 });
613}
614
615void
617{
618 NS_LOG_FUNCTION(this << linkId << duration.As(Time::US));
620
621 // The STA may choose not to (re)start the MediumSyncDelay timer if the transmission duration
622 // is less than or equal to aMediumSyncThreshold. (Sec. 35.3.16.8.1 802.11be D5.1)
624 {
625 return;
626 }
627
628 // iterate over all the other EMLSR links
629 for (auto id : m_staMac->GetLinkIds())
630 {
631 if (id != linkId && m_staMac->IsEmlsrLink(id))
632 {
634 }
635 }
636}
637
638void
640{
641 NS_LOG_FUNCTION(this << phy << linkId);
642
643 // if a MediumSyncDelay timer is running for the link on which the main PHY is going to
644 // operate, set the CCA ED threshold to the MediumSyncDelay OFDM ED threshold
645 if (auto statusIt = m_mediumSyncDelayStatus.find(linkId);
646 statusIt != m_mediumSyncDelayStatus.cend() && statusIt->second.timer.IsPending())
647 {
648 NS_LOG_DEBUG("Setting CCA ED threshold of PHY " << phy << " to " << +m_msdOfdmEdThreshold
649 << " on link " << +linkId);
650
651 // store the current CCA ED threshold in the m_prevCcaEdThreshold map, if not present
652 m_prevCcaEdThreshold.try_emplace(phy, phy->GetCcaEdThreshold());
653
654 phy->SetCcaEdThreshold(m_msdOfdmEdThreshold);
655 }
656 // otherwise, restore the previous value for the CCA ED threshold (if any)
657 else if (auto threshIt = m_prevCcaEdThreshold.find(phy);
658 threshIt != m_prevCcaEdThreshold.cend())
659 {
660 NS_LOG_DEBUG("Resetting CCA ED threshold of PHY " << phy << " to " << threshIt->second
661 << " on link " << +linkId);
662 phy->SetCcaEdThreshold(threshIt->second);
663 m_prevCcaEdThreshold.erase(threshIt);
664 }
665}
666
667void
669 bool noSwitchDelay,
670 bool resetBackoff,
671 bool requestAccess)
672{
673 NS_LOG_FUNCTION(this << linkId << noSwitchDelay << resetBackoff << requestAccess);
674
675 auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
676
677 NS_ASSERT_MSG(mainPhy != m_staMac->GetWifiPhy(linkId),
678 "Main PHY is already operating on link " << +linkId);
679
680 // find the link on which the main PHY is operating
681 auto currMainPhyLinkId = m_staMac->GetLinkForPhy(mainPhy);
682
683 NS_ASSERT_MSG(currMainPhyLinkId.has_value() || mainPhy->IsStateSwitching(),
684 "If the main PHY is not operating on a link, it must be switching");
685
686 const auto newMainPhyChannel = GetChannelForMainPhy(linkId);
687
688 NS_LOG_DEBUG("Main PHY (" << mainPhy << ") is about to switch to " << newMainPhyChannel
689 << " to operate on link " << +linkId);
690
691 // if the main PHY is operating on a link, notify the channel access manager of the upcoming
692 // channel switch
693 if (currMainPhyLinkId.has_value())
694 {
695 m_staMac->GetChannelAccessManager(*currMainPhyLinkId)
696 ->NotifySwitchingEmlsrLink(mainPhy, newMainPhyChannel, linkId);
697 }
698
699 // this assert also ensures that the actual channel switch is not delayed
700 NS_ASSERT_MSG(!mainPhy->GetState()->IsStateTx(),
701 "We should not ask the main PHY to switch channel while transmitting");
702
703 // request the main PHY to switch channel
704 const auto delay = mainPhy->GetChannelSwitchDelay();
705 const auto pifs = mainPhy->GetSifs() + mainPhy->GetSlot();
706 NS_ASSERT_MSG(noSwitchDelay || delay <= std::max(m_lastAdvTransitionDelay, pifs),
707 "Channel switch delay ("
708 << delay.As(Time::US)
709 << ") should be shorter than the maximum between the Transition delay ("
710 << m_lastAdvTransitionDelay.As(Time::US) << ") and a PIFS ("
711 << pifs.As(Time::US) << ")");
712 if (noSwitchDelay)
713 {
714 mainPhy->SetAttribute("ChannelSwitchDelay", TimeValue(Seconds(0)));
715 }
716 mainPhy->SetOperatingChannel(newMainPhyChannel);
717 // restore previous channel switch delay
718 if (noSwitchDelay)
719 {
720 mainPhy->SetAttribute("ChannelSwitchDelay", TimeValue(delay));
721 }
722 // re-enable short time slot, if needed
723 if (m_staMac->GetWifiRemoteStationManager(linkId)->GetShortSlotTimeEnabled())
724 {
725 mainPhy->SetSlot(MicroSeconds(9));
726 }
727
728 const auto timeToSwitchEnd = noSwitchDelay ? Seconds(0) : delay;
729
730 // if the main PHY is not operating on any link (because it was switching), it is not connected
731 // to a channel access manager, hence we must notify the MAC of the new link switch
732 if (!currMainPhyLinkId.has_value())
733 {
734 m_staMac->NotifySwitchingEmlsrLink(mainPhy, linkId, timeToSwitchEnd);
735 }
736
737 if (resetBackoff && currMainPhyLinkId.has_value())
738 {
739 // reset the backoffs on the link left by the main PHY
740 m_staMac->GetChannelAccessManager(*currMainPhyLinkId)->ResetAllBackoffs();
741 }
742
743 if (requestAccess)
744 {
745 // schedule channel access request on the new link when switch is completed
746 Simulator::Schedule(timeToSwitchEnd, [=, this]() {
747 for (const auto& [acIndex, ac] : wifiAcList)
748 {
749 m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
750 linkId,
753 }
754 });
755 }
756
757 SetCcaEdThresholdOnLinkSwitch(mainPhy, linkId);
758 NotifyMainPhySwitch(currMainPhyLinkId, linkId, timeToSwitchEnd);
759}
760
761void
762EmlsrManager::SwitchAuxPhy(Ptr<WifiPhy> auxPhy, uint8_t currLinkId, uint8_t nextLinkId)
763{
764 NS_LOG_FUNCTION(this << auxPhy << currLinkId << nextLinkId);
765
766 auto newAuxPhyChannel = GetChannelForAuxPhy(nextLinkId);
767
768 NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") is about to switch to " << newAuxPhyChannel
769 << " to operate on link " << +nextLinkId);
770
771 GetStaMac()
772 ->GetChannelAccessManager(currLinkId)
773 ->NotifySwitchingEmlsrLink(auxPhy, newAuxPhyChannel, nextLinkId);
774
775 auxPhy->SetOperatingChannel(newAuxPhyChannel);
776 // re-enable short time slot, if needed
777 if (m_staMac->GetWifiRemoteStationManager(nextLinkId)->GetShortSlotTimeEnabled())
778 {
779 auxPhy->SetSlot(MicroSeconds(9));
780 }
781
782 // schedule channel access request on the new link when switch is completed
783 Simulator::Schedule(auxPhy->GetChannelSwitchDelay(), [=, this]() {
784 for (const auto& [acIndex, ac] : wifiAcList)
785 {
786 m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
787 nextLinkId,
788 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
789 Txop::CHECK_MEDIUM_BUSY);
790 }
791 });
792
793 SetCcaEdThresholdOnLinkSwitch(auxPhy, nextLinkId);
794}
795
796void
797EmlsrManager::StartMediumSyncDelayTimer(uint8_t linkId)
798{
799 NS_LOG_FUNCTION(this << linkId);
800
801 if (m_mediumSyncDuration.IsZero())
802 {
803 NS_LOG_DEBUG("MediumSyncDuration is zero");
804 return;
805 }
806
807 auto phy = m_staMac->GetWifiPhy(linkId);
808
809 if (!phy)
810 {
811 NS_LOG_DEBUG("No PHY operating on link " << +linkId);
812 // MSD timer will be started when a PHY will be operating on this link
813 return;
814 }
815
816 const auto [it, inserted] = m_mediumSyncDelayStatus.try_emplace(linkId);
817
818 // reset the max number of TXOP attempts
819 it->second.msdNTxopsLeft = m_msdMaxNTxops;
820
821 if (!it->second.timer.IsPending())
822 {
823 NS_LOG_DEBUG("Setting CCA ED threshold on link "
824 << +linkId << " to " << +m_msdOfdmEdThreshold << " PHY " << phy);
825 m_prevCcaEdThreshold[phy] = phy->GetCcaEdThreshold();
826 phy->SetCcaEdThreshold(m_msdOfdmEdThreshold);
827 }
828
829 // (re)start the timer
830 it->second.timer.Cancel();
831 NS_LOG_DEBUG("Starting MediumSyncDelay timer for " << m_mediumSyncDuration.As(Time::US)
832 << " on link " << +linkId);
833 it->second.timer = Simulator::Schedule(m_mediumSyncDuration,
834 &EmlsrManager::MediumSyncDelayTimerExpired,
835 this,
836 linkId);
837}
838
839void
840EmlsrManager::CancelMediumSyncDelayTimer(uint8_t linkId)
841{
842 NS_LOG_FUNCTION(this << linkId);
843
844 auto timerIt = m_mediumSyncDelayStatus.find(linkId);
845
846 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
847
848 timerIt->second.timer.Cancel();
849 MediumSyncDelayTimerExpired(linkId);
850}
851
852void
853EmlsrManager::MediumSyncDelayTimerExpired(uint8_t linkId)
854{
855 NS_LOG_FUNCTION(this << linkId);
856
857 auto timerIt = m_mediumSyncDelayStatus.find(linkId);
858
859 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && !timerIt->second.timer.IsPending());
860
861 // reset the MSD OFDM ED threshold
862 auto phy = m_staMac->GetWifiPhy(linkId);
863
864 if (!phy)
865 {
866 // no PHY is operating on this link. This may happen when a MediumSyncDelay timer expires
867 // on the link left "uncovered" by the main PHY that is operating on another link (and the
868 // aux PHY of that link did not switch). In this case, do nothing, since the CCA ED
869 // threshold on the main PHY will be restored once the main PHY switches back to its link
870 return;
871 }
872
873 auto threshIt = m_prevCcaEdThreshold.find(phy);
874 NS_ASSERT_MSG(threshIt != m_prevCcaEdThreshold.cend(),
875 "No value to restore for CCA ED threshold on PHY " << phy);
876 NS_LOG_DEBUG("Resetting CCA ED threshold of PHY " << phy << " to " << threshIt->second
877 << " on link " << +linkId);
878 phy->SetCcaEdThreshold(threshIt->second);
879 m_prevCcaEdThreshold.erase(threshIt);
880}
881
882void
883EmlsrManager::DecrementMediumSyncDelayNTxops(uint8_t linkId)
884{
885 NS_LOG_FUNCTION(this << linkId);
886
887 const auto timerIt = m_mediumSyncDelayStatus.find(linkId);
888
889 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
890 NS_ASSERT(timerIt->second.msdNTxopsLeft != 0);
891
892 if (timerIt->second.msdNTxopsLeft)
893 {
894 --timerIt->second.msdNTxopsLeft.value();
895 }
896}
897
898void
899EmlsrManager::ResetMediumSyncDelayNTxops(uint8_t linkId)
900{
901 NS_LOG_FUNCTION(this << linkId);
902
903 auto timerIt = m_mediumSyncDelayStatus.find(linkId);
904
905 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
906 timerIt->second.msdNTxopsLeft.reset();
907}
908
909bool
910EmlsrManager::MediumSyncDelayNTxopsExceeded(uint8_t linkId)
911{
912 NS_LOG_FUNCTION(this << linkId);
913
914 auto timerIt = m_mediumSyncDelayStatus.find(linkId);
915
916 NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsPending());
917 return timerIt->second.msdNTxopsLeft == 0;
918}
919
921EmlsrManager::GetEmlOmn()
922{
923 MgtEmlOmn frame;
924
925 // Add the EMLSR Parameter Update field if needed
926 if (m_lastAdvPaddingDelay != m_emlsrPaddingDelay ||
927 m_lastAdvTransitionDelay != m_emlsrTransitionDelay)
928 {
929 m_lastAdvPaddingDelay = m_emlsrPaddingDelay;
930 m_lastAdvTransitionDelay = m_emlsrTransitionDelay;
933 frame.m_emlsrParamUpdate->paddingDelay =
934 CommonInfoBasicMle::EncodeEmlsrPaddingDelay(m_lastAdvPaddingDelay);
935 frame.m_emlsrParamUpdate->transitionDelay =
936 CommonInfoBasicMle::EncodeEmlsrTransitionDelay(m_lastAdvTransitionDelay);
937 }
938
939 // We must verify that the links included in the given EMLSR link set (if any) have been setup.
940 auto setupLinkIds = m_staMac->GetSetupLinkIds();
941
942 for (auto emlsrLinkIt = m_nextEmlsrLinks->begin(); emlsrLinkIt != m_nextEmlsrLinks->end();)
943 {
944 if (auto setupLinkIt = setupLinkIds.find(*emlsrLinkIt); setupLinkIt != setupLinkIds.cend())
945 {
946 setupLinkIds.erase(setupLinkIt);
947 frame.SetLinkIdInBitmap(*emlsrLinkIt);
948 emlsrLinkIt++;
949 }
950 else
951 {
952 NS_LOG_DEBUG("Link ID " << +(*emlsrLinkIt) << " has not been setup");
953 emlsrLinkIt = m_nextEmlsrLinks->erase(emlsrLinkIt);
954 }
955 }
956
957 // EMLSR Mode is enabled if and only if the set of EMLSR links is not empty
958 frame.m_emlControl.emlsrMode = m_nextEmlsrLinks->empty() ? 0 : 1;
959
960 return frame;
961}
962
963void
964EmlsrManager::SendEmlOmn()
965{
966 NS_LOG_FUNCTION(this);
967
968 NS_ABORT_MSG_IF(!m_emlsrTransitionTimeout,
969 "AP did not advertise a Transition Timeout, cannot send EML notification");
970 NS_ASSERT_MSG(m_nextEmlsrLinks, "Need to set EMLSR links before calling this method");
971
972 // TODO if this is a single radio non-AP MLD and not all setup links are in the EMLSR link
973 // set, we have to put setup links that are not included in the given EMLSR link set (i.e.,
974 // those remaining in setupLinkIds, if m_nextEmlsrLinks is not empty) in the sleep mode:
975 // For the EMLSR mode enabled in a single radio non-AP MLD, the STA(s) affiliated with
976 // the non-AP MLD that operates on the enabled link(s) that corresponds to the bit
977 // position(s) of the EMLSR Link Bitmap subfield set to 0 shall be in doze state if a
978 // non-AP STA affiliated with the non-AP MLD that operates on one of the EMLSR links is
979 // in awake state. (Sec. 35.3.17 of 802.11be D3.0)
980
981 auto frame = GetEmlOmn();
982 auto linkId = GetLinkToSendEmlOmn();
983 GetEhtFem(linkId)->SendEmlOmn(m_staMac->GetBssid(linkId), frame);
984}
985
986void
987EmlsrManager::TxOk(Ptr<const WifiMpdu> mpdu)
988{
989 NS_LOG_FUNCTION(this << *mpdu);
990
991 const auto& hdr = mpdu->GetHeader();
992
993 if (hdr.IsAssocReq())
994 {
995 // store padding delay and transition delay advertised in AssocReq
996 MgtAssocRequestHeader assocReq;
997 mpdu->GetPacket()->PeekHeader(assocReq);
998 auto& mle = assocReq.Get<MultiLinkElement>();
999 NS_ASSERT_MSG(mle, "AssocReq should contain a Multi-Link Element");
1000 m_lastAdvPaddingDelay = mle->GetEmlsrPaddingDelay();
1001 m_lastAdvTransitionDelay = mle->GetEmlsrTransitionDelay();
1002 }
1003
1004 if (hdr.IsMgt() && hdr.IsAction())
1005 {
1006 if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
1007 category == WifiActionHeader::PROTECTED_EHT &&
1008 action.protectedEhtAction ==
1009 WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
1010 {
1011 // the EML Operating Mode Notification frame that we sent has been acknowledged.
1012 // Start the transition timeout to wait until the request can be made effective
1013 NS_ASSERT_MSG(m_emlsrTransitionTimeout, "No transition timeout received from AP");
1014 m_transitionTimeoutEvent = Simulator::Schedule(*m_emlsrTransitionTimeout,
1015 &EmlsrManager::ChangeEmlsrMode,
1016 this);
1017 }
1018 }
1019}
1020
1021void
1022EmlsrManager::TxDropped(WifiMacDropReason reason, Ptr<const WifiMpdu> mpdu)
1023{
1024 NS_LOG_FUNCTION(this << reason << *mpdu);
1025
1026 const auto& hdr = mpdu->GetHeader();
1027
1028 if (hdr.IsMgt() && hdr.IsAction())
1029 {
1030 auto pkt = mpdu->GetPacket()->Copy();
1031 if (auto [category, action] = WifiActionHeader::Remove(pkt);
1032 category == WifiActionHeader::PROTECTED_EHT &&
1033 action.protectedEhtAction ==
1034 WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
1035 {
1036 // the EML Operating Mode Notification frame has been dropped. Ask the subclass
1037 // whether the frame needs to be resent
1038 auto linkId = ResendNotification(mpdu);
1039 if (linkId)
1040 {
1041 MgtEmlOmn frame;
1042 pkt->RemoveHeader(frame);
1043 GetEhtFem(*linkId)->SendEmlOmn(m_staMac->GetBssid(*linkId), frame);
1044 }
1045 else
1046 {
1047 m_nextEmlsrLinks.reset();
1048 }
1049 }
1050 }
1051}
1052
1053void
1054EmlsrManager::ChangeEmlsrMode()
1055{
1056 NS_LOG_FUNCTION(this);
1057
1058 // After the successful transmission of the EML Operating Mode Notification frame by the
1059 // non-AP STA affiliated with the non-AP MLD, the non-AP MLD shall operate in the EMLSR mode
1060 // and the other non-AP STAs operating on the corresponding EMLSR links shall transition to
1061 // active mode after the transition delay indicated in the Transition Timeout subfield in the
1062 // EML Capabilities subfield of the Basic Multi-Link element or immediately after receiving an
1063 // EML Operating Mode Notification frame from one of the APs operating on the EMLSR links and
1064 // affiliated with the AP MLD. (Sec. 35.3.17 of 802.11be D3.0)
1065 NS_ASSERT_MSG(m_nextEmlsrLinks, "No set of EMLSR links stored");
1066 m_emlsrLinks.swap(*m_nextEmlsrLinks);
1067 m_nextEmlsrLinks.reset();
1068
1069 // Make other non-AP STAs operating on the corresponding EMLSR links transition to
1070 // active mode or passive mode (depending on whether EMLSR mode has been enabled or disabled)
1071 m_staMac->NotifyEmlsrModeChanged(m_emlsrLinks);
1072 // Enforce the limit on the max channel width supported by aux PHYs
1073 ApplyMaxChannelWidthAndModClassOnAuxPhys();
1074
1075 NotifyEmlsrModeChanged();
1076}
1077
1078void
1079EmlsrManager::ApplyMaxChannelWidthAndModClassOnAuxPhys()
1080{
1081 NS_LOG_FUNCTION(this);
1082 auto currMainPhyLinkId = m_staMac->GetLinkForPhy(m_mainPhyId);
1083 NS_ASSERT(currMainPhyLinkId);
1084
1085 for (const auto linkId : m_staMac->GetLinkIds())
1086 {
1087 auto auxPhy = m_staMac->GetWifiPhy(linkId);
1088 auto channel = GetChannelForAuxPhy(linkId);
1089
1090 if (linkId == currMainPhyLinkId || !m_staMac->IsEmlsrLink(linkId) ||
1091 auxPhy->GetOperatingChannel() == channel)
1092 {
1093 continue;
1094 }
1095
1096 auxPhy->SetMaxModulationClassSupported(m_auxPhyMaxModClass);
1097
1098 NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") is about to switch to " << channel
1099 << " to operate on link " << +linkId);
1100 // We cannot simply set the new channel, because otherwise the MAC will disable
1101 // the setup link. We need to inform the MAC (via the Channel Access Manager) that
1102 // this channel switch must not have such a consequence. We already have a method
1103 // for doing so, i.e., inform the MAC that the PHY is switching channel to operate
1104 // on the "same" link.
1105 auto cam = m_staMac->GetChannelAccessManager(linkId);
1106 cam->NotifySwitchingEmlsrLink(auxPhy, channel, linkId);
1107
1108 // apply channel width limitation assuming an instantaneous channel switch
1109 const auto delay = auxPhy->GetChannelSwitchDelay();
1110 auxPhy->SetAttribute("ChannelSwitchDelay", TimeValue(Time{0}));
1111 auxPhy->SetOperatingChannel(channel);
1112 auxPhy->SetAttribute("ChannelSwitchDelay", TimeValue(delay));
1113
1114 // the way the ChannelAccessManager handles EMLSR link switch implies that a PHY listener
1115 // is removed when the channel switch starts and another one is attached when the channel
1116 // switch ends. In the meantime, no PHY is connected to the ChannelAccessManager. Thus,
1117 // reset all backoffs (so that access timeout is also cancelled) when the channel switch
1118 // starts and request channel access (if needed) when the channel switch ends.
1119 cam->ResetAllBackoffs();
1120 Simulator::ScheduleNow([=, this]() {
1121 for (const auto& [acIndex, ac] : wifiAcList)
1122 {
1123 m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
1124 linkId,
1125 Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
1126 Txop::CHECK_MEDIUM_BUSY);
1127 }
1128 });
1129 }
1130}
1131
1132void
1133EmlsrManager::ComputeOperatingChannels()
1134{
1135 NS_LOG_FUNCTION(this);
1136
1137 m_mainPhyChannels.clear();
1138 m_auxPhyChannels.clear();
1139
1140 auto linkIds = m_staMac->GetSetupLinkIds();
1141
1142 for (auto linkId : linkIds)
1143 {
1144 const auto& channel = m_staMac->GetWifiPhy(linkId)->GetOperatingChannel();
1145 m_mainPhyChannels.emplace(linkId, channel);
1146
1147 auto mainPhyChWidth = channel.GetWidth();
1148 auto auxPhyMaxWidth =
1149 std::min(m_auxPhyMaxWidth, GetMaximumChannelWidth(m_auxPhyMaxModClass));
1150 if (auxPhyMaxWidth >= mainPhyChWidth)
1151 {
1152 // same channel can be used by aux PHYs
1153 m_auxPhyChannels.emplace(linkId, channel);
1154 continue;
1155 }
1156 // aux PHYs will operate on a primary subchannel
1157 auto freq = channel.GetPrimaryChannelCenterFrequency(auxPhyMaxWidth);
1158 auto chIt = WifiPhyOperatingChannel::FindFirst(0,
1159 freq,
1160 auxPhyMaxWidth,
1162 channel.GetPhyBand());
1163 NS_ASSERT_MSG(chIt != WifiPhyOperatingChannel::m_frequencyChannels.end(),
1164 "Primary" << auxPhyMaxWidth << " channel not found");
1165 m_auxPhyChannels.emplace(linkId, chIt);
1166 // find the P20 index for the channel used by the aux PHYs
1167 auto p20Index = channel.GetPrimaryChannelIndex(20);
1168 while (mainPhyChWidth > auxPhyMaxWidth)
1169 {
1170 mainPhyChWidth /= 2;
1171 p20Index /= 2;
1172 }
1173 m_auxPhyChannels[linkId].SetPrimary20Index(p20Index);
1174 }
1175}
1176
1178EmlsrManager::GetChannelForMainPhy(uint8_t linkId) const
1179{
1180 auto it = m_mainPhyChannels.find(linkId);
1181 NS_ASSERT_MSG(it != m_mainPhyChannels.end(),
1182 "Channel for main PHY on link ID " << +linkId << " not found");
1183 return it->second;
1184}
1185
1187EmlsrManager::GetChannelForAuxPhy(uint8_t linkId) const
1188{
1189 auto it = m_auxPhyChannels.find(linkId);
1190 NS_ASSERT_MSG(it != m_auxPhyChannels.end(),
1191 "Channel for aux PHY on link ID " << +linkId << " not found");
1192 return it->second;
1193}
1194
1195void
1196EmlsrManager::CancelAllSleepEvents()
1197{
1198 NS_LOG_FUNCTION(this);
1199
1200 for (auto& [id, event] : m_auxPhyToSleepEvents)
1201 {
1202 event.Cancel();
1203 }
1204 m_auxPhyToSleepEvents.clear();
1205}
1206
1207void
1208EmlsrManager::SetSleepStateForAllAuxPhys(bool sleep)
1209{
1210 NS_LOG_FUNCTION(this << sleep);
1211
1212 CancelAllSleepEvents();
1213
1214 for (const auto& phy : m_staMac->GetDevice()->GetPhys())
1215 {
1216 if (phy->GetPhyId() == m_mainPhyId)
1217 {
1218 continue; // do not set sleep mode/resume from sleep the main PHY
1219 }
1220
1221 auto linkId = m_staMac->GetLinkForPhy(phy);
1222
1223 if (linkId.has_value() && !m_staMac->IsEmlsrLink(*linkId))
1224 {
1225 continue; // this PHY is not operating on an EMLSR link
1226 }
1227
1228 if (!sleep)
1229 {
1230 if (!phy->IsStateSleep())
1231 {
1232 continue; // nothing to do
1233 }
1234
1235 NS_LOG_DEBUG("PHY " << +phy->GetPhyId() << ": Resuming from sleep");
1236 phy->ResumeFromSleep();
1237
1238 // if this aux PHY is operating on a link, check if it lost medium sync
1239 if (linkId.has_value())
1240 {
1241 auto it = m_startSleep.find(phy->GetPhyId());
1242 NS_ASSERT_MSG(it != m_startSleep.cend(),
1243 "No start sleep info for PHY ID " << phy->GetPhyId());
1244 const auto sleepDuration = Simulator::Now() - it->second;
1245 m_startSleep.erase(it);
1246
1247 if (sleepDuration > MicroSeconds(MEDIUM_SYNC_THRESHOLD_USEC))
1248 {
1249 StartMediumSyncDelayTimer(*linkId);
1250 }
1251 }
1252
1253 continue; // resuming the PHY from sleep has been handled
1254 }
1255
1256 if (phy->IsStateSleep())
1257 {
1258 continue; // nothing to do
1259 }
1260
1261 // we force WifiPhy::SetSleepMode() to abort RX and switch immediately to sleep mode in
1262 // case the state is RX. If the state is TX or SWITCHING, WifiPhy::SetSleepMode() postpones
1263 // setting sleep mode to end of TX or SWITCHING. This is fine, but we schedule events here
1264 // to correctly record the time an aux PHY was put to sleep and to be able to cancel them
1265 // later if needed
1266 std::stringstream ss;
1267 auto s = std::string("PHY ") + std::to_string(phy->GetPhyId()) + ": Setting sleep mode";
1268 if (phy->IsStateTx() || phy->IsStateSwitching())
1269 {
1270 const auto delay = phy->GetDelayUntilIdle();
1271 NS_LOG_DEBUG(s << " in " << delay.As(Time::US));
1272 m_auxPhyToSleepEvents[phy->GetPhyId()] = Simulator::Schedule(delay, [=, this]() {
1273 phy->SetSleepMode(true);
1274 m_startSleep[phy->GetPhyId()] = Simulator::Now();
1275 });
1276 }
1277 else
1278 {
1279 NS_LOG_DEBUG(s);
1280 phy->SetSleepMode(true);
1281 m_startSleep[phy->GetPhyId()] = Simulator::Now();
1282 }
1283 }
1284}
1285
1286} // namespace ns3
A container for one type of attribute.
AttributeValue implementation for Boolean.
Definition boolean.h:26
void SendEmlOmn()
Send an EML Operating Mode Notification frame.
Time GetMediumSyncDuration() const
void ComputeOperatingChannels()
Compute the operating channels that the main PHY and the aux PHY(s) must switch to in order to operat...
void NotifyProtectionCompleted(uint8_t linkId)
Notify that protection (if required) is completed and data frame exchange can start on the given link...
void SetTransitionTimeout(Time timeout)
Set the Transition Timeout advertised by the associated AP with EMLSR activated.
virtual void NotifyMainPhySwitch(std::optional< uint8_t > currLinkId, uint8_t nextLinkId, Time duration)=0
Notify subclass that the main PHY is switching channel to operate on another link.
static constexpr uint16_t MEDIUM_SYNC_THRESHOLD_USEC
The aMediumSyncThreshold defined by Sec. 35.3.16.18.1 of 802.11be D4.0.
bool m_auxPhyTxCapable
whether Aux PHYs are capable of transmitting PPDUs
std::optional< Time > GetTransitionTimeout() const
std::pair< bool, Time > GetDelayUntilAccessRequest(uint8_t linkId, AcIndex aci)
Notify that an UL TXOP is gained on the given link by the given AC.
virtual std::pair< bool, Time > DoGetDelayUntilAccessRequest(uint8_t linkId)=0
Subclasses have to provide an implementation for this method, that is called by the base class when t...
Ptr< EhtFrameExchangeManager > GetEhtFem(uint8_t linkId) const
void TxDropped(WifiMacDropReason reason, Ptr< const WifiMpdu > mpdu)
Notify that the given MPDU has been discarded for the given reason.
void NotifyUlTxopStart(uint8_t linkId)
Notify the start of an UL TXOP on the given link.
void TxOk(Ptr< const WifiMpdu > mpdu)
Notify the acknowledgment of the given MPDU.
void NotifyTxopEnd(uint8_t linkId, bool ulTxopNotStarted=false, bool ongoingDlTxop=false)
Notify the end of a TXOP on the given link.
std::map< uint8_t, EventId > m_ulMainPhySwitch
link ID-indexed map of timers started when an aux PHY gains an UL TXOP and schedules a channel switch...
bool m_inDeviceInterference
whether in-device interference is such that a PHY cannot decode anything and cannot decrease the back...
void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay, bool resetBackoff, bool requestAccess)
Switch channel on the Main PHY so that it operates on the given link.
bool GetCamStateReset() const
void SetEmlsrLinks(const std::set< uint8_t > &linkIds)
Take actions to enable EMLSR mode on the given set of links, if non-empty, or disable EMLSR mode,...
void SetMediumSyncOfdmEdThreshold(int8_t threshold)
Set the Medium Synchronization OFDM ED threshold (dBm) to use while the MediumSyncDelay timer is runn...
uint8_t m_mainPhyId
ID of main PHY (position in the vector of PHYs held by WifiNetDevice)
int8_t GetMediumSyncOfdmEdThreshold() const
void NotifyIcfReceived(uint8_t linkId)
Notify the reception of an initial Control frame on the given link.
std::map< uint8_t, MediumSyncDelayStatus > m_mediumSyncDelayStatus
the status of MediumSyncDelay timers (link ID-indexed)
bool m_auxPhyToSleep
whether Aux PHYs should be put into sleep mode while the Main PHY is carrying out a (DL or UL) TXOP
virtual std::pair< bool, Time > GetDelayUnlessMainPhyTakesOverUlTxop(uint8_t linkId)=0
Subclasses have to provide an implementation for this method, that is called by the base class when t...
virtual void SwitchMainPhyIfTxopGainedByAuxPhy(uint8_t linkId, AcIndex aci)=0
Subclasses have to provide an implementation for this method, that is called by the base class when t...
void NotifyMgtFrameReceived(Ptr< const WifiMpdu > mpdu, uint8_t linkId)
Notify the reception of a management frame addressed to us.
virtual void DoNotifyUlTxopStart(uint8_t linkId)=0
Notify the subclass of the start of an UL TXOP on the given link.
Ptr< StaWifiMac > m_staMac
the MAC of the managed non-AP MLD
virtual void DoNotifyMgtFrameReceived(Ptr< const WifiMpdu > mpdu, uint8_t linkId)=0
Notify the subclass of the reception of a management frame addressed to us.
Time m_emlsrPaddingDelay
EMLSR Padding delay.
bool GetInDeviceInterference() const
MHz_u m_auxPhyMaxWidth
max channel width supported by aux PHYs
virtual void NotifyInDeviceInterferenceStart(uint8_t linkId, Time duration)
Notify that an STA affiliated with the EMLSR client is causing in-device interference for the given a...
void SetMediumSyncMaxNTxops(std::optional< uint8_t > nTxops)
Set the maximum number of TXOPs a non-AP STA is allowed to attempt to initiate while the MediumSyncDe...
Time m_emlsrTransitionDelay
EMLSR Transition delay.
void SetWifiMac(Ptr< StaWifiMac > mac)
Set the wifi MAC.
bool GetAuxPhyTxCapable() const
const std::set< uint8_t > & GetEmlsrLinks() const
virtual void EmlsrLinkSwitchCallback(uint8_t linkId, Ptr< WifiPhy > phy)
Callback connected to the EmlsrLinkSwitch trace source of StaWifiMac.
Time m_mediumSyncDuration
duration of the MediumSyncDelay timer
std::optional< Time > m_emlsrTransitionTimeout
Transition timeout advertised by APs with EMLSR activated.
void SwitchAuxPhy(Ptr< WifiPhy > auxPhy, uint8_t currLinkId, uint8_t nextLinkId)
Switch channel on the Aux PHY operating on the given current link so that it operates on the given ne...
std::map< Ptr< WifiPhy >, dBm_u > m_prevCcaEdThreshold
the CCA sensitivity threshold to restore once the MediumSyncDelay timer expires or the PHY moves to a...
std::optional< Time > GetElapsedMediumSyncDelayTimer(uint8_t linkId) const
Check whether the MediumSyncDelay timer is running for the STA operating on the given link.
virtual void DoNotifyTxopEnd(uint8_t linkId)=0
Notify the subclass of the end of a TXOP on the given link.
virtual void DoSetWifiMac(Ptr< StaWifiMac > mac)
Allow subclasses to take actions when the MAC is set.
std::map< uint8_t, Time > m_noPhySince
link ID-indexed map of the time since no PHY is operating on the link
void SetAuxPhyTxCapable(bool capable)
Set the member variable indicating whether Aux PHYs are capable of transmitting PPDUs.
std::optional< std::set< uint8_t > > m_nextEmlsrLinks
ID of the links that will become the EMLSR links when the pending notification frame is acknowledged.
void SetCcaEdThresholdOnLinkSwitch(Ptr< WifiPhy > phy, uint8_t linkId)
Set the CCA ED threshold (if needed) on the given PHY that is switching channel to operate on the giv...
void SetMainPhyId(uint8_t mainPhyId)
Set the ID of main PHY (position in the vector of PHYs held by WifiNetDevice).
const WifiPhyOperatingChannel & GetChannelForMainPhy(uint8_t linkId) const
~EmlsrManager() override
void SetMediumSyncDuration(Time duration)
Set the duration of the MediumSyncDelay timer.
static constexpr bool RESET_BACKOFF
reset backoff on main PHY switch
Time m_lastAdvTransitionDelay
last advertised transition delay
static constexpr bool DONT_REQUEST_ACCESS
do not request channel access when PHY switch ends
void DoDispose() override
Destructor implementation.
void StartMediumSyncDelayTimer(uint8_t linkId)
Start the MediumSyncDelay timer and take the appropriate actions.
int8_t m_msdOfdmEdThreshold
MediumSyncDelay OFDM ED threshold.
std::optional< uint8_t > m_msdMaxNTxops
MediumSyncDelay max number of TXOPs.
Ptr< StaWifiMac > GetStaMac() const
WifiModulationClass m_auxPhyMaxModClass
max modulation class supported by aux PHYs
uint8_t GetMainPhyId() const
std::optional< uint8_t > GetMediumSyncMaxNTxops() const
void SetCamStateReset(bool enable)
Set the member variable indicating whether the state of the CAM should be reset when the main PHY swi...
virtual void NotifyRtsSent(uint8_t linkId, Ptr< const WifiPsdu > rts, const WifiTxVector &txVector)
Notify that RTS transmission is starting on the given link.
EventId m_transitionTimeoutEvent
Timer started after the successful transmission of an EML Operating Mode Notification frame.
virtual void DoNotifyIcfReceived(uint8_t linkId)=0
Notify the subclass of the reception of an initial Control frame on the given link.
void SetInDeviceInterference(bool enable)
Set the member variable indicating whether in-device interference is such that a PHY cannot decode an...
const WifiPhyOperatingChannel & GetChannelForAuxPhy(uint8_t linkId) const
bool m_resetCamState
whether to reset the state of CAM when main PHY switches channel
static TypeId GetTypeId()
Get the type ID.
std::set< uint8_t > m_emlsrLinks
ID of the EMLSR links (empty if EMLSR mode is disabled)
void SetSleepStateForAllAuxPhys(bool sleep)
Set sleep state or awake state for all aux PHYs.
Hold variables of type enum.
Definition enum.h:52
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition event-id.cc:44
bool IsPending() const
This method is syntactic sugar for !IsExpired().
Definition event-id.cc:65
EventImpl * PeekEventImpl() const
Definition event-id.cc:78
void Invoke()
Called by the simulation engine to notify the event that it is time to execute.
Definition event-impl.cc:36
Implement the header for management frames of type association request.
Implement the header for Action frames of type EML Operating Mode Notification.
void SetLinkIdInBitmap(uint8_t linkId)
Set the bit position in the link bitmap corresponding to the given link.
EmlControl m_emlControl
EML Control field.
std::optional< EmlsrParamUpdate > m_emlsrParamUpdate
EMLSR Parameter Update field.
A base class which provides memory management and object aggregation.
Definition object.h:78
virtual void DoDispose()
Destructor implementation.
Definition object.cc:433
bool IsInitialized() const
Check if the object has been initialized.
Definition object.cc:240
Smart pointer class similar to boost::intrusive_ptr.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:560
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition simulator.h:594
static Time GetDelayLeft(const EventId &id)
Get the remaining time until this event will execute.
Definition simulator.cc:206
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:404
@ US
microsecond
Definition nstime.h:107
AttributeValue implementation for Time.
Definition nstime.h:1431
static constexpr bool DIDNT_HAVE_FRAMES_TO_TRANSMIT
no packet available for transmission was in the queue
Definition txop.h:390
static constexpr bool CHECK_MEDIUM_BUSY
generation of backoff (also) depends on the busy/idle state of the medium
Definition txop.h:392
a unique identifier for an interface.
Definition type-id.h:48
@ ATTR_GET
The attribute can be read.
Definition type-id.h:53
@ ATTR_CONSTRUCT
The attribute can be written at construction-time.
Definition type-id.h:55
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
Hold an unsigned integer type.
Definition uinteger.h:34
static std::pair< CategoryValue, ActionValue > Peek(Ptr< const Packet > pkt)
Peek an Action header from the given packet.
Class that keeps track of all information about the current PHY operating channel.
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
#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< AttributeChecker > MakeAttributeContainerChecker()
Make uninitialized AttributeContainerChecker using explicit types.
Ptr< const AttributeAccessor > MakeAttributeContainerAccessor(T1 a1)
Make AttributeContainerAccessor using explicit types.
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
Ptr< const AttributeAccessor > MakeEnumAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition enum.h:221
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition nstime.h:1432
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1452
Ptr< const AttributeChecker > MakeUintegerChecker()
Definition uinteger.h:85
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Create an AttributeAccessor for a class data member, or a lone class get functor or set method.
Definition uinteger.h:35
#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_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1368
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1344
WifiMacDropReason
The reason why an MPDU was dropped.
Definition wifi-mac.h:70
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition qos-utils.h:62
@ STA
Definition wifi-mac.h:58
@ WIFI_STANDARD_UNSPECIFIED
@ WIFI_MOD_CLASS_OFDM
OFDM (Clause 17)
@ WIFI_MOD_CLASS_HR_DSSS
HR/DSSS (Clause 16)
@ WIFI_MOD_CLASS_HT
HT (Clause 19)
@ WIFI_MOD_CLASS_EHT
EHT (Clause 36)
@ WIFI_MOD_CLASS_VHT
VHT (Clause 22)
@ WIFI_MOD_CLASS_HE
HE (Clause 27)
@ WIFI_MOD_CLASS_ERP_OFDM
ERP-OFDM (18.4)
Every class exported by the ns3 library is enclosed in the ns3 namespace.
static constexpr uint8_t DEFAULT_MSD_MAX_N_TXOPS
default MediumSyncDelay max number of TXOP attempts
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition callback.h:684
Ptr< const AttributeChecker > MakeEnumChecker(T v, std::string n, Ts... args)
Make an EnumChecker pre-configured with a set of allowed values by name.
Definition enum.h:179
MHz_u GetMaximumChannelWidth(WifiModulationClass modulation)
Get the maximum channel width allowed for the given modulation class.
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 constexpr int8_t DEFAULT_MSD_OFDM_ED_THRESH
default MediumSyncDelay timer OFDM ED threshold
@ LOG_FUNCTION
Function tracing for non-trivial function calls.
Definition log.h:95
Ptr< T1 > StaticCast(const Ptr< T2 > &p)
Cast a Ptr.
Definition ptr.h:587
static constexpr uint16_t DEFAULT_MSD_DURATION_USEC
default MediumSyncDelay timer duration (max PPDU TX time rounded to a multiple of 32 us)
ns3::Time timeout
uint8_t emlsrParamUpdateCtrl
EMLSR Parameter Update Control.
EMLSR Parameter Update field.