• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.SetChannel(mChannel);
166     dataset.SetDelay(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         LogInfo("Auto-triggered channel select");
220         IgnoreError(RequestAutoChannelSelect(false));
221         StartAutoSelectTimer();
222         break;
223 
224     case kStateChangeRequested:
225 #if OPENTHREAD_FTD
226         StartDatasetUpdate();
227 #endif
228         break;
229 
230     case kStateChangeInProgress:
231         break;
232     }
233 }
234 
235 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
236 
FindBetterChannel(uint8_t & aNewChannel,uint16_t & aOccupancy)237 Error ChannelManager::FindBetterChannel(uint8_t &aNewChannel, uint16_t &aOccupancy)
238 {
239     Error            error = kErrorNone;
240     Mac::ChannelMask favoredAndSupported;
241     Mac::ChannelMask favoredBest;
242     Mac::ChannelMask supportedBest;
243     uint16_t         favoredOccupancy;
244     uint16_t         supportedOccupancy;
245 
246     if (Get<ChannelMonitor>().GetSampleCount() <= kMinChannelMonitorSampleCount)
247     {
248         LogInfo("Too few samples (%lu <= %lu) to select channel", ToUlong(Get<ChannelMonitor>().GetSampleCount()),
249                 ToUlong(kMinChannelMonitorSampleCount));
250         ExitNow(error = kErrorInvalidState);
251     }
252 
253     favoredAndSupported = mFavoredChannelMask;
254     favoredAndSupported.Intersect(mSupportedChannelMask);
255 
256     favoredBest   = Get<ChannelMonitor>().FindBestChannels(favoredAndSupported, favoredOccupancy);
257     supportedBest = Get<ChannelMonitor>().FindBestChannels(mSupportedChannelMask, supportedOccupancy);
258 
259     LogInfo("Best favored %s, occupancy 0x%04x", favoredBest.ToString().AsCString(), favoredOccupancy);
260     LogInfo("Best overall %s, occupancy 0x%04x", supportedBest.ToString().AsCString(), supportedOccupancy);
261 
262     // Prefer favored channels unless there is no favored channel,
263     // or the occupancy rate of the best favored channel is worse
264     // than the best overall by at least `kThresholdToSkipFavored`.
265 
266     if (favoredBest.IsEmpty() || ((favoredOccupancy >= kThresholdToSkipFavored) &&
267                                   (supportedOccupancy < favoredOccupancy - kThresholdToSkipFavored)))
268     {
269         if (!favoredBest.IsEmpty())
270         {
271             LogInfo("Preferring an unfavored channel due to high occupancy rate diff");
272         }
273 
274         favoredBest      = supportedBest;
275         favoredOccupancy = supportedOccupancy;
276     }
277 
278     VerifyOrExit(!favoredBest.IsEmpty(), error = kErrorNotFound);
279 
280     aNewChannel = favoredBest.ChooseRandomChannel();
281     aOccupancy  = favoredOccupancy;
282 
283 exit:
284     return error;
285 }
286 
ShouldAttemptChannelChange(void)287 bool ChannelManager::ShouldAttemptChannelChange(void)
288 {
289     uint16_t ccaFailureRate = Get<Mac::Mac>().GetCcaFailureRate();
290     bool     shouldAttempt  = (ccaFailureRate >= mCcaFailureRateThreshold);
291 
292     LogInfo("CCA-err-rate: 0x%04x %s 0x%04x, selecting channel: %s", ccaFailureRate, shouldAttempt ? ">=" : "<",
293             mCcaFailureRateThreshold, ToYesNo(shouldAttempt));
294 
295     return shouldAttempt;
296 }
297 
298 #if OPENTHREAD_FTD
RequestNetworkChannelSelect(bool aSkipQualityCheck)299 Error ChannelManager::RequestNetworkChannelSelect(bool aSkipQualityCheck)
300 {
301     Error error = kErrorNone;
302 
303     SuccessOrExit(error = RequestChannelSelect(aSkipQualityCheck));
304     RequestNetworkChannelChange(mChannelSelected);
305 
306 exit:
307     if ((error == kErrorAbort) || (error == kErrorAlready))
308     {
309         // ignore aborted channel change
310         error = kErrorNone;
311     }
312     return error;
313 }
314 #endif
315 
316 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
RequestCslChannelSelect(bool aSkipQualityCheck)317 Error ChannelManager::RequestCslChannelSelect(bool aSkipQualityCheck)
318 {
319     Error error = kErrorNone;
320 
321     SuccessOrExit(error = RequestChannelSelect(aSkipQualityCheck));
322     ChangeCslChannel(mChannelSelected);
323 
324 exit:
325     if ((error == kErrorAbort) || (error == kErrorAlready))
326     {
327         // ignore aborted channel change
328         error = kErrorNone;
329     }
330     return error;
331 }
332 #endif
333 
RequestAutoChannelSelect(bool aSkipQualityCheck)334 Error ChannelManager::RequestAutoChannelSelect(bool aSkipQualityCheck)
335 {
336     Error error = kErrorNone;
337 
338     SuccessOrExit(error = RequestChannelSelect(aSkipQualityCheck));
339     RequestChannelChange(mChannelSelected);
340 
341 exit:
342     return error;
343 }
344 
RequestChannelSelect(bool aSkipQualityCheck)345 Error ChannelManager::RequestChannelSelect(bool aSkipQualityCheck)
346 {
347     Error    error = kErrorNone;
348     uint8_t  curChannel, newChannel;
349     uint16_t curOccupancy, newOccupancy;
350 
351     LogInfo("Request to select channel (skip quality check: %s)", ToYesNo(aSkipQualityCheck));
352 
353     VerifyOrExit(!Get<Mle::Mle>().IsDisabled(), error = kErrorInvalidState);
354 
355     VerifyOrExit(aSkipQualityCheck || ShouldAttemptChannelChange(), error = kErrorAbort);
356 
357     SuccessOrExit(error = FindBetterChannel(newChannel, newOccupancy));
358 
359 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
360     if (Get<Mac::Mac>().IsCslEnabled() && (Get<Mac::Mac>().GetCslChannel() != 0))
361     {
362         curChannel = Get<Mac::Mac>().GetCslChannel();
363     }
364     else
365 #endif
366     {
367         curChannel = Get<Mac::Mac>().GetPanChannel();
368     }
369 
370     curOccupancy = Get<ChannelMonitor>().GetChannelOccupancy(curChannel);
371 
372     if (newChannel == curChannel)
373     {
374         LogInfo("Already on best possible channel %d", curChannel);
375         ExitNow(error = kErrorAlready);
376     }
377 
378     LogInfo("Cur channel %d, occupancy 0x%04x - Best channel %d, occupancy 0x%04x", curChannel, curOccupancy,
379             newChannel, newOccupancy);
380 
381     // Switch only if new channel's occupancy rate is better than current
382     // channel's occupancy rate by threshold `kThresholdToChangeChannel`.
383 
384     if ((newOccupancy >= curOccupancy) ||
385         (static_cast<uint16_t>(curOccupancy - newOccupancy) < kThresholdToChangeChannel))
386     {
387         LogInfo("Occupancy rate diff too small to change channel");
388         ExitNow(error = kErrorAbort);
389     }
390 
391     mChannelSelected = newChannel;
392 
393 exit:
394     LogWarnOnError(error, "select better channel");
395     return error;
396 }
397 #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
398 
StartAutoSelectTimer(void)399 void ChannelManager::StartAutoSelectTimer(void)
400 {
401     VerifyOrExit(mState == kStateIdle);
402 
403 #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \
404      OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
405     if (mAutoSelectEnabled || mAutoSelectCslEnabled)
406 #elif OPENTHREAD_FTD
407     if (mAutoSelectEnabled)
408 #elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
409     if (mAutoSelectCslEnabled)
410 #endif
411     {
412         mTimer.Start(Time::SecToMsec(mAutoSelectInterval));
413     }
414     else
415     {
416         mTimer.Stop();
417     }
418 
419 exit:
420     return;
421 }
422 
423 #if OPENTHREAD_FTD
SetAutoNetworkChannelSelectionEnabled(bool aEnabled)424 void ChannelManager::SetAutoNetworkChannelSelectionEnabled(bool aEnabled)
425 {
426     if (aEnabled != mAutoSelectEnabled)
427     {
428         mAutoSelectEnabled = aEnabled;
429         IgnoreError(RequestNetworkChannelSelect(false));
430         StartAutoSelectTimer();
431     }
432 }
433 #endif
434 
435 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
SetAutoCslChannelSelectionEnabled(bool aEnabled)436 void ChannelManager::SetAutoCslChannelSelectionEnabled(bool aEnabled)
437 {
438     if (aEnabled != mAutoSelectCslEnabled)
439     {
440         mAutoSelectCslEnabled = aEnabled;
441         IgnoreError(RequestAutoChannelSelect(false));
442         StartAutoSelectTimer();
443     }
444 }
445 #endif
446 
SetAutoChannelSelectionInterval(uint32_t aInterval)447 Error ChannelManager::SetAutoChannelSelectionInterval(uint32_t aInterval)
448 {
449     Error    error        = kErrorNone;
450     uint32_t prevInterval = mAutoSelectInterval;
451 
452     VerifyOrExit((aInterval != 0) && (aInterval <= Time::MsecToSec(Timer::kMaxDelay)), error = kErrorInvalidArgs);
453 
454     mAutoSelectInterval = aInterval;
455 
456 #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \
457      OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
458     if (mAutoSelectEnabled || mAutoSelectCslEnabled)
459 #elif OPENTHREAD_FTD
460     if (mAutoSelectEnabled)
461 #elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
462     if (mAutoSelectCslEnabled)
463 #endif
464     {
465         if ((mState == kStateIdle) && mTimer.IsRunning() && (prevInterval != aInterval))
466         {
467             mTimer.StartAt(mTimer.GetFireTime() - Time::SecToMsec(prevInterval), Time::SecToMsec(aInterval));
468         }
469     }
470 
471 exit:
472     return error;
473 }
474 
SetSupportedChannels(uint32_t aChannelMask)475 void ChannelManager::SetSupportedChannels(uint32_t aChannelMask)
476 {
477     mSupportedChannelMask.SetMask(aChannelMask & Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
478 
479     LogInfo("Supported channels: %s", mSupportedChannelMask.ToString().AsCString());
480 }
481 
SetFavoredChannels(uint32_t aChannelMask)482 void ChannelManager::SetFavoredChannels(uint32_t aChannelMask)
483 {
484     mFavoredChannelMask.SetMask(aChannelMask & Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
485 
486     LogInfo("Favored channels: %s", mFavoredChannelMask.ToString().AsCString());
487 }
488 
SetCcaFailureRateThreshold(uint16_t aThreshold)489 void ChannelManager::SetCcaFailureRateThreshold(uint16_t aThreshold)
490 {
491     mCcaFailureRateThreshold = aThreshold;
492 
493     LogInfo("CCA threshold: 0x%04x", mCcaFailureRateThreshold);
494 }
495 
496 } // namespace Utils
497 } // namespace ot
498 
499 #endif // #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE
500