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