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 includes definitions for Channel Manager.
32  */
33 
34 #ifndef CHANNEL_MANAGER_HPP_
35 #define CHANNEL_MANAGER_HPP_
36 
37 #include "openthread-core-config.h"
38 
39 #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
40     (OPENTHREAD_FTD ||                          \
41      (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
42 
43 #include <openthread/platform/radio.h>
44 
45 #include "common/locator.hpp"
46 #include "common/non_copyable.hpp"
47 #include "common/timer.hpp"
48 #include "mac/mac.hpp"
49 
50 namespace ot {
51 namespace Utils {
52 
53 /**
54  * @addtogroup utils-channel-manager
55  *
56  * @brief
57  *   This module includes definitions for Channel Manager.
58  *
59  * @{
60  */
61 
62 /**
63  * Implements the Channel Manager.
64  *
65  */
66 class ChannelManager : public InstanceLocator, private NonCopyable
67 {
68 public:
69 #if OPENTHREAD_FTD
70     /**
71      * Minimum delay (in seconds) used for network channel change.
72      *
73      */
74     static constexpr uint16_t kMinimumDelay = OPENTHREAD_CONFIG_CHANNEL_MANAGER_MINIMUM_DELAY;
75 #endif
76 
77     /**
78      * Initializes a `ChanelManager` object.
79      *
80      * @param[in]   aInstance  A reference to the OpenThread instance.
81      *
82      */
83     explicit ChannelManager(Instance &aInstance);
84 
85 #if OPENTHREAD_FTD
86     /**
87      * Requests a Thread network channel change.
88      *
89      * The Thread network switches to the given channel after a specified delay (@sa GetDelay()). The channel change is
90      * performed by updating the Pending Operational Dataset.
91      *
92      * A subsequent call to this method will cancel an ongoing previously requested channel change.
93      *
94      * If the requested channel changes, it will trigger a `Notifier` event `kEventChannelManagerNewChannelChanged`.
95      *
96      * @param[in] aChannel             The new channel for the Thread network.
97      *
98      */
99     void RequestNetworkChannelChange(uint8_t aChannel);
100 #endif
101 
102     /**
103      * Gets the channel from the last successful call to `RequestNetworkChannelChange()` or `ChangeCslChannel()`.
104      *
105      * @returns The last requested channel, or zero if there has been no channel change request yet.
106      *
107      */
GetRequestedChannel(void) const108     uint8_t GetRequestedChannel(void) const { return mChannel; }
109 
110 #if OPENTHREAD_FTD
111     /**
112      * Gets the delay (in seconds) used for a channel change.
113      *
114      * @returns The delay (in seconds)
115      *
116      */
GetDelay(void) const117     uint16_t GetDelay(void) const { return mDelay; }
118 
119     /**
120      * Sets the delay (in seconds) used for a channel change.
121      *
122      * The delay should preferably be longer than maximum data poll interval used by all sleepy-end-devices within the
123      * Thread network.
124      *
125      * @param[in]  aDelay             Delay in seconds.
126      *
127      * @retval kErrorNone          Delay was updated successfully.
128      * @retval kErrorInvalidArgs   The given delay @p aDelay is shorter than `kMinimumDelay`.
129      *
130      */
131     Error SetDelay(uint16_t aDelay);
132 #endif // OPENTHREAD_FTD
133 
134 #if OPENTHREAD_FTD
135     /**
136      * Requests that `ChannelManager` checks and selects a new network channel and starts a network channel change.
137      *
138      * Unlike the `RequestChannelChange()`  where the channel must be given as a parameter, this method asks the
139      * `ChannelManager` to select a channel by itself (based on the collected channel quality info).
140      *
141      * Once called, the `ChannelManager` will perform the following 3 steps:
142      *
143      * 1) `ChannelManager` decides if the channel change would be helpful. This check can be skipped if
144      *    `aSkipQualityCheck` is set to true (forcing a channel selection to happen and skipping the quality check).
145      *    This step uses the collected link quality metrics on the device (such as CCA failure rate, frame and message
146      *    error rates per neighbor, etc.) to determine if the current channel quality is at the level that justifies
147      *    a channel change.
148      *
149      * 2) If the first step passes, then `ChannelManager` selects a potentially better channel. It uses the collected
150      *    channel occupancy data by `ChannelMonitor` module. The supported and favored channels are used at this step.
151      *    (@sa SetSupportedChannels, @sa SetFavoredChannels).
152      *
153      * 3) If the newly selected channel is different from the current channel, `ChannelManager` requests/starts the
154      *    channel change process (internally invoking a `RequestNetworkChannelChange()`).
155      *
156      *
157      * @param[in] aSkipQualityCheck        Indicates whether the quality check (step 1) should be skipped.
158      *
159      * @retval kErrorNone              Channel selection finished successfully.
160      * @retval kErrorNotFound          Supported channels is empty, therefore could not select a channel.
161      * @retval kErrorInvalidState      Thread is not enabled or not enough data to select new channel.
162      *
163      */
164     Error RequestNetworkChannelSelect(bool aSkipQualityCheck);
165 #endif // OPENTHREAD_FTD
166 
167 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
168     /**
169      * Requests that `ChannelManager` checks and selects a new Csl channel and starts a channel change.
170      *
171      * Once called, the `ChannelManager` will perform the following 3 steps:
172      *
173      * 1) `ChannelManager` decides if the channel change would be helpful. This check can be skipped if
174      *    `aSkipQualityCheck` is set to true (forcing a channel selection to happen and skipping the quality check).
175      *    This step uses the collected link quality metrics on the device (such as CCA failure rate, frame and message
176      *    error rates per neighbor, etc.) to determine if the current channel quality is at the level that justifies
177      *    a channel change.
178      *
179      * 2) If the first step passes, then `ChannelManager` selects a potentially better channel. It uses the collected
180      *    channel occupancy data by `ChannelMonitor` module. The supported and favored channels are used at this step.
181      *    (@sa SetSupportedChannels, @sa SetFavoredChannels).
182      *
183      * 3) If the newly selected channel is different from the current Csl channel, `ChannelManager` starts the
184      *    channel change process (internally invoking a `ChangeCslChannel()`).
185      *
186      *
187      * @param[in] aSkipQualityCheck        Indicates whether the quality check (step 1) should be skipped.
188      *
189      * @retval kErrorNone              Channel selection finished successfully.
190      * @retval kErrorNotFound          Supported channels is empty, therefore could not select a channel.
191      * @retval kErrorInvalidState      Thread is not enabled or not enough data to select new channel.
192      *
193      */
194     Error RequestCslChannelSelect(bool aSkipQualityCheck);
195 #endif // (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
196 
197 #if OPENTHREAD_FTD
198     /**
199      * Enables/disables the auto-channel-selection functionality.
200      *
201      * When enabled, `ChannelManager` will periodically invoke a `RequestChannelSelect(false)`. The period interval
202      * can be set by `SetAutoChannelSelectionInterval()`.
203      *
204      * @param[in]  aEnabled  Indicates whether to enable or disable this functionality.
205      *
206      */
207     void SetAutoNetworkChannelSelectionEnabled(bool aEnabled);
208 
209     /**
210      * Indicates whether the auto-channel-selection functionality is enabled or not.
211      *
212      * @returns TRUE if enabled, FALSE if disabled.
213      *
214      */
GetAutoNetworkChannelSelectionEnabled(void) const215     bool GetAutoNetworkChannelSelectionEnabled(void) const { return mAutoSelectEnabled; }
216 #endif
217 
218 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
219     /**
220      * Enables/disables the auto-channel-selection functionality.
221      *
222      * When enabled, `ChannelManager` will periodically invoke a `RequestChannelSelect(false)`. The period interval
223      * can be set by `SetAutoChannelSelectionInterval()`.
224      *
225      * @param[in]  aEnabled  Indicates whether to enable or disable this functionality.
226      *
227      */
228     void SetAutoCslChannelSelectionEnabled(bool aEnabled);
229 
230     /**
231      * Indicates whether the auto-channel-selection functionality is enabled or not.
232      *
233      * @returns TRUE if enabled, FALSE if disabled.
234      *
235      */
GetAutoCslChannelSelectionEnabled(void) const236     bool GetAutoCslChannelSelectionEnabled(void) const { return mAutoSelectCslEnabled; }
237 #endif
238 
239     /**
240      * Sets the period interval (in seconds) used by auto-channel-selection functionality.
241      *
242      * @param[in] aInterval            The interval (in seconds).
243      *
244      * @retval kErrorNone          The interval was set successfully.
245      * @retval kErrorInvalidArgs   The @p aInterval is not valid (zero).
246      *
247      */
248     Error SetAutoChannelSelectionInterval(uint32_t aInterval);
249 
250     /**
251      * Gets the period interval (in seconds) used by auto-channel-selection functionality.
252      *
253      * @returns The interval (in seconds).
254      *
255      */
GetAutoChannelSelectionInterval(void) const256     uint32_t GetAutoChannelSelectionInterval(void) const { return mAutoSelectInterval; }
257 
258     /**
259      * Gets the supported channel mask.
260      *
261      * @returns  The supported channels mask.
262      *
263      */
GetSupportedChannels(void) const264     uint32_t GetSupportedChannels(void) const { return mSupportedChannelMask.GetMask(); }
265 
266     /**
267      * Sets the supported channel mask.
268      *
269      * @param[in]  aChannelMask  A channel mask.
270      *
271      */
272     void SetSupportedChannels(uint32_t aChannelMask);
273 
274     /**
275      * Gets the favored channel mask.
276      *
277      * @returns  The favored channels mask.
278      *
279      */
GetFavoredChannels(void) const280     uint32_t GetFavoredChannels(void) const { return mFavoredChannelMask.GetMask(); }
281 
282     /**
283      * Sets the favored channel mask.
284      *
285      * @param[in]  aChannelMask  A channel mask.
286      *
287      */
288     void SetFavoredChannels(uint32_t aChannelMask);
289 
290     /**
291      * Gets the CCA failure rate threshold
292      *
293      * @returns  The CCA failure rate threshold
294      *
295      */
GetCcaFailureRateThreshold(void) const296     uint16_t GetCcaFailureRateThreshold(void) const { return mCcaFailureRateThreshold; }
297 
298     /**
299      * Sets the CCA failure rate threshold
300      *
301      * @param[in]  aThreshold  A CCA failure rate threshold.
302      *
303      */
304     void SetCcaFailureRateThreshold(uint16_t aThreshold);
305 
306 private:
307     // Retry interval to resend Pending Dataset in case of tx failure (in ms).
308     static constexpr uint32_t kPendingDatasetTxRetryInterval = 20000;
309 
310     // Maximum jitter/wait time to start a requested network channel change (in ms).
311     static constexpr uint32_t kRequestStartJitterInterval = 10000;
312 
313     // The minimum number of RSSI samples required before using the collected data (by `ChannelMonitor`) to select
314     // a channel.
315     static constexpr uint32_t kMinChannelMonitorSampleCount =
316         OPENTHREAD_CONFIG_CHANNEL_MANAGER_MINIMUM_MONITOR_SAMPLE_COUNT;
317 
318     // Minimum channel occupancy difference to prefer an unfavored channel over a favored one.
319     static constexpr uint16_t kThresholdToSkipFavored = OPENTHREAD_CONFIG_CHANNEL_MANAGER_THRESHOLD_TO_SKIP_FAVORED;
320 
321     // Minimum channel occupancy difference between current channel and the selected channel to trigger the channel
322     // change process to start.
323     static constexpr uint16_t kThresholdToChangeChannel = OPENTHREAD_CONFIG_CHANNEL_MANAGER_THRESHOLD_TO_CHANGE_CHANNEL;
324 
325     // Default auto-channel-selection period (in seconds).
326     static constexpr uint32_t kDefaultAutoSelectInterval =
327         OPENTHREAD_CONFIG_CHANNEL_MANAGER_DEFAULT_AUTO_SELECT_INTERVAL;
328 
329     // Minimum CCA failure rate on current channel to start the channel selection process.
330     static constexpr uint16_t kCcaFailureRateThreshold = OPENTHREAD_CONFIG_CHANNEL_MANAGER_CCA_FAILURE_THRESHOLD;
331 
332     enum State : uint8_t
333     {
334         kStateIdle,
335         kStateChangeRequested,
336         kStateChangeInProgress,
337     };
338 
339 #if OPENTHREAD_FTD
340     void        StartDatasetUpdate(void);
341     static void HandleDatasetUpdateDone(Error aError, void *aContext);
342     void        HandleDatasetUpdateDone(Error aError);
343 #endif
344     void  HandleTimer(void);
345     void  StartAutoSelectTimer(void);
346     Error RequestChannelSelect(bool aSkipQualityCheck);
347     Error RequestAutoChannelSelect(bool aSkipQualityCheck);
348     void  RequestChannelChange(uint8_t aChannel);
349 
350 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
351     Error FindBetterChannel(uint8_t &aNewChannel, uint16_t &aOccupancy);
352     bool  ShouldAttemptChannelChange(void);
353 #endif
354 
355 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
356     void ChangeCslChannel(uint8_t aChannel);
357 #endif
358 
359     using ManagerTimer = TimerMilliIn<ChannelManager, &ChannelManager::HandleTimer>;
360 
361     Mac::ChannelMask mSupportedChannelMask;
362     Mac::ChannelMask mFavoredChannelMask;
363 #if OPENTHREAD_FTD
364     uint16_t mDelay;
365 #endif
366     uint8_t      mChannel;
367     uint8_t      mChannelSelected;
368     State        mState;
369     ManagerTimer mTimer;
370     uint32_t     mAutoSelectInterval;
371 #if OPENTHREAD_FTD
372     bool mAutoSelectEnabled;
373 #endif
374 #if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
375     bool mAutoSelectCslEnabled;
376 #endif
377     uint16_t mCcaFailureRateThreshold;
378 };
379 
380 /**
381  * @}
382  *
383  */
384 
385 } // namespace Utils
386 } // namespace ot
387 
388 #endif // OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
389 
390 #endif // CHANNEL_MANAGER_HPP_
391