mirror of
https://github.com/espressif/openthread.git
synced 2026-09-01 14:59:54 +00:00
[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.
This commit is contained in:
committed by
Jonathan Hui
parent
db4759cc41
commit
4d339496e5
+25
-14
@@ -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<uint8_t>(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<uint32_t>(kScanChannelsAll) : aScanChannels;
|
||||
mScanChannel = OT_RADIO_CHANNEL_MIN - 1;
|
||||
mScanChannel = ChannelMask::kChannelIteratorFirst;
|
||||
mScanChannelMask.SetMask((aScanChannels == 0) ? static_cast<uint32_t>(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)
|
||||
|
||||
+135
-5
@@ -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;
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <openthread/openthread.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user