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