From cc8579fb3f99130f2ce2b1aaf5c1821218b821c0 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 6 Feb 2018 09:23:10 -0800 Subject: [PATCH] [channel-manager] adding Channel Manager module (#2535) This commit adds a new module/class `ChannelManager` which provides a mechanism to perform a network-wide channel change. It internally uses Pending Operational Dataset updates to trigger the channel change in the network. It also manages retries in case of errors/failures. This commit also adds public OT APIs for user to request a channel change and get/set different parameters related to operation of Channel Manager (e.g., supported channels mask). This feature can be enabled using `--enable-channel-manager` configure option (by default it is disabled). --- configure.ac | 33 +++ include/openthread/Makefile.am | 1 + include/openthread/channel_manager.h | 128 ++++++++++++ src/core/Makefile.am | 3 + src/core/api/channel_manager_api.cpp | 79 +++++++ src/core/common/instance.cpp | 10 + src/core/common/instance.hpp | 17 ++ src/core/openthread-core-default-config.h | 15 ++ src/core/utils/channel_manager.cpp | 241 ++++++++++++++++++++++ src/core/utils/channel_manager.hpp | 193 +++++++++++++++++ 10 files changed, 720 insertions(+) create mode 100644 include/openthread/channel_manager.h create mode 100644 src/core/api/channel_manager_api.cpp create mode 100644 src/core/utils/channel_manager.cpp create mode 100644 src/core/utils/channel_manager.hpp diff --git a/configure.ac b/configure.ac index 512ba480f..41288a1b7 100644 --- a/configure.ac +++ b/configure.ac @@ -796,6 +796,38 @@ AC_SUBST(OPENTHREAD_ENABLE_CHANNEL_MONITOR) AM_CONDITIONAL([OPENTHREAD_ENABLE_CHANNEL_MONITOR], [test "${enable_channel_monitor}" = "yes"]) AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_CHANNEL_MONITOR],[${OPENTHREAD_ENABLE_CHANNEL_MONITOR}],[Define to 1 if you want to use channel monitor feature]) +# +# Channel Manager +# + +AC_ARG_ENABLE(channel_manager, + [AS_HELP_STRING([--enable-channel-manager],[Enable Channel Manager support @<:@default=no@:>@.])], + [ + case "${enableval}" in + + no|yes) + enable_channel_manager=${enableval} + ;; + + *) + AC_MSG_ERROR([Invalid value ${enable_channel_manager} for --enable-channel-manager]) + ;; + esac + ], + [enable_channel_manager=no]) + +if test "$enable_channel_manager" = "yes"; then + OPENTHREAD_ENABLE_CHANNEL_MANAGER=1 +else + OPENTHREAD_ENABLE_CHANNEL_MANAGER=0 +fi + +AC_MSG_CHECKING([whether to enable channel manager]) +AC_MSG_RESULT(${enable_channel_manager}) +AC_SUBST(OPENTHREAD_ENABLE_CHANNEL_MANAGER) +AM_CONDITIONAL([OPENTHREAD_ENABLE_CHANNEL_MANAGER], [test "${enable_channel_manager}" = "yes"]) +AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_CHANNEL_MANAGER],[${OPENTHREAD_ENABLE_CHANNEL_MANAGER}],[Define to 1 if you want to enable channel manager feature]) + # # MAC Filter (include AddressFilter and RssInFilter) # @@ -1601,6 +1633,7 @@ AC_MSG_NOTICE([ OpenThread DTLS support : ${enable_dtls} OpenThread Jam Detection support : ${enable_jam_detection} OpenThread Channel Monitor support : ${enable_channel_monitor} + OpenThread Channel Manager support : ${enable_channel_manager} OpenThread MAC Filter support : ${enable_mac_filter} OpenThread Diagnostics support : ${enable_diag} OpenThread Child Supervision support : ${enable_child_supervision} diff --git a/include/openthread/Makefile.am b/include/openthread/Makefile.am index 7aff1b4fd..cb461fe9f 100644 --- a/include/openthread/Makefile.am +++ b/include/openthread/Makefile.am @@ -47,6 +47,7 @@ PRETTY_SUBDIRS = \ $(NULL) openthread_headers = \ + channel_manager.h \ channel_monitor.h \ child_supervision.h \ cli.h \ diff --git a/include/openthread/channel_manager.h b/include/openthread/channel_manager.h new file mode 100644 index 000000000..af196eddc --- /dev/null +++ b/include/openthread/channel_manager.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2018, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief + * This file includes the OpenThread API for Channel Manager module. + */ + +#ifndef OPENTHREAD_CHANNEL_MANAGER_H_ +#define OPENTHREAD_CHANNEL_MANAGER_H_ + + +#include "openthread/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup api-channel-manager + * + * @brief + * This module includes functions for Channel Manager. + * + * The functions in this module are available when Channel Manager feature (`OPENTHREAD_ENABLE_CHANNEL_MANAGER`) + * is enabled. Channel Manager is available only on an FTD build. + * + * @{ + * + */ + +/** + * This function requests a Thread network channel change. + * + * The network switches to the given channel after a specified delay (@sa otChannelManagerSetDelay). The channel change + * is performed by updating the Pending Operational Dataset. + * + * A subsequent call to this function will cancel an ongoing previously requested channel change. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aChannel The new channel for the Thread network. + + * @retval OT_ERROR_NONE Channel change request successfully processed. + * @retval OT_ERROR_INVALID_ARGS The channel is not a supported channel (@sa otChannelManagerGetSupportedChannels). + * + */ +otError otChannelManagerRequestChannelChange(otInstance *aInstance, uint8_t aChannel); + +/** + * This function gets the delay (in seconds) used by Channel Manager for a channel change. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The delay (in seconds) for channel change. + * + */ +uint16_t otChannelManagerGetDelay(otInstance *aInstance); + +/** + * This function sets the delay (in seconds) used for a channel change. + * + * The delay should preferably be longer than maximum data poll interval used by all sleepy-end-devices within the + * Thread network. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aDelay Delay in seconds. + * + * @retval OT_ERROR_NONE Delay was updated successfully. + * @retval OT_ERROR_INVALID_ARGS The given delay @p aDelay is too short. + * + */ +otError otChannelManagerSetDelay(otInstance *aInstance, uint16_t aDelay); + +/** + * This function gets the supported channel mask. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The supported channels as bit-mask. + * + */ +uint32_t otChannelManagerGetSupportedChannels(otInstance *aInstance); + +/** + * This function sets the supported channel mask. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aChannelMask A channel mask. + * + */ +void otChannelManagerSetSupportedChannels(otInstance *aInstance, uint32_t aChannelMask); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTHREAD_CHANNEL_MANAGER_H_ diff --git a/src/core/Makefile.am b/src/core/Makefile.am index b149369ac..3f70f19b8 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -90,6 +90,7 @@ libopenthread_mtd_a_CPPFLAGS = \ SOURCES_COMMON = \ api/coap_api.cpp \ api/commissioner_api.cpp \ + api/channel_manager_api.cpp \ api/channel_monitor_api.cpp \ api/child_supervision_api.cpp \ api/crypto_api.cpp \ @@ -185,6 +186,7 @@ SOURCES_COMMON = \ thread/thread_netif.cpp \ thread/tmf_proxy.cpp \ thread/topology.cpp \ + utils/channel_manager.cpp \ utils/channel_monitor.cpp \ utils/child_supervision.cpp \ utils/jam_detector.cpp \ @@ -295,6 +297,7 @@ HEADERS_COMMON = \ thread/thread_uri_paths.hpp \ thread/tmf_proxy.hpp \ thread/topology.hpp \ + utils/channel_manager.hpp \ utils/channel_monitor.hpp \ utils/child_supervision.hpp \ utils/slaac_address.hpp \ diff --git a/src/core/api/channel_manager_api.cpp b/src/core/api/channel_manager_api.cpp new file mode 100644 index 000000000..ccc97a690 --- /dev/null +++ b/src/core/api/channel_manager_api.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements the OpenThread channel manager APIs. + */ + +#include "openthread-core-config.h" +#include "openthread/channel_manager.h" + +#include "common/instance.hpp" +#include "utils/channel_manager.hpp" + +using namespace ot; + +#if OPENTHREAD_ENABLE_CHANNEL_MANAGER && OPENTHREAD_FTD + +otError otChannelManagerRequestChannelChange(otInstance *aInstance, uint8_t aChannel) +{ + Instance &instance = *static_cast(aInstance); + + return instance.GetChannelManager().RequestChannelChange(aChannel); +} + +uint16_t otChannelManagerGetDelay(otInstance *aInstance) +{ + Instance &instance = *static_cast(aInstance); + + return instance.GetChannelManager().GetDelay(); +} + +otError otChannelManagerSetDelay(otInstance *aInstance, uint16_t aMinDelay) +{ + Instance &instance = *static_cast(aInstance); + + return instance.GetChannelManager().SetDelay(aMinDelay); +} + +uint32_t otChannelManagerGetSupportedChannels(otInstance *aInstance) +{ + Instance &instance = *static_cast(aInstance); + + return instance.GetChannelManager().GetSupportedChannels(); +} + +void otChannelManagerSetSupportedChannels(otInstance *aInstance, uint32_t aChannelMask) +{ + Instance &instance = *static_cast(aInstance); + + return instance.GetChannelManager().SetSupportedChannels(aChannelMask); +} + +#endif // OPENTHREAD_ENABLE_CHANNEL_MANAGER && OPENTHREAD_FTD diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp index 36b9aa26d..b911bf004 100644 --- a/src/core/common/instance.cpp +++ b/src/core/common/instance.cpp @@ -73,6 +73,9 @@ Instance::Instance(void) : #endif #if OPENTHREAD_ENABLE_CHANNEL_MONITOR mChannelMonitor(*this), +#endif +#if OPENTHREAD_ENABLE_CHANNEL_MANAGER + mChannelManager(*this), #endif mMessagePool(*this), mIsInitialized(false) @@ -402,4 +405,11 @@ template<> Utils::ChannelMonitor &Instance::Get(void) } #endif +#if OPENTHREAD_ENABLE_CHANNEL_MANAGER +template<> Utils::ChannelManager &Instance::Get(void) +{ + return GetChannelManager(); +} +#endif + } // namespace ot diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index 77c499f84..6e84c29d0 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -55,6 +55,9 @@ #include "net/ip6.hpp" #include "thread/link_quality.hpp" #include "thread/thread_netif.hpp" +#if OPENTHREAD_ENABLE_CHANNEL_MANAGER +#include "utils/channel_manager.hpp" +#endif #if OPENTHREAD_ENABLE_CHANNEL_MONITOR #include "utils/channel_monitor.hpp" #endif @@ -314,6 +317,16 @@ public: Utils::ChannelMonitor &GetChannelMonitor(void) { return mChannelMonitor; } #endif +#if OPENTHREAD_ENABLE_CHANNEL_MANAGER + /** + * This method returns a reference to ChannelManager object. + * + * @returns A reference to the ChannelManager object. + * + */ + Utils::ChannelManager &GetChannelManager(void) { return mChannelManager; } +#endif + /** * This method returns a reference to message pool object. * @@ -382,6 +395,10 @@ private: Utils::ChannelMonitor mChannelMonitor; #endif +#if OPENTHREAD_ENABLE_CHANNEL_MANAGER + Utils::ChannelManager mChannelManager; +#endif + MessagePool mMessagePool; bool mIsInitialized; }; diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index 4c7024129..f82bdd347 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -1056,6 +1056,21 @@ #define OPENTHREAD_CONFIG_CHANNEL_MONITOR_SAMPLE_WINDOW 960 #endif +/** + * @def OPENTHREAD_CONFIG_CHANNEL_MANAGER_MINIMUM_DELAY + * + * The minimum delay in seconds used by Channel Manager module for performing a channel change. + * + * The minimum delay should preferably be longer than maximum data poll interval used by all sleepy-end-devices within + * the Thread network. + * + * Applicable only if Channel Manager feature is enabled (i.e., `OPENTHREAD_ENABLE_CHANNEL_MANAGER` is set). + * + */ +#ifndef OPENTHREAD_CONFIG_CHANNEL_MANAGER_MINIMUM_DELAY +#define OPENTHREAD_CONFIG_CHANNEL_MANAGER_MINIMUM_DELAY 120 +#endif + /** * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL * diff --git a/src/core/utils/channel_manager.cpp b/src/core/utils/channel_manager.cpp new file mode 100644 index 000000000..2f2fef41e --- /dev/null +++ b/src/core/utils/channel_manager.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2018, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements Channel Manager. + * + */ + +#include "channel_manager.hpp" + +#include + +#include "common/code_utils.hpp" +#include "common/instance.hpp" +#include "common/logging.hpp" +#include "common/owner-locator.hpp" + +#if OPENTHREAD_ENABLE_CHANNEL_MANAGER && OPENTHREAD_FTD + +namespace ot { +namespace Utils { + +ChannelManager::ChannelManager(Instance &aInstance): + InstanceLocator(aInstance), + mSupportedChannels(kDefaultSupprotedChannelMask), + mActiveTimestamp(0), + mNotifierCallback(&ChannelManager::HandleStateChanged, this), + mDelay(kMinimumDelay), + mChannel(0), + mState(kStateIdle), + mTimer(aInstance, &ChannelManager::HandleTimer, this) +{ +} + +otError ChannelManager::RequestChannelChange(uint8_t aChannel) +{ + otError error = OT_ERROR_NONE; + + otLogInfoUtil(GetInstance(), "ChannelManager: Request to change to channel %d with delay %d sec", aChannel, mDelay); + + VerifyOrExit(aChannel != GetInstance().Get().GetChannel()); + + if ((mSupportedChannels & (1U << aChannel)) == 0) + { + otLogInfoUtil(GetInstance(), "ChannelManager: Request rejected! Channel %d not in supported mask 0x%x", + aChannel, mSupportedChannels); + + ExitNow(error = OT_ERROR_INVALID_ARGS); + } + + mState = kStateChangeRequested; + mChannel = aChannel; + mActiveTimestamp = 0; + PreparePendingDataset(); + +exit: + return error; +} + +otError ChannelManager::SetDelay(uint16_t aDelay) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aDelay >= kMinimumDelay, error = OT_ERROR_INVALID_ARGS); + mDelay = aDelay; + +exit: + return error; +} + +void ChannelManager::PreparePendingDataset(void) +{ + ThreadNetif &netif = GetInstance().GetThreadNetif(); + uint64_t pendingTimestamp = 0; + otOperationalDataset dataset; + uint32_t delayInMs; + otError error; + + VerifyOrExit(mState == kStateChangeRequested); + + if (netif.GetPendingDataset().Get(dataset) == OT_ERROR_NONE) + { + if (dataset.mIsPendingTimestampSet) + { + pendingTimestamp = dataset.mPendingTimestamp; + } + } + + pendingTimestamp += (otPlatRandomGet() % kMaxTimestampIncrease) + 1; + + error = netif.GetActiveDataset().Get(dataset); + + if (error != OT_ERROR_NONE) + { + // If there is no valid Active Dataset but we are not disabled, set + // the timer to try again after the retry interval. This handles the + // situation where a channel change request comes right after the + // network is formed but before the active dataset is created. + + if (GetInstance().Get().GetRole() != OT_DEVICE_ROLE_DISABLED) + { + mTimer.Start(kPendingDatasetTxRetryInterval); + } + else + { + mState = kStateIdle; + otLogInfoUtil(GetInstance(), "ChannelManager: Request to change to channel %d failed. Device is disabled", + mChannel); + } + + ExitNow(); + } + + // A non-zero `mActiveTimestamp` indicates that this is not the first + // attempt to update the Dataset for the ongoing requested channel + // change. In that case, if the Timestamp in current Active Dataset is + // more recent compared to `mActiveTimestamp`, the channel change + // process is canceled. This helps address situations where two + // different devices may be performing channel change around the same + // time. + + if (mActiveTimestamp != 0) + { + if (dataset.mActiveTimestamp >= mActiveTimestamp) + { + otLogInfoUtil(GetInstance(), + "ChannelManager: Canceling channel change to %d since current ActiveDataset is more recent", + mChannel); + + ExitNow(); + } + } + else + { + mActiveTimestamp = dataset.mActiveTimestamp + 1 + (otPlatRandomGet() % kMaxTimestampIncrease); + } + + dataset.mActiveTimestamp = mActiveTimestamp; + dataset.mIsActiveTimestampSet = true; + + dataset.mChannel = mChannel; + dataset.mIsChannelSet = true; + + dataset.mPendingTimestamp = pendingTimestamp; + dataset.mIsPendingTimestampSet = true; + + delayInMs = TimerMilli::SecToMsec(static_cast(mDelay)); + dataset.mDelay = delayInMs; + dataset.mIsDelaySet = true; + + error = netif.GetPendingDataset().SendSetRequest(dataset, NULL, 0); + + if (error == OT_ERROR_NONE) + { + otLogInfoUtil(GetInstance(), "ChannelManager: Sent PendingDatasetSet to change channel to %d", mChannel); + + mState = kStateSentMgmtPendingDataset; + mTimer.Start(delayInMs + kChangeCheckWaitInterval); + } + else + { + otLogInfoUtil(GetInstance(), "ChannelManager: %s error in dataset update (channel change %d), retry in %d sec", + otThreadErrorToString(error), mChannel, TimerMilli::MsecToSec(kPendingDatasetTxRetryInterval)); + } + +exit: + return; +} + +void ChannelManager::HandleTimer(Timer &aTimer) +{ + aTimer.GetOwner().HandleTimer(); +} + +void ChannelManager::HandleTimer(void) +{ + switch (mState) + { + case kStateIdle: + break; + + case kStateSentMgmtPendingDataset: + otLogInfoUtil(GetInstance(), "ChannelManager: Timed out waiting for channel change to %d", mChannel); + + // fall through + + case kStateChangeRequested: + PreparePendingDataset(); + break; + } +} + +void ChannelManager::HandleStateChanged(Notifier::Callback &aCallback, uint32_t aFlags) +{ + aCallback.GetOwner().HandleStateChanged(aFlags); +} + +void ChannelManager::HandleStateChanged(uint32_t aFlags) +{ + VerifyOrExit((aFlags & OT_CHANGED_THREAD_CHANNEL) != 0); + VerifyOrExit(mChannel == GetInstance().Get().GetChannel()); + + mState = kStateIdle; + mTimer.Stop(); + + otLogInfoUtil(GetInstance(), "ChannelManager: Channel successfully changed to %d", mChannel); + +exit: + return; +} + +} // namespace Utils +} // namespace ot + +#endif // #if OPENTHREAD_ENABLE_CHANNEL_MANAGER diff --git a/src/core/utils/channel_manager.hpp b/src/core/utils/channel_manager.hpp new file mode 100644 index 000000000..0855067b3 --- /dev/null +++ b/src/core/utils/channel_manager.hpp @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2018, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for Channel Manager. + */ + +#ifndef CHANNEL_MANAGER_HPP_ +#define CHANNEL_MANAGER_HPP_ + +#include "openthread-core-config.h" + +#include +#include + +#include "common/locator.hpp" +#include "common/notifier.hpp" +#include "common/timer.hpp" + +namespace ot { +namespace Utils { + +/** + * @addtogroup utils-channel-manager + * + * @brief + * This module includes definitions for Channel Manager. + * + * @{ + */ + +#if OPENTHREAD_ENABLE_CHANNEL_MANAGER + +#if OPENTHREAD_FTD + +/** + * This class implements the Channel Manager. + * + */ +class ChannelManager : public InstanceLocator +{ +public: + enum + { + /** + * Minimum delay in seconds used for network channel change. + * + */ + kMinimumDelay = OPENTHREAD_CONFIG_CHANNEL_MANAGER_MINIMUM_DELAY, + }; + + /** + * This constructor initializes a `ChanelManager` object. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + explicit ChannelManager(Instance &aInstance); + + /** + * This method requests a Thread network channel change. + * + * The Thread network switches to the given channel after a specified delay (@sa GetDelay()). The channel change is + * performed by updating the Pending Operational Dataset. + * + * A subsequent call to this method will cancel an ongoing previously requested channel change. + * + * @param[in] aChannel The new channel for the Thread network. + * + * @retval OT_ERROR_NONE Channel change request successfully processed. + * @retval OT_ERROR_INVALID_ARGS The new channel is not a supported channel. + * + */ + otError RequestChannelChange(uint8_t aChannel); + + /** + * This method gets the delay (in seconds) used for a channel change. + * + * @returns The delay (in seconds) + * + */ + uint16_t GetDelay(void) const { return mDelay; } + + /** + * This method sets the delay (in seconds) used for a channel change. + * + * The delay should preferably be longer than maximum data poll interval used by all sleepy-end-devices within the + * Thread network. + * + * @param[in] aDelay Delay in seconds. + * + * @retval OT_ERROR_NONE Delay was updated successfully. + * @retval OT_ERROR_INVALID_ARGS The given delay @p aDelay is shorter than `kMinimumDelay`. + * + */ + otError SetDelay(uint16_t aDelay); + + /** + * This method gets the supported channel mask. + * + * @returns The supported channels mask. + * + */ + uint32_t GetSupportedChannels(void) const { return mSupportedChannels; } + + /** + * This method sets the supported channel mask. + * + * @param[in] aChannelMask A channel mask. + * + */ + void SetSupportedChannels(uint32_t aChannelMask) { + mSupportedChannels = (aChannelMask & OT_RADIO_SUPPORTED_CHANNELS); + } + +private: + enum + { + kDefaultSupprotedChannelMask = OT_RADIO_SUPPORTED_CHANNELS, + kMaxTimestampIncrease = 128, + + kPendingDatasetTxRetryInterval = 20000, // in ms + kChangeCheckWaitInterval = 30000, // in ms + }; + + enum State + { + kStateIdle, + kStateChangeRequested, + kStateSentMgmtPendingDataset, + }; + + static void HandleTimer(Timer &aTimer); + void HandleTimer(void); + static void HandleStateChanged(Notifier::Callback &aCallback, uint32_t aChangedFlags); + void HandleStateChanged(uint32_t aChangedFlags); + void PreparePendingDataset(void); + + uint32_t mSupportedChannels; + uint64_t mActiveTimestamp; + Notifier::Callback mNotifierCallback; + uint16_t mDelay; + uint8_t mChannel; + State mState; + TimerMilli mTimer; +}; + +#else // OPENTHREAD_FTD + +class ChannelManager +{ +public: + explicit ChannelManager(Instance &) { } +}; + +#endif // OPENTHREAD_FTD + +#endif // OPENTHREAD_ENABLE_CHANNEL_MANAGER +/** + * @} + * + */ + +} // namespace Utils +} // namespace ot + +#endif // CHANNEL_MANAGER_HPP_