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 && OPENTHREAD_FTD
38
39 #include "common/code_utils.hpp"
40 #include "common/instance.hpp"
41 #include "common/locator_getters.hpp"
42 #include "common/logging.hpp"
43 #include "common/random.hpp"
44 #include "meshcop/dataset_updater.hpp"
45 #include "radio/radio.hpp"
46
47 namespace ot {
48 namespace Utils {
49
ChannelManager(Instance & aInstance)50 ChannelManager::ChannelManager(Instance &aInstance)
51 : InstanceLocator(aInstance)
52 , mSupportedChannelMask(0)
53 , mFavoredChannelMask(0)
54 , mDelay(kMinimumDelay)
55 , mChannel(0)
56 , mState(kStateIdle)
57 , mTimer(aInstance, ChannelManager::HandleTimer)
58 , mAutoSelectInterval(kDefaultAutoSelectInterval)
59 , mAutoSelectEnabled(false)
60 , mCcaFailureRateThreshold(kCcaFailureRateThreshold)
61 {
62 }
63
RequestChannelChange(uint8_t aChannel)64 void ChannelManager::RequestChannelChange(uint8_t aChannel)
65 {
66 otLogInfoUtil("ChannelManager: Request to change to channel %d with delay %d sec", aChannel, mDelay);
67
68 if (aChannel == Get<Mac::Mac>().GetPanChannel())
69 {
70 otLogInfoUtil("ChannelManager: Already operating on the requested channel %d", aChannel);
71 ExitNow();
72 }
73
74 if (mState == kStateChangeInProgress)
75 {
76 VerifyOrExit(mChannel != aChannel);
77 }
78
79 mState = kStateChangeRequested;
80 mChannel = aChannel;
81
82 mTimer.Start(1 + Random::NonCrypto::GetUint32InRange(0, kRequestStartJitterInterval));
83
84 Get<Notifier>().Signal(kEventChannelManagerNewChannelChanged);
85
86 exit:
87 return;
88 }
89
SetDelay(uint16_t aDelay)90 Error ChannelManager::SetDelay(uint16_t aDelay)
91 {
92 Error error = kErrorNone;
93
94 VerifyOrExit(aDelay >= kMinimumDelay, error = kErrorInvalidArgs);
95 mDelay = aDelay;
96
97 exit:
98 return error;
99 }
100
StartDatasetUpdate(void)101 void ChannelManager::StartDatasetUpdate(void)
102 {
103 MeshCoP::Dataset::Info dataset;
104
105 dataset.Clear();
106 dataset.SetChannel(mChannel);
107 dataset.SetDelay(Time::SecToMsec(mDelay));
108
109 switch (Get<MeshCoP::DatasetUpdater>().RequestUpdate(dataset, HandleDatasetUpdateDone, this))
110 {
111 case kErrorNone:
112 mState = kStateChangeInProgress;
113 // Wait for the `HandleDatasetUpdateDone()` callback.
114 break;
115
116 case kErrorBusy:
117 case kErrorNoBufs:
118 mTimer.Start(kPendingDatasetTxRetryInterval);
119 break;
120
121 case kErrorInvalidState:
122 otLogInfoUtil("ChannelManager: Request to change to channel %d failed. Device is disabled", mChannel);
123
124 OT_FALL_THROUGH;
125
126 default:
127 mState = kStateIdle;
128 StartAutoSelectTimer();
129 break;
130 }
131 }
132
HandleDatasetUpdateDone(Error aError,void * aContext)133 void ChannelManager::HandleDatasetUpdateDone(Error aError, void *aContext)
134 {
135 static_cast<ChannelManager *>(aContext)->HandleDatasetUpdateDone(aError);
136 }
137
HandleDatasetUpdateDone(Error aError)138 void ChannelManager::HandleDatasetUpdateDone(Error aError)
139 {
140 if (aError == kErrorNone)
141 {
142 otLogInfoUtil("ChannelManager: Channel changed to %d", mChannel);
143 }
144 else
145 {
146 otLogInfoUtil("ChannelManager: Canceling channel change to %d%s", mChannel,
147 (aError == kErrorAlready) ? " since current ActiveDataset is more recent" : "");
148 }
149
150 mState = kStateIdle;
151 StartAutoSelectTimer();
152 }
153
HandleTimer(Timer & aTimer)154 void ChannelManager::HandleTimer(Timer &aTimer)
155 {
156 aTimer.Get<ChannelManager>().HandleTimer();
157 }
158
HandleTimer(void)159 void ChannelManager::HandleTimer(void)
160 {
161 switch (mState)
162 {
163 case kStateIdle:
164 otLogInfoUtil("ChannelManager: Auto-triggered channel select");
165 IgnoreError(RequestChannelSelect(false));
166 StartAutoSelectTimer();
167 break;
168
169 case kStateChangeRequested:
170 StartDatasetUpdate();
171 break;
172
173 case kStateChangeInProgress:
174 break;
175 }
176 }
177
178 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
179
FindBetterChannel(uint8_t & aNewChannel,uint16_t & aOccupancy)180 Error ChannelManager::FindBetterChannel(uint8_t &aNewChannel, uint16_t &aOccupancy)
181 {
182 Error error = kErrorNone;
183 Mac::ChannelMask favoredAndSupported;
184 Mac::ChannelMask favoredBest;
185 Mac::ChannelMask supportedBest;
186 uint16_t favoredOccupancy;
187 uint16_t supportedOccupancy;
188
189 if (Get<ChannelMonitor>().GetSampleCount() <= kMinChannelMonitorSampleCount)
190 {
191 otLogInfoUtil("ChannelManager: Too few samples (%d <= %d) to select channel",
192 Get<ChannelMonitor>().GetSampleCount(), kMinChannelMonitorSampleCount);
193 ExitNow(error = kErrorInvalidState);
194 }
195
196 favoredAndSupported = mFavoredChannelMask;
197 favoredAndSupported.Intersect(mSupportedChannelMask);
198
199 favoredBest = Get<ChannelMonitor>().FindBestChannels(favoredAndSupported, favoredOccupancy);
200 supportedBest = Get<ChannelMonitor>().FindBestChannels(mSupportedChannelMask, supportedOccupancy);
201
202 otLogInfoUtil("ChannelManager: Best favored %s, occupancy 0x%04x", favoredBest.ToString().AsCString(),
203 favoredOccupancy);
204 otLogInfoUtil("ChannelManager: Best overall %s, occupancy 0x%04x", supportedBest.ToString().AsCString(),
205 supportedOccupancy);
206
207 // Prefer favored channels unless there is no favored channel,
208 // or the occupancy rate of the best favored channel is worse
209 // than the best overall by at least `kThresholdToSkipFavored`.
210
211 if (favoredBest.IsEmpty() || ((favoredOccupancy >= kThresholdToSkipFavored) &&
212 (supportedOccupancy < favoredOccupancy - kThresholdToSkipFavored)))
213 {
214 if (!favoredBest.IsEmpty())
215 {
216 otLogInfoUtil("ChannelManager: Preferring an unfavored channel due to high occupancy rate diff");
217 }
218
219 favoredBest = supportedBest;
220 favoredOccupancy = supportedOccupancy;
221 }
222
223 VerifyOrExit(!favoredBest.IsEmpty(), error = kErrorNotFound);
224
225 aNewChannel = favoredBest.ChooseRandomChannel();
226 aOccupancy = favoredOccupancy;
227
228 exit:
229 return error;
230 }
231
ShouldAttemptChannelChange(void)232 bool ChannelManager::ShouldAttemptChannelChange(void)
233 {
234 uint16_t ccaFailureRate = Get<Mac::Mac>().GetCcaFailureRate();
235 bool shouldAttempt = (ccaFailureRate >= mCcaFailureRateThreshold);
236
237 otLogInfoUtil("ChannelManager: CCA-err-rate: 0x%04x %s 0x%04x, selecting channel: %s", ccaFailureRate,
238 shouldAttempt ? ">=" : "<", mCcaFailureRateThreshold, shouldAttempt ? "yes" : "no");
239
240 return shouldAttempt;
241 }
242
RequestChannelSelect(bool aSkipQualityCheck)243 Error ChannelManager::RequestChannelSelect(bool aSkipQualityCheck)
244 {
245 Error error = kErrorNone;
246 uint8_t curChannel, newChannel;
247 uint16_t curOccupancy, newOccupancy;
248
249 otLogInfoUtil("ChannelManager: Request to select channel (skip quality check: %s)",
250 aSkipQualityCheck ? "yes" : "no");
251
252 VerifyOrExit(!Get<Mle::Mle>().IsDisabled(), error = kErrorInvalidState);
253
254 VerifyOrExit(aSkipQualityCheck || ShouldAttemptChannelChange());
255
256 SuccessOrExit(error = FindBetterChannel(newChannel, newOccupancy));
257
258 curChannel = Get<Mac::Mac>().GetPanChannel();
259 curOccupancy = Get<ChannelMonitor>().GetChannelOccupancy(curChannel);
260
261 if (newChannel == curChannel)
262 {
263 otLogInfoUtil("ChannelManager: Already on best possible channel %d", curChannel);
264 ExitNow();
265 }
266
267 otLogInfoUtil("ChannelManager: Cur channel %d, occupancy 0x%04x - Best channel %d, occupancy 0x%04x", curChannel,
268 curOccupancy, newChannel, newOccupancy);
269
270 // Switch only if new channel's occupancy rate is better than current
271 // channel's occupancy rate by threshold `kThresholdToChangeChannel`.
272
273 if ((newOccupancy >= curOccupancy) ||
274 (static_cast<uint16_t>(curOccupancy - newOccupancy) < kThresholdToChangeChannel))
275 {
276 otLogInfoUtil("ChannelManager: Occupancy rate diff too small to change channel");
277 ExitNow();
278 }
279
280 RequestChannelChange(newChannel);
281
282 exit:
283
284 if (error != kErrorNone)
285 {
286 otLogInfoUtil("ChannelManager: Request to select better channel failed, error: %s", ErrorToString(error));
287 }
288
289 return error;
290 }
291 #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
292
StartAutoSelectTimer(void)293 void ChannelManager::StartAutoSelectTimer(void)
294 {
295 VerifyOrExit(mState == kStateIdle);
296
297 if (mAutoSelectEnabled)
298 {
299 mTimer.Start(Time::SecToMsec(mAutoSelectInterval));
300 }
301 else
302 {
303 mTimer.Stop();
304 }
305
306 exit:
307 return;
308 }
309
SetAutoChannelSelectionEnabled(bool aEnabled)310 void ChannelManager::SetAutoChannelSelectionEnabled(bool aEnabled)
311 {
312 if (aEnabled != mAutoSelectEnabled)
313 {
314 mAutoSelectEnabled = aEnabled;
315 IgnoreError(RequestChannelSelect(false));
316 StartAutoSelectTimer();
317 }
318 }
319
SetAutoChannelSelectionInterval(uint32_t aInterval)320 Error ChannelManager::SetAutoChannelSelectionInterval(uint32_t aInterval)
321 {
322 Error error = kErrorNone;
323 uint32_t prevInterval = mAutoSelectInterval;
324
325 VerifyOrExit((aInterval != 0) && (aInterval <= Time::MsecToSec(Timer::kMaxDelay)), error = kErrorInvalidArgs);
326
327 mAutoSelectInterval = aInterval;
328
329 if (mAutoSelectEnabled && (mState == kStateIdle) && mTimer.IsRunning() && (prevInterval != aInterval))
330 {
331 mTimer.StartAt(mTimer.GetFireTime() - Time::SecToMsec(prevInterval), Time::SecToMsec(aInterval));
332 }
333
334 exit:
335 return error;
336 }
337
SetSupportedChannels(uint32_t aChannelMask)338 void ChannelManager::SetSupportedChannels(uint32_t aChannelMask)
339 {
340 mSupportedChannelMask.SetMask(aChannelMask & Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
341
342 otLogInfoUtil("ChannelManager: Supported channels: %s", mSupportedChannelMask.ToString().AsCString());
343 }
344
SetFavoredChannels(uint32_t aChannelMask)345 void ChannelManager::SetFavoredChannels(uint32_t aChannelMask)
346 {
347 mFavoredChannelMask.SetMask(aChannelMask & Get<Mac::Mac>().GetSupportedChannelMask().GetMask());
348
349 otLogInfoUtil("ChannelManager: Favored channels: %s", mFavoredChannelMask.ToString().AsCString());
350 }
351
SetCcaFailureRateThreshold(uint16_t aThreshold)352 void ChannelManager::SetCcaFailureRateThreshold(uint16_t aThreshold)
353 {
354 mCcaFailureRateThreshold = aThreshold;
355
356 otLogInfoUtil("ChannelManager: CCA threshold: 0x%04x", mCcaFailureRateThreshold);
357 }
358
359 } // namespace Utils
360 } // namespace ot
361
362 #endif // #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE
363