From 4d339496e5764f04c915cad13c45be9a2e2c754b Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 20 Feb 2018 09:28:46 -0800 Subject: [PATCH] [mac] adding Mac::ChannelMask class (#2560) This commit adds a `Mac::ChannelMask` class to define a channel (a `uint32_t` bit-vector specifying a set of channels). The `ChannelMask` class provides methods to add/remove channel to the mask, intersect two masks, and iterate through the channels in the mask. A unit test for the new class is also added. --- src/core/mac/mac.cpp | 39 ++++++---- src/core/mac/mac.hpp | 140 ++++++++++++++++++++++++++++++++-- tests/unit/test_mac_frame.cpp | 104 +++++++++++++++++++++++++ 3 files changed, 264 insertions(+), 19 deletions(-) diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index 212f8a931..ba902eb37 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -73,6 +73,27 @@ const uint32_t kMaxBackoffSum = kMinBackoff + (kUnitBackoffPeriod * OT_RADIO_SYM static_assert(kMinBackoffSum > 0, "The min backoff value should be greater than zero!"); #endif +otError ChannelMask::GetNextChannel(uint8_t &aChannel) const +{ + otError error = OT_ERROR_NOT_FOUND; + + if (aChannel == kChannelIteratorFirst) + { + aChannel = (OT_RADIO_CHANNEL_MIN - 1); + } + + for (aChannel++; aChannel <= OT_RADIO_CHANNEL_MAX; aChannel++) + { + if (ContainsChannel(aChannel)) + { + ExitNow(error = OT_ERROR_NONE); + } + } + +exit: + return error; +} + Mac::Mac(Instance &aInstance) : InstanceLocator(aInstance) , mOperation(kOperationIdle) @@ -102,7 +123,7 @@ Mac::Mac(Instance &aInstance) , mDataSequence(static_cast(otPlatRandomGet())) , mCsmaAttempts(0) , mTransmitAttempts(0) - , mScanChannels(0xff) + , mScanChannelMask() , mScanDuration(0) , mScanChannel(OT_RADIO_CHANNEL_MIN) , mEnergyScanCurrentMaxRssi(kInvalidRssiValue) @@ -169,8 +190,8 @@ void Mac::Scan(Operation aScanOperation, uint32_t aScanChannels, uint16_t aScanD { mScanContext = aContext; mScanDuration = aScanDuration; - mScanChannels = (aScanChannels == 0) ? static_cast(kScanChannelsAll) : aScanChannels; - mScanChannel = OT_RADIO_CHANNEL_MIN - 1; + mScanChannel = ChannelMask::kChannelIteratorFirst; + mScanChannelMask.SetMask((aScanChannels == 0) ? static_cast(kScanChannelsAll) : aScanChannels); StartOperation(aScanOperation); } @@ -237,17 +258,7 @@ exit: otError Mac::UpdateScanChannel(void) { - otError error = OT_ERROR_NONE; - - do - { - mScanChannel++; - VerifyOrExit(mScanChannel <= OT_RADIO_CHANNEL_MAX, error = OT_ERROR_NOT_FOUND); - - } while ((mScanChannels & (1U << mScanChannel)) == 0); - -exit: - return error; + return mScanChannelMask.GetNextChannel(mScanChannel); } void Mac::PerformActiveScan(void) diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index d00ffc8e1..66c68583f 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -101,6 +101,136 @@ enum kIndirectFrameMacTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_PER_POLL, }; +/** + * This class defines a channel mask. + * + * It is a wrapper class around a `uint32_t` bit vector representing a set of channels. + * + */ +class ChannelMask +{ +public: + enum + { + kChannelIteratorFirst = 0xff, ///< Value to pass in `GetNextChannel()` to get the first channel in the mask. + }; + + /** + * This constructor initializes a `ChannelMask` instance. + * + */ + ChannelMask(void) + : mMask(0) + { + } + + /** + * This constructor initializes a `ChannelMask` instance with a given mask. + * + * @param[in] aMask A channel mask (as a `uint32_t` bit-vector mask with bit 0 (lsb) -> channel 0, and so on). + * + */ + ChannelMask(uint32_t aMask) + : mMask(aMask) + { + } + + /** + * This method clears the channel mask. + * + */ + void Clear(void) { mMask = 0; } + + /** + * This method gets the channel mask (as a `uint32_t` bit-vector mask with bit 0 (lsb) -> channel 0, and so on). + * + * @returns The channel mask. + * + */ + uint32_t GetMask(void) const { return mMask; } + + /** + * This method sets the channel mask. + * + * @param[in] aMask A channel mask (as a `uint32_t` bit-vector mask with bit 0 (lsb) -> channel 0, and so on). + * + */ + void SetMask(uint32_t aMask) { mMask = aMask; } + + /** + * This method indicates if the mask is empty. + * + * @returns TRUE if the mask is empty, FALSE otherwise. + * + */ + bool IsEmpty(void) const { return (mMask == 0); } + + /** + * This method indicates if the mask contains only a single channel. + * + * @returns TRUE if channel mask contains a single channel, FALSE otherwise + * + */ + bool IsSingleChannel(void) const { return ((mMask != 0) && ((mMask & (mMask - 1)) == 0)); } + + /** + * This method indicates if the mask contains a given channel. + * + * @param[in] aChannel A channel. + * + * @returns TRUE if the channel @p aChannel is included in the mask, FALSE otherwise. + * + */ + bool ContainsChannel(uint8_t aChannel) const { return ((1U << aChannel) & mMask) != 0; } + + /** + * This method adds a channel to the channel mask. + * + * @param[in] aChannel A channel + * + */ + void AddChannel(uint8_t aChannel) { mMask |= (1U << aChannel); } + + /** + * This method removes a channel from the channel mask. + * + * @param[in] aChannel A channel + * + */ + void RemoveChannel(uint8_t aChannel) { mMask &= ~(1U << aChannel); } + + /** + * This method updates the channel mask by intersecting it with another mask. + * + * @param[in] aOtherMask Another channel mask. + * + */ + void Intersect(const ChannelMask &aOtherMask) { mMask &= aOtherMask.mMask; } + + /** + * This method gets the next channel in the channel mask. + * + * This method can be used to iterate over all channels in the channel mask. To get the first channel (channel with + * lowest number) in the mask the @p aChannel should be set to `kChannelIteratorFirst`. + * + * @param[inout] aChannel A reference to a `uint8_t`. + * On entry it should contain the previous channel or `kChannelIteratorFirst`. + * On exit it contains the next channel. + * + * @retval OT_ERROR_NONE Got the next channel, @p aChannel updated successfully. + * @retval OT_ERROR_NOT_FOUND No next channel in the channel mask (note: @p aChannel may be changed). + * + */ + otError GetNextChannel(uint8_t &aChannel) const; + +private: +#if (OT_RADIO_CHANNEL_MIN >= 32) || (OT_RADIO_CHANNEL_MAX >= 32) +#error `OT_RADIO_CHANNEL_MAX` or `OT_RADIO_CHANNEL_MIN` are larger than 32. `ChannelMask` uses 32 bit mask. +#endif + + uint32_t mMask; +}; + /** * This class implements a MAC receiver client. * @@ -749,11 +879,11 @@ private: uint8_t mCsmaAttempts; uint8_t mTransmitAttempts; - uint32_t mScanChannels; - uint16_t mScanDuration; - uint8_t mScanChannel; - int8_t mEnergyScanCurrentMaxRssi; - void * mScanContext; + ChannelMask mScanChannelMask; + uint16_t mScanDuration; + uint8_t mScanChannel; + int8_t mEnergyScanCurrentMaxRssi; + void * mScanContext; union { ActiveScanHandler mActiveScanHandler; diff --git a/tests/unit/test_mac_frame.cpp b/tests/unit/test_mac_frame.cpp index 39e98abfb..1fd162eff 100644 --- a/tests/unit/test_mac_frame.cpp +++ b/tests/unit/test_mac_frame.cpp @@ -29,6 +29,7 @@ #include #include "common/debug.hpp" +#include "mac/mac.hpp" #include "mac/mac_frame.hpp" #include "utils/wrap_string.h" @@ -80,12 +81,115 @@ void TestMacHeader(void) } } +void VerifyChannelMaskContent(const Mac::ChannelMask &aMask, uint8_t *aChannels, uint8_t aLength) +{ + uint8_t index = 0; + uint8_t channel; + + for (channel = OT_RADIO_CHANNEL_MIN; channel <= OT_RADIO_CHANNEL_MAX; channel++) + { + if (index < aLength) + { + if (channel == aChannels[index]) + { + index++; + VerifyOrQuit(aMask.ContainsChannel(channel), "ChannelMask.ContainsChannel() failed\n"); + } + else + { + VerifyOrQuit(!aMask.ContainsChannel(channel), "ChannelMask.ContainsChannel() failed\n"); + } + } + } + + index = 0; + channel = Mac::ChannelMask::kChannelIteratorFirst; + + while (aMask.GetNextChannel(channel) == OT_ERROR_NONE) + { + VerifyOrQuit(channel == aChannels[index++], "ChannelMask.GetNextChannel() failed\n"); + } + + VerifyOrQuit(index == aLength, "ChannelMask.GetNextChannel() failed\n"); + + if (aLength == 1) + { + VerifyOrQuit(aMask.IsSingleChannel(), "ChannelMask.IsSingleChannel() failed\n"); + } + else + { + VerifyOrQuit(!aMask.IsSingleChannel(), "ChannelMask.IsSingleChannel() failed\n"); + } +} + +void TestMacChannelMask(void) +{ + uint8_t all_channels[] = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}; + uint8_t channels1[] = {11, 14, 15, 20, 21, 26}; + uint8_t channels2[] = {14, 21, 25}; + uint8_t channels3[] = {14, 21}; + uint8_t channles4[] = {20}; + + Mac::ChannelMask mask1; + Mac::ChannelMask mask2(OT_RADIO_SUPPORTED_CHANNELS); + + printf("Testing Mac::ChannelMask\n"); + + VerifyOrQuit(mask1.IsEmpty(), "ChannelMask.IsEmpty failed\n"); + + VerifyOrQuit(!mask2.IsEmpty(), "ChannelMask.IsEmpty failed\n"); + VerifyOrQuit(mask2.GetMask() == OT_RADIO_SUPPORTED_CHANNELS, "ChannelMask.GetMask() failed\n"); + + mask1.SetMask(OT_RADIO_SUPPORTED_CHANNELS); + VerifyOrQuit(!mask1.IsEmpty(), "ChannelMask.IsEmpty failed\n"); + VerifyOrQuit(mask1.GetMask() == OT_RADIO_SUPPORTED_CHANNELS, "ChannelMask.GetMask() failed\n"); + + VerifyChannelMaskContent(mask1, all_channels, sizeof(all_channels)); + + // Test ChannelMask::RemoveChannel() + for (uint8_t index = 0; index < sizeof(all_channels) - 1; index++) + { + mask1.RemoveChannel(all_channels[index]); + VerifyChannelMaskContent(mask1, &all_channels[index + 1], sizeof(all_channels) - 1 - index); + } + + mask1.Clear(); + VerifyOrQuit(mask1.IsEmpty(), "ChannelMask.IsEmpty failed\n"); + VerifyChannelMaskContent(mask1, NULL, 0); + + for (uint16_t index = 0; index < sizeof(channels1); index++) + { + mask1.AddChannel(channels1[index]); + } + + VerifyOrQuit(!mask1.IsEmpty(), "ChannelMask.IsEmpty failed\n"); + VerifyChannelMaskContent(mask1, channels1, sizeof(channels1)); + + mask2.Clear(); + + for (uint16_t index = 0; index < sizeof(channels2); index++) + { + mask2.AddChannel(channels2[index]); + } + + VerifyOrQuit(!mask2.IsEmpty(), "ChannelMask.IsEmpty failed\n"); + VerifyChannelMaskContent(mask2, channels2, sizeof(channels2)); + + mask1.Intersect(mask2); + VerifyChannelMaskContent(mask1, channels3, sizeof(channels3)); + + mask2.Clear(); + mask2.AddChannel(channles4[0]); + VerifyChannelMaskContent(mask2, channles4, sizeof(channles4)); +} + } // namespace ot #ifdef ENABLE_TEST_MAIN int main(void) { ot::TestMacHeader(); + ot::TestMacChannelMask(); printf("All tests passed\n"); return 0; }