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