From 4275c5826f07e0ebbebf1a7249bec56100f9c584 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Wed, 25 Oct 2023 05:36:57 +0800 Subject: [PATCH] [mac] update the cached supported channel mask when the region code changes (#9418) The current code of the `mac.cpp` caches the supported channel mask to a local variable. But the supported channel mask may be changed after the country code is changed. This will cause the supported channel mask used in `mac.cpp` to be inconsistent with the actual supported channel mask. This commit adds an API `otLinkSetRegion()` to set the region code and then update the cached supported channel mask to avoid the channel mask inconsistencies. The current Thread channel may be not included in the new supported channel mask. When the Thread stack detects this case, it detaches the current Thread network gracefully. --- include/openthread/link.h | 33 ++++++++++++++++++++ include/openthread/platform/radio.h | 3 +- src/cli/cli.cpp | 8 ++--- src/core/api/link_api.cpp | 21 +++++++++++++ src/core/mac/mac.cpp | 19 ++++++++++-- src/core/mac/mac.hpp | 30 ++++++++++++++++++ src/core/radio/radio.hpp | 30 ++++++++++++++++++ src/core/thread/mle.cpp | 12 ++++++++ tests/scripts/expect/posix-channel-mask.exp | 34 +++++++++++++++++++++ 9 files changed, 183 insertions(+), 7 deletions(-) diff --git a/include/openthread/link.h b/include/openthread/link.h index d48d5d498..f4d203e72 100644 --- a/include/openthread/link.h +++ b/include/openthread/link.h @@ -1182,6 +1182,39 @@ bool otLinkIsCslSupported(otInstance *aInstance); */ otError otLinkSendEmptyData(otInstance *aInstance); +/** + * Sets the region code. + * + * The radio region format is the 2-bytes ascii representation of the ISO 3166 alpha-2 code. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aRegionCode The radio region code. The `aRegionCode >> 8` is first ascii char + * and the `aRegionCode & 0xff` is the second ascii char. + * + * @retval OT_ERROR_FAILED Other platform specific errors. + * @retval OT_ERROR_NONE Successfully set region code. + * @retval OT_ERROR_NOT_IMPLEMENTED The feature is not implemented. + * + */ +otError otLinkSetRegion(otInstance *aInstance, uint16_t aRegionCode); + +/** + * Get the region code. + * + * The radio region format is the 2-bytes ascii representation of the ISO 3166 alpha-2 code. + + * @param[in] aInstance The OpenThread instance structure. + * @param[out] aRegionCode The radio region code. The `aRegionCode >> 8` is first ascii char + * and the `aRegionCode & 0xff` is the second ascii char. + * + * @retval OT_ERROR_INVALID_ARGS @p aRegionCode is nullptr. + * @retval OT_ERROR_FAILED Other platform specific errors. + * @retval OT_ERROR_NONE Successfully got region code. + * @retval OT_ERROR_NOT_IMPLEMENTED The feature is not implemented. + * + */ +otError otLinkGetRegion(otInstance *aInstance, uint16_t *aRegionCode); + /** * @} * diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index d11549e07..7f39d9832 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -1186,7 +1186,8 @@ otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aCh * ISO 3166 alpha-2 code. * * @param[in] aInstance The OpenThread instance structure. - * @param[in] aRegionCode The radio region. + * @param[in] aRegionCode The radio region code. The `aRegionCode >> 8` is first ascii char + * and the `aRegionCode & 0xff` is the second ascii char. * * @retval OT_ERROR_FAILED Other platform specific errors. * @retval OT_ERROR_NONE Successfully set region code. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index b4cad097a..28a9ea1f3 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -6658,7 +6658,7 @@ exit: * Done * @endcode * @par api_copy - * #otPlatRadioGetRegion + * #otLinkGetRegion */ template <> otError Interpreter::Process(Arg aArgs[]) { @@ -6667,7 +6667,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) if (aArgs[0].IsEmpty()) { - SuccessOrExit(error = otPlatRadioGetRegion(GetInstancePtr(), ®ionCode)); + SuccessOrExit(error = otLinkGetRegion(GetInstancePtr(), ®ionCode)); OutputLine("%c%c", regionCode >> 8, regionCode & 0xff); } /** @@ -6677,7 +6677,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) * Done * @endcode * @par api_copy - * #otPlatRadioSetRegion + * #otLinkSetRegion * @par * Changing this can affect the transmit power limit. */ @@ -6688,7 +6688,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) regionCode = static_cast(static_cast(aArgs[0].GetCString()[0]) << 8) + static_cast(aArgs[0].GetCString()[1]); - error = otPlatRadioSetRegion(GetInstancePtr(), regionCode); + error = otLinkSetRegion(GetInstancePtr(), regionCode); } exit: diff --git a/src/core/api/link_api.cpp b/src/core/api/link_api.cpp index f342627a9..95ec35c42 100644 --- a/src/core/api/link_api.cpp +++ b/src/core/api/link_api.cpp @@ -460,3 +460,24 @@ otError otLinkSendEmptyData(otInstance *aInstance) return AsCoreType(aInstance).Get().SendEmptyMessage(); } #endif + +otError otLinkSetRegion(otInstance *aInstance, uint16_t aRegionCode) +{ + return AsCoreType(aInstance).Get().SetRegion(aRegionCode); +} + +otError otLinkGetRegion(otInstance *aInstance, uint16_t *aRegionCode) +{ + Error error; + + if (aRegionCode == nullptr) + { + error = kErrorInvalidArgs; + } + else + { + error = AsCoreType(aInstance).Get().GetRegion(*aRegionCode); + } + + return error; +} diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index d48228697..8dfbecc71 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -187,7 +187,7 @@ void Mac::Scan(Operation aScanOperation, uint32_t aScanChannels, uint16_t aScanD if (aScanChannels == 0) { - aScanChannels = GetSupportedChannelMask().GetMask(); + aScanChannels = mSupportedChannelMask.GetMask(); } mScanChannelMask.SetMask(aScanChannels); @@ -474,7 +474,7 @@ void Mac::SetSupportedChannelMask(const ChannelMask &aMask) { ChannelMask newMask = aMask; - newMask.Intersect(ChannelMask(Get().GetSupportedChannelMask())); + newMask.Intersect(mSupportedChannelMask); IgnoreError(Get().Update(mSupportedChannelMask, newMask, kEventSupportedChannelMaskChanged)); } @@ -2105,6 +2105,21 @@ void Mac::SetPromiscuous(bool aPromiscuous) UpdateIdleMode(); } +Error Mac::SetRegion(uint16_t aRegionCode) +{ + Error error; + ChannelMask oldMask = mSupportedChannelMask; + + SuccessOrExit(error = Get().SetRegion(aRegionCode)); + mSupportedChannelMask.SetMask(Get().GetSupportedChannelMask()); + IgnoreError(Get().Update(oldMask, mSupportedChannelMask, kEventSupportedChannelMaskChanged)); + +exit: + return error; +} + +Error Mac::GetRegion(uint16_t &aRegionCode) const { return Get().GetRegion(aRegionCode); } + #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE const uint32_t *Mac::GetDirectRetrySuccessHistogram(uint8_t &aNumberOfEntries) { diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index e14d436c8..1b2ae6c67 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -720,6 +720,36 @@ public: bool IsRadioFilterEnabled(void) const { return mLinks.GetSubMac().IsRadioFilterEnabled(); } #endif + /** + * Sets the region code. + * + * The radio region format is the 2-bytes ascii representation of the ISO 3166 alpha-2 code. + * + * @param[in] aRegionCode The radio region code. The `aRegionCode >> 8` is first ascii char + * and the `aRegionCode & 0xff` is the second ascii char. + * + * @retval kErrorFailed Other platform specific errors. + * @retval kErrorNone Successfully set region code. + * @retval kErrorNotImplemented The feature is not implemented. + * + */ + Error SetRegion(uint16_t aRegionCode); + + /** + * Get the region code. + * + * The radio region format is the 2-bytes ascii representation of the ISO 3166 alpha-2 code. + * + * @param[out] aRegionCode The radio region code. The `aRegionCode >> 8` is first ascii char + * and the `aRegionCode & 0xff` is the second ascii char. + * + * @retval kErrorFailed Other platform specific errors. + * @retval kErrorNone Successfully set region code. + * @retval kErrorNotImplemented The feature is not implemented. + * + */ + Error GetRegion(uint16_t &aRegionCode) const; + private: static constexpr uint16_t kMaxCcaSampleCount = OPENTHREAD_CONFIG_CCA_FAILURE_RATE_AVERAGING_WINDOW; diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index a8d36a1a5..892e57919 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -733,6 +733,36 @@ public: ((kChannelMin == aCslChannel) || ((kChannelMin < aCslChannel) && (aCslChannel <= kChannelMax)))); } + /** + * Sets the region code. + * + * The radio region format is the 2-bytes ascii representation of the ISO 3166 alpha-2 code. + * + * @param[in] aRegionCode The radio region code. The `aRegionCode >> 8` is first ascii char + * and the `aRegionCode & 0xff` is the second ascii char. + * + * @retval kErrorFailed Other platform specific errors. + * @retval kErrorNone Successfully set region code. + * @retval kErrorNotImplemented The feature is not implemented. + * + */ + Error SetRegion(uint16_t aRegionCode) { return otPlatRadioSetRegion(GetInstancePtr(), aRegionCode); } + + /** + * Get the region code. + * + * The radio region format is the 2-bytes ascii representation of the ISO 3166 alpha-2 code. + * + * @param[out] aRegionCode The radio region code. The `aRegionCode >> 8` is first ascii char + * and the `aRegionCode & 0xff` is the second ascii char. + * + * @retval kErrorFailed Other platform specific errors. + * @retval kErrorNone Successfully set region code. + * @retval kErrorNotImplemented The feature is not implemented. + * + */ + Error GetRegion(uint16_t &aRegionCode) const { return otPlatRadioGetRegion(GetInstancePtr(), &aRegionCode); } + private: otInstance *GetInstancePtr(void) const { return reinterpret_cast(&InstanceLocator::GetInstance()); } diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 9591e4593..72bbe7f21 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -1202,6 +1202,18 @@ void Mle::HandleNotifierEvents(Events aEvents) } #endif + if (aEvents.Contains(kEventSupportedChannelMaskChanged)) + { + Mac::ChannelMask channelMask = Get().GetSupportedChannelMask(); + + if (!channelMask.ContainsChannel(Get().GetPanChannel()) && (mRole != kRoleDisabled)) + { + LogWarn("Channel %u is not in the supported channel mask %s, detach the network gracefully!", + Get().GetPanChannel(), channelMask.ToString().AsCString()); + IgnoreError(DetachGracefully(nullptr, nullptr)); + } + } + exit: return; } diff --git a/tests/scripts/expect/posix-channel-mask.exp b/tests/scripts/expect/posix-channel-mask.exp index 6e387d802..d754feaa4 100644 --- a/tests/scripts/expect/posix-channel-mask.exp +++ b/tests/scripts/expect/posix-channel-mask.exp @@ -61,4 +61,38 @@ send "channel preferred\n" expect "0x1fff800" expect_line "Done" +send "region US\n" +expect_line "Done" + +send "channel supported\n" +expect "0x7fff800" +expect_line "Done" + +send "dataset init new\n" +expect_line "Done" +send "dataset channel 26\n" +expect_line "Done" +send "dataset commit active\n" +expect_line "Done" + +attach "leader" + +send "channel\n" +expect "26" +expect_line "Done" + +send "region WW\n" +expect_line "Done" + +wait_for "state" "disabled" +expect_line "Done" + +send "channel supported\n" +expect "0x3fff800" +expect_line "Done" + +send "channel\n" +expect "26" +expect_line "Done" + dispose_node 1