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