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