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