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