1 /*
2 * Copyright (c) 2016-2018, 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 /**
30 * @file
31 * This file implements the AnnounceSender.
32 */
33
34 #include "announce_sender.hpp"
35
36 #include <openthread/platform/radio.h>
37
38 #include "common/code_utils.hpp"
39 #include "common/instance.hpp"
40 #include "common/locator_getters.hpp"
41 #include "common/logging.hpp"
42 #include "common/random.hpp"
43 #include "meshcop/meshcop.hpp"
44 #include "meshcop/meshcop_tlvs.hpp"
45 #include "radio/radio.hpp"
46
47 namespace ot {
48
49 //---------------------------------------------------------------------------------------------------------------------
50 // AnnounceSenderBase
51
AnnounceSenderBase(Instance & aInstance,Timer::Handler aHandler)52 AnnounceSenderBase::AnnounceSenderBase(Instance &aInstance, Timer::Handler aHandler)
53 : InstanceLocator(aInstance)
54 , mPeriod(0)
55 , mJitter(0)
56 , mCount(0)
57 , mChannel(0)
58 , mStartingChannel(kChannelIteratorFirst)
59 , mTimer(aInstance, aHandler)
60 {
61 }
62
SendAnnounce(uint8_t aCount)63 void AnnounceSenderBase::SendAnnounce(uint8_t aCount)
64 {
65 if (IsRunning())
66 {
67 mCount += aCount;
68 ExitNow();
69 }
70
71 VerifyOrExit((mPeriod != 0) && !mChannelMask.IsEmpty());
72
73 SelectStartingChannel();
74
75 mCount = aCount;
76 mChannel = mStartingChannel;
77
78 mTimer.Start(Random::NonCrypto::GetUint32InRange(0, mJitter + 1));
79
80 exit:
81 return;
82 }
83
Stop(void)84 void AnnounceSenderBase::Stop(void)
85 {
86 mTimer.Stop();
87 mCount = 0;
88 }
89
SetChannelMask(Mac::ChannelMask aChannelMask)90 void AnnounceSenderBase::SetChannelMask(Mac::ChannelMask aChannelMask)
91 {
92 mChannelMask = aChannelMask;
93 mChannelMask.Intersect(Get<Mac::Mac>().GetSupportedChannelMask());
94
95 VerifyOrExit(!mChannelMask.IsEmpty(), Stop());
96 SelectStartingChannel();
97
98 exit:
99 return;
100 }
101
SetStartingChannel(uint8_t aStartingChannel)102 void AnnounceSenderBase::SetStartingChannel(uint8_t aStartingChannel)
103 {
104 mStartingChannel = aStartingChannel;
105 SelectStartingChannel();
106 }
107
SelectStartingChannel(void)108 void AnnounceSenderBase::SelectStartingChannel(void)
109 {
110 // If the starting channel is not set or it is not present
111 // in the channel mask, then start from the first channel
112 // in the mask.
113
114 VerifyOrExit(!mChannelMask.IsEmpty());
115 VerifyOrExit((mStartingChannel == kChannelIteratorFirst) || !mChannelMask.ContainsChannel(mStartingChannel));
116
117 mStartingChannel = kChannelIteratorFirst;
118 IgnoreError(mChannelMask.GetNextChannel(mStartingChannel));
119
120 exit:
121 return;
122 }
123
HandleTimer(void)124 void AnnounceSenderBase::HandleTimer(void)
125 {
126 Get<Mle::MleRouter>().SendAnnounce(mChannel, false);
127
128 // Go to the next channel in the mask. If we have reached the end
129 // of the channel mask, we start over from the first channel in
130 // the mask. Once we get back to `mStartingChannel` we have
131 // finished one full cycle and can decrement `mCount`.
132
133 while (mChannelMask.GetNextChannel(mChannel) != kErrorNone)
134 {
135 mChannel = kChannelIteratorFirst;
136 }
137
138 if ((mChannel == mStartingChannel) && (mCount != 0))
139 {
140 mCount--;
141 VerifyOrExit(mCount != 0);
142 }
143
144 mTimer.Start(Random::NonCrypto::AddJitter(mPeriod, mJitter));
145
146 exit:
147 return;
148 }
149
150 //---------------------------------------------------------------------------------------------------------------------
151 // AnnounceSender
152
153 #if OPENTHREAD_CONFIG_ANNOUNCE_SENDER_ENABLE
154
AnnounceSender(Instance & aInstance)155 AnnounceSender::AnnounceSender(Instance &aInstance)
156 : AnnounceSenderBase(aInstance, AnnounceSender::HandleTimer)
157 , mTrickleTimer(aInstance, AnnounceSender::HandleTrickleTimer)
158 {
159 SetJitter(kMaxJitter);
160 }
161
UpdateOnReceivedAnnounce(void)162 void AnnounceSender::UpdateOnReceivedAnnounce(void)
163 {
164 mTrickleTimer.IndicateConsistent();
165 }
166
Stop(void)167 void AnnounceSender::Stop(void)
168 {
169 AnnounceSenderBase::Stop();
170 mTrickleTimer.Stop();
171 otLogInfoMle("[announce-sender] Stopped");
172 }
173
HandleTimer(Timer & aTimer)174 void AnnounceSender::HandleTimer(Timer &aTimer)
175 {
176 aTimer.Get<AnnounceSender>().AnnounceSenderBase::HandleTimer();
177 }
178
HandleTrickleTimer(TrickleTimer & aTimer)179 void AnnounceSender::HandleTrickleTimer(TrickleTimer &aTimer)
180 {
181 aTimer.Get<AnnounceSender>().HandleTrickleTimer();
182 }
183
HandleTrickleTimer(void)184 void AnnounceSender::HandleTrickleTimer(void)
185 {
186 // The trickle timer handler is called when
187 // we do not receive enough Announce messages
188 // within the current interval and therefore
189 // the device itself needs to send Announce.
190 // We then request one more cycle of Announce
191 // message transmissions.
192
193 SendAnnounce(1);
194 otLogInfoMle("[announce-sender] Schedule tx for one cycle");
195 }
196
HandleNotifierEvents(Events aEvents)197 void AnnounceSender::HandleNotifierEvents(Events aEvents)
198 {
199 if (aEvents.Contains(kEventThreadRoleChanged))
200 {
201 HandleRoleChanged();
202 }
203
204 if (aEvents.Contains(kEventActiveDatasetChanged))
205 {
206 HandleActiveDatasetChanged();
207 }
208
209 if (aEvents.Contains(kEventThreadChannelChanged))
210 {
211 HandleThreadChannelChanged();
212 }
213 }
214
HandleRoleChanged(void)215 void AnnounceSender::HandleRoleChanged(void)
216 {
217 switch (Get<Mle::Mle>().GetRole())
218 {
219 case Mle::kRoleLeader:
220 case Mle::kRoleRouter:
221 break;
222
223 case Mle::kRoleChild:
224 #if OPENTHREAD_FTD
225 if (Get<Mle::MleRouter>().IsRouterEligible() && Get<Mle::Mle>().IsRxOnWhenIdle())
226 {
227 break;
228 }
229 #endif
230
231 OT_FALL_THROUGH;
232
233 case Mle::kRoleDisabled:
234 case Mle::kRoleDetached:
235 Stop();
236 ExitNow();
237 }
238
239 // Start the trickle timer with same min and max interval as the
240 // desired Announce Tx cycle interval.
241
242 mTrickleTimer.Start(TrickleTimer::kModeTrickle, kInterval, kInterval, kRedundancyConstant);
243 otLogInfoMle("[announce-sender] Started");
244
245 exit:
246 return;
247 }
248
HandleActiveDatasetChanged(void)249 void AnnounceSender::HandleActiveDatasetChanged(void)
250 {
251 Mac::ChannelMask channelMask;
252
253 SuccessOrExit(Get<MeshCoP::ActiveDataset>().GetChannelMask(channelMask));
254 VerifyOrExit(!channelMask.IsEmpty());
255
256 VerifyOrExit(channelMask != GetChannelMask());
257
258 SetChannelMask(channelMask);
259 SetPeriod(kTxInterval / channelMask.GetNumberOfChannels());
260 otLogInfoMle("[announce-sender] ChannelMask:%s, period:%u", GetChannelMask().ToString().AsCString(), GetPeriod());
261
262 // When channel mask is changed, we also check and update the PAN
263 // channel. This handles the case where `ThreadChannelChanged` event
264 // may be received and processed before `ActiveDatasetChanged`
265 // event.
266
267 HandleThreadChannelChanged();
268
269 exit:
270 return;
271 }
272
HandleThreadChannelChanged(void)273 void AnnounceSender::HandleThreadChannelChanged(void)
274 {
275 SetStartingChannel(Get<Mac::Mac>().GetPanChannel());
276 otLogInfoMle("[announce-sender] StartingChannel:%d", GetStartingChannel());
277 }
278
279 #endif // OPENTHREAD_CONFIG_ANNOUNCE_SENDER_ENABLE
280
281 } // namespace ot
282