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