1 /*
2 * Copyright (c) 2024, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "wakeup_tx_scheduler.hpp"
30
31 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
32
33 #include "common/code_utils.hpp"
34 #include "common/log.hpp"
35 #include "common/num_utils.hpp"
36 #include "common/time.hpp"
37 #include "core/instance/instance.hpp"
38 #include "radio/radio.hpp"
39
40 namespace ot {
41
42 RegisterLogModule("WakeupTxSched");
43
WakeupTxScheduler(Instance & aInstance)44 WakeupTxScheduler::WakeupTxScheduler(Instance &aInstance)
45 : InstanceLocator(aInstance)
46 , mTxTimeUs(0)
47 , mTxEndTimeUs(0)
48 , mTimer(aInstance)
49 , mIsRunning(false)
50 {
51 UpdateFrameRequestAhead();
52 }
53
WakeUp(const Mac::ExtAddress & aWedAddress,uint16_t aIntervalUs,uint16_t aDurationMs)54 Error WakeupTxScheduler::WakeUp(const Mac::ExtAddress &aWedAddress, uint16_t aIntervalUs, uint16_t aDurationMs)
55 {
56 Error error = kErrorNone;
57
58 VerifyOrExit(!mIsRunning, error = kErrorInvalidState);
59
60 mWedAddress = aWedAddress;
61 mTxTimeUs = TimerMicro::GetNow() + mTxRequestAheadTimeUs;
62 mTxEndTimeUs = mTxTimeUs + aDurationMs * Time::kOneMsecInUsec + aIntervalUs;
63 mIntervalUs = aIntervalUs;
64 mIsRunning = true;
65
66 LogInfo("Started wake-up sequence to %s", aWedAddress.ToString().AsCString());
67
68 ScheduleTimer();
69
70 exit:
71 return error;
72 }
73
RequestWakeupFrameTransmission(void)74 void WakeupTxScheduler::RequestWakeupFrameTransmission(void) { Get<Mac::Mac>().RequestWakeupFrameTransmission(); }
75
76 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
77
PrepareWakeupFrame(Mac::TxFrames & aTxFrames)78 Mac::TxFrame *WakeupTxScheduler::PrepareWakeupFrame(Mac::TxFrames &aTxFrames)
79 {
80 Mac::TxFrame *frame = nullptr;
81 Mac::Address target;
82 Mac::Address source;
83 uint32_t radioTxDelay;
84 uint32_t rendezvousTimeUs;
85 TimeMicro nowUs = TimerMicro::GetNow();
86 Mac::ConnectionIe *connectionIe;
87
88 VerifyOrExit(mIsRunning);
89
90 target.SetExtended(mWedAddress);
91 source.SetExtended(Get<Mac::Mac>().GetExtAddress());
92 VerifyOrExit(mTxTimeUs >= nowUs);
93 radioTxDelay = mTxTimeUs - nowUs;
94
95 #if OPENTHREAD_CONFIG_MULTI_RADIO
96 frame = &aTxFrames.GetTxFrame(Mac::kRadioTypeIeee802154);
97 #else
98 frame = &aTxFrames.GetTxFrame();
99 #endif
100
101 VerifyOrExit(frame->GenerateWakeupFrame(Get<Mac::Mac>().GetPanId(), target, source) == kErrorNone, frame = nullptr);
102 frame->SetTxDelayBaseTime(static_cast<uint32_t>(Get<Radio>().GetNow()));
103 frame->SetTxDelay(radioTxDelay);
104 frame->SetCsmaCaEnabled(kWakeupFrameTxCca);
105 frame->SetMaxCsmaBackoffs(0);
106 frame->SetMaxFrameRetries(0);
107
108 // Rendezvous Time is the time between the end of a wake-up frame and the start of the first payload frame.
109 // For the n-th wake-up frame, set the Rendezvous Time so that the expected reception of a Parent Request happens in
110 // the "free space" between the "n+1"-th and "n+2"-th wake-up frame.
111 rendezvousTimeUs = mIntervalUs;
112 rendezvousTimeUs += (mIntervalUs - (kWakeupFrameLength + kParentRequestLength) * kOctetDuration) / 2;
113 frame->GetRendezvousTimeIe()->SetRendezvousTime(rendezvousTimeUs / kUsPerTenSymbols);
114
115 connectionIe = frame->GetConnectionIe();
116 connectionIe->SetRetryInterval(kConnectionRetryInterval);
117 connectionIe->SetRetryCount(kConnectionRetryCount);
118
119 // Advance to the time of the next wake-up frame.
120 mTxTimeUs = Max(mTxTimeUs + mIntervalUs, TimerMicro::GetNow() + mTxRequestAheadTimeUs);
121
122 // Schedule the next timer right away before waiting for the transmission completion
123 // to keep up with the high rate of wake-up frames in the RCP architecture.
124 ScheduleTimer();
125
126 exit:
127 return frame;
128 }
129
130 #else // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
131
PrepareWakeupFrame(Mac::TxFrames &)132 Mac::TxFrame *WakeupTxScheduler::PrepareWakeupFrame(Mac::TxFrames &) { return nullptr; }
133
134 #endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
135
ScheduleTimer(void)136 void WakeupTxScheduler::ScheduleTimer(void)
137 {
138 if (mTxTimeUs >= mTxEndTimeUs)
139 {
140 mIsRunning = false;
141 LogInfo("Stopped wake-up sequence");
142 ExitNow();
143 }
144
145 mTimer.FireAt(mTxTimeUs - mTxRequestAheadTimeUs);
146
147 exit:
148 return;
149 }
150
Stop(void)151 void WakeupTxScheduler::Stop(void)
152 {
153 mIsRunning = false;
154 mTimer.Stop();
155 }
156
UpdateFrameRequestAhead(void)157 void WakeupTxScheduler::UpdateFrameRequestAhead(void)
158 {
159 // A rough estimate of the size of data that has to be exchanged with the radio to schedule a wake-up frame TX.
160 // This is used to make sure that a wake-up frame is received by the radio early enough to be transmitted on time.
161 constexpr uint32_t kWakeupFrameSize = 100;
162
163 mTxRequestAheadTimeUs = Mac::kCslRequestAhead + Get<Mac::Mac>().CalculateRadioBusTransferTime(kWakeupFrameSize);
164 }
165
166 } // namespace ot
167
168 #endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
169