1 /*
2  *  Copyright (c) 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 Channel Manager.
32  *
33  */
34 
35 #include "channel_manager.hpp"
36 
37 #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
38     (OPENTHREAD_FTD ||                          \
39      (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
40 
41 #include "common/code_utils.hpp"
42 #include "common/locator_getters.hpp"
43 #include "common/log.hpp"
44 #include "common/random.hpp"
45 #include "common/string.hpp"
46 #include "instance/instance.hpp"
47 #include "meshcop/dataset_updater.hpp"
48 #include "radio/radio.hpp"
49 
50 namespace ot {
51 namespace Utils {
52 
53 RegisterLogModule("ChannelManager");
54 
ChannelManager(Instance & aInstance)55 ChannelManager::ChannelManager(Instance &aInstance)
56     : InstanceLocator(aInstance)
57     , mSupportedChannelMask(0)
58     , mFavoredChannelMask(0)
59 #if OPENTHREAD_FTD
60     , mDelay(kMinimumDelay)
61 #endif
62     , mChannel(0)
63     , mChannelSelected(0)
64     , mState(kStateIdle)
65     , mTimer(aInstance)
66     , mAutoSelectInterval(kDefaultAutoSelectInterval)
67 #if OPENTHREAD_FTD
68     , mAutoSelectEnabled(false)
69 #endif
70 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
71     , mAutoSelectCslEnabled(false)
72 #endif
73     , mCcaFailureRateThreshold(kCcaFailureRateThreshold)
74 {
75 }
76 
RequestChannelChange(uint8_t aChannel)77 void ChannelManager::RequestChannelChange(uint8_t aChannel)
78 {
79 #if OPENTHREAD_FTD
80     if (Get<Mle::Mle>().IsFullThreadDevice() && Get<Mle::Mle>().IsRxOnWhenIdle() && mAutoSelectEnabled)
81     {
82         RequestNetworkChannelChange(aChannel);
83     }
84 #endif
85 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
86     if (mAutoSelectCslEnabled)
87     {
88         ChangeCslChannel(aChannel);
89     }
90 #endif
91 }
92 
93 #if OPENTHREAD_FTD
RequestNetworkChannelChange(uint8_t aChannel)94 void ChannelManager::RequestNetworkChannelChange(uint8_t aChannel)
95 {
96     // Check requested channel != current channel
97     if (aChannel == Get<Mac::Mac>().GetPanChannel())
98     {
99         LogInfo("Already operating on the requested channel %d", aChannel);
100         ExitNow();
101     }
102 
103     LogInfo("Request to change to channel %d with delay %d sec", aChannel, mDelay);
104     if (mState == kStateChangeInProgress)
105     {
106         VerifyOrExit(mChannel != aChannel);
107     }
108 
109     mState   = kStateChangeRequested;
110     mChannel = aChannel;
111 
112     mTimer.Start(1 + Random::NonCrypto::GetUint32InRange(0, kRequestStartJitterInterval));
113 
114     Get<Notifier>().Signal(kEventChannelManagerNewChannelChanged);
115 
116 exit:
117     return;
118 }
119 #endif
120 
121 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
ChangeCslChannel(uint8_t aChannel)122 void ChannelManager::ChangeCslChannel(uint8_t aChannel)
123 {
124     if (!(!Get<Mle::Mle>().IsRxOnWhenIdle() && Get<Mac::Mac>().IsCslEnabled()))
125     {
126         // cannot select or use other channel
127         ExitNow();
128     }
129 
130     if (aChannel == Get<Mac::Mac>().GetCslChannel())
131     {
132         LogInfo("Already operating on the requested channel %d", aChannel);
133         ExitNow();
134     }
135 
136     VerifyOrExit(Radio::IsCslChannelValid(aChannel));
137 
138     LogInfo("Change to Csl channel %d now.", aChannel);
139 
140     mChannel = aChannel;
141     Get<Mac::Mac>().SetCslChannel(aChannel);
142 
143 exit:
144     return;
145 }
146 #endif // (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
147 
148 #if OPENTHREAD_FTD
SetDelay(uint16_t aDelay)149 Error ChannelManager::SetDelay(uint16_t aDelay)
150 {
151     Error error = kErrorNone;
152 
153     VerifyOrExit(aDelay >= kMinimumDelay, error = kErrorInvalidArgs);
154     mDelay = aDelay;
155 
156 exit:
157     return error;
158 }
159 
StartDatasetUpdate(void)160 void ChannelManager::StartDatasetUpdate(void)
161 {
162     MeshCoP::Dataset::Info dataset;
163 
164     dataset.Clear();
165     dataset.Set<MeshCoP::Dataset::kChannel>(mChannel);
166     dataset.Set<MeshCoP::Dataset::kDelay>(Time::SecToMsec(mDelay));
167 
168     switch (Get<MeshCoP::DatasetUpdater>().RequestUpdate(dataset, HandleDatasetUpdateDone, this))
169     {
170     case kErrorNone:
171         mState = kStateChangeInProgress;
172         // Wait for the `HandleDatasetUpdateDone()` callback.
173         break;
174 
175     case kErrorBusy:
176     case kErrorNoBufs:
177         mTimer.Start(kPendingDatasetTxRetryInterval);
178         break;
179 
180     case kErrorInvalidState:
181         LogInfo("Request to change to channel %d failed. Device is disabled", mChannel);
182 
183         OT_FALL_THROUGH;
184 
185     default:
186         mState = kStateIdle;
187         StartAutoSelectTimer();
188         break;
189     }
190 }
191 
HandleDatasetUpdateDone(Error aError,void * aContext)192 void ChannelManager::HandleDatasetUpdateDone(Error aError, void *aContext)
193 {
194     static_cast<ChannelManager *>(aContext)->HandleDatasetUpdateDone(aError);
195 }
196 
HandleDatasetUpdateDone(Error aError)197 void ChannelManager::HandleDatasetUpdateDone(Error aError)
198 {
199     if (aError == kErrorNone)
200     {
201         LogInfo("Channel changed to %d", mChannel);
202     }
203     else
204     {
205         LogInfo("Canceling channel change to %d%s", mChannel,
206                 (aError == kErrorAlready) ? " since current ActiveDataset is more recent" : "");
207     }
208 
209     mState = kStateIdle;
210     StartAutoSelectTimer();
211 }
212 #endif // OPENTHREAD_FTD
213 
HandleTimer(void)214 void ChannelManager::HandleTimer(void)
215 {
216     switch (mState)
217     {
218     case kStateIdle:
219 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
220         LogInfo("Auto-triggered channel select");
221         IgnoreError(RequestAutoChannelSelect(false));
222 #endif
223         StartAutoSelectTimer();
224         break;
225 
226     case kStateChangeRequested:
227 #if OPENTHREAD_FTD
228         StartDatasetUpdate();
229 #endif
230         break;
231 
232     case kStateChangeInProgress:
233         break;
234     }
235 }
236 
237 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
238 
FindBetterChannel(uint8_t & aNewChannel,uint16_t & aOccupancy)239 Error ChannelManager::FindBetterChannel(uint8_t &aNewChannel, uint16_t &aOccupancy)
240 {
241     Error            error = kErrorNone;
242     Mac::ChannelMask favoredAndSupported;
243     Mac::ChannelMask favoredBest;
244     Mac::ChannelMask supportedBest;
245     uint16_t         favoredOccupancy;
246     uint16_t         supportedOccupancy;
247 
248     if (Get<ChannelMonitor>().GetSampleCount() <= kMinChannelMonitorSampleCount)
249     {
250         LogInfo("Too few samples (%lu <= %lu) to select channel", ToUlong(Get<ChannelMonitor>().GetSampleCount()),
251                 ToUlong(kMinChannelMonitorSampleCount));
252         ExitNow(error = kErrorInvalidState);
253     }
254 
255     favoredAndSupported = mFavoredChannelMask;
256     favoredAndSupported.Intersect(mSupportedChannelMask);
257 
258     favoredBest   = Get<ChannelMonitor>().FindBestChannels(favoredAndSupported, favoredOccupancy);
259     supportedBest = Get<ChannelMonitor>().FindBestChannels(mSupportedChannelMask, supportedOccupancy);
260 
261     LogInfo("Best favored %s, occupancy 0x%04x", favoredBest.ToString().AsCString(), favoredOccupancy);
262     LogInfo("Best overall %s, occupancy 0x%04x", supportedBest.ToString().AsCString(), supportedOccupancy);
263 
264     // Prefer favored channels unless there is no favored channel,
265     // or the occupancy rate of the best favored channel is worse
266     // than the best overall by at least `kThresholdToSkipFavored`.
267 
268     if (favoredBest.IsEmpty() || ((favoredOccupancy >= kThresholdToSkipFavored) &&
269                                   (supportedOccupancy < favoredOccupancy - kThresholdToSkipFavored)))
270     {
271         if (!favoredBest.IsEmpty())
272         {
273             LogInfo("Preferring an unfavored channel due to high occupancy rate diff");
274         }
275 
276         favoredBest      = supportedBest;
277         favoredOccupancy = supportedOccupancy;
278     }
279 
280     VerifyOrExit(!favoredBest.IsEmpty(), error = kErrorNotFound);
281 
282     aNewChannel = favoredBest.ChooseRandomChannel();
283     aOccupancy  = favoredOccupancy;
284 
285 exit:
286     return error;
287 }
288 
ShouldAttemptChannelChange(void)289 bool ChannelManager::ShouldAttemptChannelChange(void)
290 {
291     uint16_t ccaFailureRate = Get<Mac::Mac>().GetCcaFailureRate();
292     bool     shouldAttempt  = (ccaFailureRate >= mCcaFailureRateThreshold);
293 
294     LogInfo("CCA-err-rate: 0x%04x %s 0x%04x, selecting channel: %s", ccaFailureRate, shouldAttempt ? ">=" : "<",
295             mCcaFailureRateThreshold, ToYesNo(shouldAttempt));
296 
297     return shouldAttempt;
298 }
299 
300 #if OPENTHREAD_FTD
RequestNetworkChannelSelect(bool aSkipQualityCheck)301 Error ChannelManager::RequestNetworkChannelSelect(bool aSkipQualityCheck)
302 {
303     Error error = kErrorNone;
304 
305     SuccessOrExit(error = RequestChannelSelect(aSkipQualityCheck));
306     RequestNetworkChannelChange(mChannelSelected);
307 
308 exit:
309     if ((error == kErrorAbort) || (error == kErrorAlready))
310     {
311         // ignore aborted channel change
312         error = kErrorNone;
313     }
314     return error;
315 }
316 #endif
317 
318 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
RequestCslChannelSelect(bool aSkipQualityCheck)319 Error ChannelManager::RequestCslChannelSelect(bool aSkipQualityCheck)
320 {
321     Error error = kErrorNone;
322 
323     SuccessOrExit(error = RequestChannelSelect(aSkipQualityCheck));
324     ChangeCslChannel(mChannelSelected);
325 
326 exit:
327     if ((error == kErrorAbort) || (error == kErrorAlready))
328     {
329         // ignore aborted channel change
330         error = kErrorNone;
331     }
332     return error;
333 }
334 #endif
335 
RequestAutoChannelSelect(bool aSkipQualityCheck)336 Error ChannelManager::RequestAutoChannelSelect(bool aSkipQualityCheck)
337 {
338     Error error = kErrorNone;
339 
340     SuccessOrExit(error = RequestChannelSelect(aSkipQualityCheck));
341     RequestChannelChange(mChannelSelected);
342 
343 exit:
344     return error;
345 }
346 
RequestChannelSelect(bool aSkipQualityCheck)347 Error ChannelManager::RequestChannelSelect(bool aSkipQualityCheck)
348 {
349     Error    error = kErrorNone;
350     uint8_t  curChannel, newChannel;
351     uint16_t curOccupancy, newOccupancy;
352 
353     LogInfo("Request to select channel (skip quality check: %s)", ToYesNo(aSkipQualityCheck));
354 
355     VerifyOrExit(!Get<Mle::Mle>().IsDisabled(), error = kErrorInvalidState);
356 
357     VerifyOrExit(aSkipQualityCheck || ShouldAttemptChannelChange(), error = kErrorAbort);
358 
359     SuccessOrExit(error = FindBetterChannel(newChannel, newOccupancy));
360 
361 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
362     if (Get<Mac::Mac>().IsCslEnabled() && (Get<Mac::Mac>().GetCslChannel() != 0))
363     {
364         curChannel = Get<Mac::Mac>().GetCslChannel();
365     }
366     else
367 #endif
368     {
369         curChannel = Get<Mac::Mac>().GetPanChannel();
370     }
371 
372     curOccupancy = Get<ChannelMonitor>().GetChannelOccupancy(curChannel);
373 
374     if (newChannel == curChannel)
375     {
376         LogInfo("Already on best possible channel %d", curChannel);
377         ExitNow(error = kErrorAlready);
378     }
379 
380     LogInfo("Cur channel %d, occupancy 0x%04x - Best channel %d, occupancy 0x%04x", curChannel, curOccupancy,
381             newChannel, newOccupancy);
382 
383     // Switch only if new channel's occupancy rate is better than current
384     // channel's occupancy rate by threshold `kThresholdToChangeChannel`.
385 
386     if ((newOccupancy >= curOccupancy) ||
387         (static_cast<uint16_t>(curOccupancy - newOccupancy) < kThresholdToChangeChannel))
388     {
389         LogInfo("Occupancy rate diff too small to change channel");
390         ExitNow(error = kErrorAbort);
391     }
392 
393     mChannelSelected = newChannel;
394 
395 exit:
396     LogWarnOnError(error, "select better channel");
397     return error;
398 }
399 #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
400 
StartAutoSelectTimer(void)401 void ChannelManager::StartAutoSelectTimer(void)
402 {
403     VerifyOrExit(mState == kStateIdle);
404 
405 #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \
406      OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
407     if (mAutoSelectEnabled || mAutoSelectCslEnabled)
408 #elif OPENTHREAD_FTD
409     if (mAutoSelectEnabled)
410 #elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
411     if (mAutoSelectCslEnabled)
412 #endif
413     {
414         mTimer.Start(Time::SecToMsec(mAutoSelectInterval));
415     }
416     else
417     {
418         mTimer.Stop();
419     }
420 
421 exit:
422     return;
423 }
424 
425 #if OPENTHREAD_FTD
SetAutoNetworkChannelSelectionEnabled(bool aEnabled)426 void ChannelManager::SetAutoNetworkChannelSelectionEnabled(bool aEnabled)
427 {
428 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
429     if (aEnabled != mAutoSelectEnabled)
430     {
431         mAutoSelectEnabled = aEnabled;
432         IgnoreError(RequestNetworkChannelSelect(false));
433         StartAutoSelectTimer();
434     }
435 #endif
436 }
437 #endif
438 
439 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
SetAutoCslChannelSelectionEnabled(bool aEnabled)440 void ChannelManager::SetAutoCslChannelSelectionEnabled(bool aEnabled)
441 {
442     if (aEnabled != mAutoSelectCslEnabled)
443     {
444         mAutoSelectCslEnabled = aEnabled;
445         IgnoreError(RequestAutoChannelSelect(false));
446         StartAutoSelectTimer();
447     }
448 }
449 #endif
450 
SetAutoChannelSelectionInterval(uint32_t aInterval)451 Error ChannelManager::SetAutoChannelSelectionInterval(uint32_t aInterval)
452 {
453     Error    error        = kErrorNone;
454     uint32_t prevInterval = mAutoSelectInterval;
455 
456     VerifyOrExit((aInterval != 0) && (aInterval <= Time::MsecToSec(Timer::kMaxDelay)), error = kErrorInvalidArgs);
457 
458     mAutoSelectInterval = aInterval;
459 
460 #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \
461      OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
462     if (mAutoSelectEnabled || mAutoSelectCslEnabled)
463 #elif OPENTHREAD_FTD
464     if (mAutoSelectEnabled)
465 #elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
466     if (mAutoSelectCslEnabled)
467 #endif
468     {
469         if ((mState == kStateIdle) && mTimer.IsRunning() && (prevInterval != aInterval))
470         {
471             mTimer.StartAt(mTimer.GetFireTime() - Time::SecToMsec(prevInterval), Time::SecToMsec(aInterval));
472         }
473     }
474 
475 exit:
476     return error;
477 }
478 
SetSupportedChannels(uint32_t aChannelMask)479 void ChannelManager::SetSupportedChannels(uint32_t aChannelMask)
480 {
481     mSupportedChannelMask.SetMask(aChannelMask & Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
482 
483     LogInfo("Supported channels: %s", mSupportedChannelMask.ToString().AsCString());
484 }
485 
SetFavoredChannels(uint32_t aChannelMask)486 void ChannelManager::SetFavoredChannels(uint32_t aChannelMask)
487 {
488     mFavoredChannelMask.SetMask(aChannelMask & Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
489 
490     LogInfo("Favored channels: %s", mFavoredChannelMask.ToString().AsCString());
491 }
492 
SetCcaFailureRateThreshold(uint16_t aThreshold)493 void ChannelManager::SetCcaFailureRateThreshold(uint16_t aThreshold)
494 {
495     mCcaFailureRateThreshold = aThreshold;
496 
497     LogInfo("CCA threshold: 0x%04x", mCcaFailureRateThreshold);
498 }
499 
500 } // namespace Utils
501 } // namespace ot
502 
503 #endif // #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE
504