diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj index e9cdd7bf4..d8ab3ed10 100644 --- a/etc/visual-studio/libopenthread.vcxproj +++ b/etc/visual-studio/libopenthread.vcxproj @@ -124,6 +124,7 @@ + @@ -206,6 +207,7 @@ + diff --git a/etc/visual-studio/libopenthread.vcxproj.filters b/etc/visual-studio/libopenthread.vcxproj.filters index 472f8e824..1fd4e610a 100644 --- a/etc/visual-studio/libopenthread.vcxproj.filters +++ b/etc/visual-studio/libopenthread.vcxproj.filters @@ -345,6 +345,9 @@ Source Files\thread + + Source Files\thread + Source Files\common @@ -458,6 +461,9 @@ Header Files\thread + + Header Files\thread + Header Files\thread diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj index fed719489..c2fac623e 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj +++ b/etc/visual-studio/libopenthread_k.vcxproj @@ -132,6 +132,7 @@ + @@ -243,6 +244,7 @@ + diff --git a/etc/visual-studio/libopenthread_k.vcxproj.filters b/etc/visual-studio/libopenthread_k.vcxproj.filters index 45120444a..288725a99 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj.filters +++ b/etc/visual-studio/libopenthread_k.vcxproj.filters @@ -333,6 +333,9 @@ Source Files\thread + + Source Files\thread + Source Files\meshcop @@ -590,6 +593,9 @@ Header Files\thread + + Header Files\thread + Header Files\meshcop diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 3f718fb62..a710739a8 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -171,6 +171,7 @@ SOURCES_COMMON = \ net/udp6.cpp \ thread/address_resolver.cpp \ thread/announce_begin_server.cpp \ + thread/announce_sender.cpp \ thread/child_table.cpp \ thread/data_poll_manager.cpp \ thread/energy_scan_server.cpp \ @@ -292,6 +293,7 @@ HEADERS_COMMON = \ net/udp6.hpp \ thread/address_resolver.hpp \ thread/announce_begin_server.hpp \ + thread/announce_sender.hpp \ thread/child_table.hpp \ thread/data_poll_manager.hpp \ thread/energy_scan_server.hpp \ diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp index c4cd65e64..26257e9cb 100644 --- a/src/core/common/instance.cpp +++ b/src/core/common/instance.cpp @@ -72,6 +72,9 @@ Instance::Instance(void) #endif #if OPENTHREAD_ENABLE_CHANNEL_MANAGER , mChannelManager(*this) +#endif +#if OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER + , mAnnounceSender(*this) #endif , mMessagePool(*this) #endif // OPENTHREAD_MTD || OPENTHREAD_FTD @@ -417,6 +420,13 @@ template <> Utils::ChannelManager &Instance::Get(void) } #endif +#if OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER +template <> AnnounceSender &Instance::Get(void) +{ + return GetAnnounceSender(); +} +#endif + #endif // OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index 45ae38004..2c48f6c34 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -56,6 +56,7 @@ #include "common/notifier.hpp" #include "common/settings.hpp" #include "net/ip6.hpp" +#include "thread/announce_sender.hpp" #include "thread/link_quality.hpp" #include "thread/thread_netif.hpp" #if OPENTHREAD_ENABLE_CHANNEL_MANAGER @@ -329,6 +330,16 @@ public: Utils::ChannelManager &GetChannelManager(void) { return mChannelManager; } #endif +#if OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER + /** + * This method returns a reference to AnnounceSender object. + * + * @returns A reference to the AnnounceSender object. + * + */ + AnnounceSender &GetAnnounceSender(void) { return mAnnounceSender; } +#endif + /** * This method returns a reference to message pool object. * @@ -404,6 +415,10 @@ private: Utils::ChannelManager mChannelManager; #endif +#if OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER + AnnounceSender mAnnounceSender; +#endif + MessagePool mMessagePool; #endif // OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index a67826710..70c287d8c 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -73,6 +73,19 @@ const uint32_t kMaxBackoffSum = kMinBackoff + (kUnitBackoffPeriod * OT_RADIO_SYM static_assert(kMinBackoffSum > 0, "The min backoff value should be greater than zero!"); #endif +uint8_t ChannelMask::GetNumberOfChannels(void) const +{ + uint8_t num = 0; + uint8_t channel = kChannelIteratorFirst; + + while (GetNextChannel(channel) == OT_ERROR_NONE) + { + num++; + } + + return num; +} + otError ChannelMask::GetNextChannel(uint8_t &aChannel) const { otError error = OT_ERROR_NOT_FOUND; diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index 472b3287f..08b0f2b78 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -204,6 +204,14 @@ public: */ void Intersect(const ChannelMask &aOtherMask) { mMask &= aOtherMask.mMask; } + /** + * This method returns the number of channels in the mask. + * + * @returns Number of channels in the mask. + * + */ + uint8_t GetNumberOfChannels(void) const; + /** * This method gets the next channel in the channel mask. * @@ -220,6 +228,26 @@ public: */ otError GetNextChannel(uint8_t &aChannel) const; + /** + * This method overloads `==` operator to indicate whether two masks are equal. + * + * @param[in] aAnother A reference to another mask to compare with the current one. + * + * @returns TRUE if the two masks are equal, FALSE otherwise. + * + */ + bool operator==(const ChannelMask &aAnother) const { return (mMask == aAnother.mMask); } + + /** + * This method overloads `!=` operator to indicate whether two masks are different. + * + * @param[in] aAnother A reference to another mask to compare with the current one. + * + * @returns TRUE if the two masks are different, FALSE otherwise. + * + */ + bool operator!=(const ChannelMask &aAnother) const { return (mMask != aAnother.mMask); } + /** * This method converts the channel mask into a human-readable NULL-terminated string. * diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index e4764232c..409353435 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -1353,6 +1353,51 @@ #define OPENTHREAD_CONFIG_SEND_UNICAST_ANNOUNCE_RESPONSE 1 #endif +/** + * @def OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER + * + * Define as 1 to enable `AnnounceSender` which will periodically send MLE Announce message on all channels. + * + * The list of channels is determined from the Operational Dataset's ChannelMask. The period intervals are determined + * by `OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_ROUTER` and `OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_REED` + * configuration options. + * + */ +#ifndef OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER +#define OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_ROUTER + * + * Specifies the time interval (in milliseconds) between `AnnounceSender` transmit cycles on a device in Router role. + * + * In a cycle, the `AnnounceSender` sends MLE Announcement on all channels in Active Operational Dataset's ChannelMask. + * The transmissions on different channels happen uniformly over the given interval (i.e., if there are 16 channels, + * there will be 16 MLE Announcement messages each on one channel with `interval / 16` between two consecutive MLE + * Announcement transmissions). + * + * Applicable only if `AnnounceSender` feature is enabled (see `OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER`). + * + */ +#ifndef OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_ROUTER +#define OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_ROUTER 688000 // 668 seconds = 11 min and 28 sec. +#endif + +/** + * @def OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_REED + * + * Specifies the time interval (in milliseconds) between `AnnounceSender` transmit cycles on a device in REED role. + * + * This is similar to `OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_ROUTER` but used when device is in REED role. + * + * Applicable only if `AnnounceSender` feature is enabled (see `OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER`). + * + */ +#ifndef OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_REED +#define OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_REED (668000 * 3) +#endif + /** * @def OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE * diff --git a/src/core/thread/announce_begin_server.cpp b/src/core/thread/announce_begin_server.cpp index e6ce43806..9a94f9674 100644 --- a/src/core/thread/announce_begin_server.cpp +++ b/src/core/thread/announce_begin_server.cpp @@ -52,12 +52,7 @@ using ot::Encoding::BigEndian::HostSwap32; namespace ot { AnnounceBeginServer::AnnounceBeginServer(Instance &aInstance) - : InstanceLocator(aInstance) - , mChannelMask(0) - , mPeriod(0) - , mCount(0) - , mChannel(0) - , mTimer(aInstance, &AnnounceBeginServer::HandleTimer, this) + : AnnounceSenderBase(aInstance, &AnnounceBeginServer::HandleTimer) , mAnnounceBegin(OT_URI_PATH_ANNOUNCE_BEGIN, &AnnounceBeginServer::HandleRequest, this) { GetNetif().GetCoap().AddResource(mAnnounceBegin); @@ -70,23 +65,7 @@ otError AnnounceBeginServer::SendAnnounce(uint32_t aChannelMask) otError AnnounceBeginServer::SendAnnounce(uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod) { - otError error = OT_ERROR_NONE; - - mChannelMask = aChannelMask; - mCount = aCount; - mPeriod = aPeriod; - mChannel = OT_RADIO_CHANNEL_MIN; - - while ((mChannelMask & (1 << mChannel)) == 0) - { - mChannel++; - VerifyOrExit(mChannel <= OT_RADIO_CHANNEL_MAX, error = OT_ERROR_INVALID_ARGS); - } - - mTimer.Start(mPeriod); - -exit: - return error; + return AnnounceSenderBase::SendAnnounce(Mac::ChannelMask(aChannelMask), aCount, aPeriod, kDefaultJitter); } void AnnounceBeginServer::HandleRequest(void * aContext, @@ -98,6 +77,7 @@ void AnnounceBeginServer::HandleRequest(void * aContext, *static_cast(aMessage), *static_cast(aMessageInfo)); } + void AnnounceBeginServer::HandleRequest(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { MeshCoP::ChannelMask0Tlv channelMask; @@ -130,29 +110,7 @@ exit: void AnnounceBeginServer::HandleTimer(Timer &aTimer) { - aTimer.GetOwner().HandleTimer(); -} - -void AnnounceBeginServer::HandleTimer(void) -{ - GetNetif().GetMle().SendAnnounce(mChannel++, false); - - while (mCount > 0) - { - if (mChannelMask & (1 << mChannel)) - { - mTimer.Start(mPeriod); - break; - } - - mChannel++; - - if (mChannel > OT_RADIO_CHANNEL_MAX) - { - mChannel = OT_RADIO_CHANNEL_MIN; - mCount--; - } - } + aTimer.GetOwner().AnnounceSenderBase::HandleTimer(); } } // namespace ot diff --git a/src/core/thread/announce_begin_server.hpp b/src/core/thread/announce_begin_server.hpp index 8b800fbfb..2c5dafdad 100644 --- a/src/core/thread/announce_begin_server.hpp +++ b/src/core/thread/announce_begin_server.hpp @@ -42,6 +42,7 @@ #include "common/locator.hpp" #include "common/timer.hpp" #include "net/ip6_address.hpp" +#include "thread/announce_sender.hpp" namespace ot { @@ -49,7 +50,7 @@ namespace ot { * This class implements handling Announce Begin Requests. * */ -class AnnounceBeginServer : public InstanceLocator +class AnnounceBeginServer : public AnnounceSenderBase { public: /** @@ -85,6 +86,7 @@ private: { kDefaultCount = 3, kDefaultPeriod = 1000, + kDefaultJitter = 0, }; static void HandleRequest(void * aContext, @@ -94,14 +96,6 @@ private: void HandleRequest(Coap::Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); static void HandleTimer(Timer &aTimer); - void HandleTimer(void); - - uint32_t mChannelMask; - uint16_t mPeriod; - uint8_t mCount; - uint8_t mChannel; - - TimerMilli mTimer; Coap::Resource mAnnounceBegin; }; diff --git a/src/core/thread/announce_sender.cpp b/src/core/thread/announce_sender.cpp new file mode 100644 index 000000000..3c7ca9941 --- /dev/null +++ b/src/core/thread/announce_sender.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2016-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 AnnounceSender. + */ + +#define WPP_NAME "announce_sender.tmh" + +#include "announce_sender.hpp" + +#include + +#include "common/code_utils.hpp" +#include "common/instance.hpp" +#include "common/logging.hpp" +#include "common/owner-locator.hpp" +#include "common/random.hpp" +#include "meshcop/meshcop.hpp" +#include "meshcop/meshcop_tlvs.hpp" + +namespace ot { + +AnnounceSenderBase::AnnounceSenderBase(Instance &aInstance, Timer::Handler aHandler) + : InstanceLocator(aInstance) + , mChannelMask() + , mPeriod(0) + , mJitter(0) + , mCount(0) + , mChannel(0) + , mTimer(aInstance, aHandler, this) +{ +} + +otError AnnounceSenderBase::SendAnnounce(Mac::ChannelMask aMask, uint8_t aCount, uint32_t aPeriod, uint16_t aJitter) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aPeriod != 0, error = OT_ERROR_INVALID_ARGS); + VerifyOrExit(aJitter < aPeriod, error = OT_ERROR_INVALID_ARGS); + + aMask.Intersect(OT_RADIO_SUPPORTED_CHANNELS); + VerifyOrExit(!aMask.IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + mChannelMask = aMask; + mCount = aCount; + mPeriod = aPeriod; + mJitter = aJitter; + mChannel = Mac::ChannelMask::kChannelIteratorFirst; + + mTimer.Start(Random::AddJitter(mPeriod, mJitter)); + +exit: + return error; +} + +void AnnounceSenderBase::HandleTimer(void) +{ + otError error; + + error = mChannelMask.GetNextChannel(mChannel); + + if (error == OT_ERROR_NOT_FOUND) + { + if (mCount != 0) + { + mCount--; + VerifyOrExit(mCount != 0); + } + + mChannel = Mac::ChannelMask::kChannelIteratorFirst; + error = mChannelMask.GetNextChannel(mChannel); + } + + assert(error == OT_ERROR_NONE); + + GetNetif().GetMle().SendAnnounce(mChannel, false); + + mTimer.Start(Random::AddJitter(mPeriod, mJitter)); + +exit: + return; +} + +#if OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER + +AnnounceSender::AnnounceSender(Instance &aInstance) + : AnnounceSenderBase(aInstance, &AnnounceSender::HandleTimer) + , mNotifierCallback(HandleStateChanged, this) +{ + aInstance.GetNotifier().RegisterCallback(mNotifierCallback); +} + +void AnnounceSender::HandleTimer(Timer &aTimer) +{ + aTimer.GetOwner().AnnounceSenderBase::HandleTimer(); +} + +otError AnnounceSender::GetActiveDatasetChannelMask(Mac::ChannelMask &aMask) const +{ + otError error = OT_ERROR_NONE; + const MeshCoP::ChannelMask0Tlv *channelMaskTlv; + MeshCoP::Dataset dataset(MeshCoP::Tlv::kActiveTimestamp); + + SuccessOrExit(error = GetNetif().GetActiveDataset().Get(dataset)); + + channelMaskTlv = static_cast(dataset.Get(MeshCoP::Tlv::kChannelMask)); + VerifyOrExit(channelMaskTlv != NULL, error = OT_ERROR_NOT_FOUND); + + aMask.SetMask(channelMaskTlv->GetMask()); + +exit: + return error; +} + +void AnnounceSender::CheckState(void) +{ + Mle::MleRouter & mle = GetInstance().Get(); + uint32_t interval = kRouterTxInterval; + uint32_t period; + Mac::ChannelMask channelMask; + char string[Mac::ChannelMask::kInfoStringSize]; + + switch (mle.GetRole()) + { + case OT_DEVICE_ROLE_ROUTER: + case OT_DEVICE_ROLE_LEADER: + period = kRouterTxInterval; + break; + + case OT_DEVICE_ROLE_CHILD: + if (mle.IsRouterRoleEnabled() && (mle.GetDeviceMode() & Mle::ModeTlv::kModeRxOnWhenIdle)) + { + period = kReedTxInterval; + break; + } + + // fall through + + case OT_DEVICE_ROLE_DISABLED: + case OT_DEVICE_ROLE_DETACHED: + Stop(); + ExitNow(); + } + + VerifyOrExit(GetActiveDatasetChannelMask(channelMask) == OT_ERROR_NONE, Stop()); + channelMask.Intersect(OT_RADIO_SUPPORTED_CHANNELS); + VerifyOrExit(!channelMask.IsEmpty(), Stop()); + + period = interval / channelMask.GetNumberOfChannels(); + + if (period < kMinTxPeriod) + { + period = kMinTxPeriod; + } + + VerifyOrExit(!IsRunning() || (period != GetPeriod()) || (GetChannelMask() != channelMask)); + + SendAnnounce(channelMask, 0, period, kMaxJitter); + + otLogInfoMle(GetInstance(), "Starting periodic MLE Announcements tx, period %u, mask %s", period, + channelMask.ToString(string, sizeof(string))); + + OT_UNUSED_VARIABLE(string); + +exit: + return; +} + +void AnnounceSender::Stop(void) +{ + AnnounceSenderBase::Stop(); + otLogInfoMle(GetInstance(), "Stopping periodic MLE Announcements tx"); +} + +void AnnounceSender::HandleStateChanged(Notifier::Callback &aCallback, uint32_t aFlags) +{ + aCallback.GetOwner().HandleStateChanged(aFlags); +} + +void AnnounceSender::HandleStateChanged(uint32_t aFlags) +{ + if ((aFlags & OT_CHANGED_THREAD_ROLE) != 0) + { + CheckState(); + } +} + +#endif // OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER + +} // namespace ot diff --git a/src/core/thread/announce_sender.hpp b/src/core/thread/announce_sender.hpp new file mode 100644 index 000000000..17f76564f --- /dev/null +++ b/src/core/thread/announce_sender.hpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2016-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 definition for AnnounceSender. + */ + +#ifndef ANNOUNCE_SENDER_HPP_ +#define ANNOUNCE_SENDER_HPP_ + +#include "openthread-core-config.h" + +#include + +#include "common/locator.hpp" +#include "common/notifier.hpp" +#include "common/timer.hpp" +#include "mac/mac.hpp" + +namespace ot { + +/** + * This class implements the base class for an `AnnounceSender` and `AnnoucneBeginSever`. + * + * This class provides APIs to schedule periodic transmission of MLE Announcement messages for a given number + * transmissions per channel. + */ +class AnnounceSenderBase : public InstanceLocator +{ +protected: + /** + * This constructor initializes the object. + * + * @param[in] aInstance A reference to the OpenThread instance. + * @param[in] aHandler A timer handler provided by sub-class. + * + */ + AnnounceSenderBase(Instance &aInstance, Timer::Handler aHandler); + + /** + * This method schedules the MLE Announce transmissions. + * + * This method schedules `aCount` MLE Announcement transmission cycles. Each cycle covers all the channel in + * the `aChannelMask`, with `aPeriod` time interval between any two successive MLE Announcement transmissions + * (possibly) on different channels from the given mask. The `aJitter` can be used to add a random jitter + * of `[-aJitter, aJitter]` to `aPeriod` interval. A zero value for `aCount` indicates non-stop MLE Announcement + * transmission cycles. + * + * @param[in] aChannelMask The channel mask providing the list of channels to use for transmission. + * @param[in] aCount The number of transmissions per channel. Zero indicates non-stop transmissions. + * @param[in] aPeriod The time between two successive MLE Announce transmissions (in milliseconds). + * @param[in] aJitter Maximum random jitter added to @aPeriod per transmission (in milliseconds). + * + * @retval OT_ERROR_NONE Successfully started the transmission process. + * @retval OT_ERROR_INVALID_ARGS @p aChanelMask is empty, or @p aPeriod is zero or smaller than @aJitter. + * + */ + otError SendAnnounce(Mac::ChannelMask aChannelMask, uint8_t aCount, uint32_t aPeriod, uint16_t aJitter); + + /** + * This method stops the ongoing MLE Announce transmissions. + * + */ + void Stop(void) { mTimer.Stop(); } + + /** + * This method indicates whether the latest scheduled MLE Announce transmission is currently in progress or is + * finished. + * + * @returns TRUE if the MLE Announce transmission is in progress, FALSE otherwise. + * + */ + bool IsRunning(void) const { return mTimer.IsRunning(); } + + /** + * This method gets the period for the latest scheduled MLE Announce transmission (the one in progress or the last + * finished one). + * + * @returns The period interval (in milliseconds) between two successive MLE Announcement transmissions. + * + */ + uint32_t GetPeriod(void) const { return mPeriod; } + + /** + * This method gets the channel mask for the latest scheduled MLE Announce transmission (the one in progress or the + * last finished one). + * + * @returns A constant reference to channel mask + * + */ + const Mac::ChannelMask GetChannelMask(void) const { return mChannelMask; } + + /** + * This method is the timer handler and must be invoked by sub-class when the timer expires from the `aHandler` + * callback function provided in the constructor. + * + */ + void HandleTimer(void); + +private: + Mac::ChannelMask mChannelMask; + uint32_t mPeriod; + uint16_t mJitter; + uint8_t mCount; + uint8_t mChannel; + TimerMilli mTimer; +}; + +#if OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER + +/** + * This class implements an AnnounceSender. + * + */ +class AnnounceSender : public AnnounceSenderBase +{ +public: + /** + * This constructor initializes the object. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + AnnounceSender(Instance &aInstance); + +private: + enum + { + kRouterTxInterval = OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_ROUTER, + kReedTxInterval = OPENTHREAD_CONFIG_ANNOUNCE_SENDER_INTERVAL_REED, + kMinTxPeriod = 1000, // in ms + kMaxJitter = 500, // in ms + }; + + otError GetActiveDatasetChannelMask(Mac::ChannelMask &aMask) const; + void CheckState(void); + void Stop(void); + static void HandleTimer(Timer &aTimer); + static void HandleStateChanged(Notifier::Callback &aCallback, uint32_t aFlags); + void HandleStateChanged(uint32_t aFlags); + + Notifier::Callback mNotifierCallback; +}; + +#endif // OPENTHREAD_CONFIG_ENABLE_ANNOUNCE_SENDER + +/** + * @} + */ + +} // namespace ot + +#endif // ANNOUNCE_SENDER_HPP_ diff --git a/src/core/thread/mle_router_mtd.hpp b/src/core/thread/mle_router_mtd.hpp index 2453d1785..8a8df6dbd 100644 --- a/src/core/thread/mle_router_mtd.hpp +++ b/src/core/thread/mle_router_mtd.hpp @@ -59,6 +59,8 @@ public: { } + bool IsRouterRoleEnabled(void) const { return false; } + bool IsSingleton(void) { return false; } otError BecomeRouter(ThreadStatusTlv::Status) { return OT_ERROR_NOT_CAPABLE; } diff --git a/tests/unit/test_mac_frame.cpp b/tests/unit/test_mac_frame.cpp index 9d8b3e47d..135eaf3ee 100644 --- a/tests/unit/test_mac_frame.cpp +++ b/tests/unit/test_mac_frame.cpp @@ -120,6 +120,8 @@ void VerifyChannelMaskContent(const Mac::ChannelMask &aMask, uint8_t *aChannels, { VerifyOrQuit(!aMask.IsSingleChannel(), "ChannelMask.IsSingleChannel() failed\n"); } + + VerifyOrQuit(aLength == aMask.GetNumberOfChannels(), "ChannelMask.GetNumberOfChannels() failed\n"); } void TestMacChannelMask(void) @@ -191,6 +193,17 @@ void TestMacChannelMask(void) VerifyChannelMaskContent(mask2, channles4, sizeof(channles4)); printf("channels4 = %s\n", mask2.ToString(stringBuffer, sizeof(stringBuffer))); + + mask1.Clear(); + mask2.Clear(); + VerifyOrQuit(mask1 == mask2, "ChannelMask.operator== failed\n"); + + mask1.SetMask(OT_RADIO_SUPPORTED_CHANNELS); + mask2.SetMask(OT_RADIO_SUPPORTED_CHANNELS); + VerifyOrQuit(mask1 == mask2, "ChannelMask.operator== failed\n"); + + mask1.Clear(); + VerifyOrQuit(mask1 != mask2, "ChannelMask.operator== failed\n"); } } // namespace ot