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