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