[posix] add power calibration support (#8293)

The actual output power of the Thread device may be determined by both
the Thread radio chip and the FEM. Consider the output power error of
the Thread radio chip and the gain error of the FEM, the actual output
power is inconsistent with the expected output power.  To guarantee
that the actual output power is accurate and meet the regulatory
requirements, the output power should be calibrated in the factory if
the output power is not accurate.

This commit contains the following changes:

- Adds a power calibration implementation
- Adds a config file to configure the target power for different
  countries
- Adds a tool for the factory to persist the power calibration data
This commit is contained in:
Zhanglong Xia
2022-12-22 02:15:48 +08:00
committed by GitHub
parent 9af0bfa60e
commit 8b59f4d31e
44 changed files with 2934 additions and 4 deletions
+4
View File
@@ -65,6 +65,10 @@ jobs:
ulimit -c unlimited
./script/test prepare_coredump_upload
OT_OPTIONS='-DOT_READLINE=OFF -DOT_FULL_LOGS=ON -DOT_LOG_OUTPUT=PLATFORM_DEFINED' VIRTUAL_TIME=0 OT_NODE_TYPE=rcp ./script/test build expect
- name: Run ot-fct
run: |
OT_CMAKE_NINJA_TARGET="ot-fct" script/cmake-build posix
tests/scripts/expect/ot-fct.exp
- name: Check Crash
if: ${{ failure() }}
run: |
+41
View File
@@ -373,6 +373,7 @@ LOCAL_SRC_FILES := \
src/core/utils/otns.cpp \
src/core/utils/parse_cmdline.cpp \
src/core/utils/ping_sender.cpp \
src/core/utils/power_calibration.cpp \
src/core/utils/slaac_address.cpp \
src/core/utils/srp_client_buffers.cpp \
src/lib/hdlc/hdlc.cpp \
@@ -384,6 +385,7 @@ LOCAL_SRC_FILES := \
src/posix/platform/alarm.cpp \
src/posix/platform/backbone.cpp \
src/posix/platform/backtrace.cpp \
src/posix/platform/config_file.cpp \
src/posix/platform/daemon.cpp \
src/posix/platform/entropy.cpp \
src/posix/platform/firewall.cpp \
@@ -395,6 +397,8 @@ LOCAL_SRC_FILES := \
src/posix/platform/misc.cpp \
src/posix/platform/multicast_routing.cpp \
src/posix/platform/netif.cpp \
src/posix/platform/power.cpp \
src/posix/platform/power_updater.cpp \
src/posix/platform/radio.cpp \
src/posix/platform/radio_url.cpp \
src/posix/platform/settings.cpp \
@@ -656,6 +660,43 @@ LOCAL_SRC_FILES := src/posix/client.cpp
include $(BUILD_EXECUTABLE)
endif # ($(USE_OTBR_DAEMON), 1)
include $(CLEAR_VARS)
LOCAL_MODULE := ot-fct
LOCAL_MODULE_TAGS := eng
LOCAL_CPPFLAGS := \
-std=c++11 \
-pedantic-errors \
$(NULL)
LOCAL_CFLAGS := \
$(OPENTHREAD_PUBLIC_CFLAGS) \
$(OPENTHREAD_PRIVATE_CFLAGS) \
$(OPENTHREAD_PROJECT_CFLAGS) \
$(NULL)
LOCAL_C_INCLUDES := \
$(OPENTHREAD_PROJECT_INCLUDES) \
$(LOCAL_PATH)/include \
$(LOCAL_PATH)/src/ \
$(LOCAL_PATH)/src/core \
$(LOCAL_PATH)/src/posix/platform \
$(NULL)
LOCAL_SRC_FILES := \
src/core/common/string.cpp \
src/core/utils/parse_cmdline.cpp \
src/lib/platform/exit_code.c \
src/posix/platform/config_file.cpp \
src/posix/platform/power.cpp \
tools/ot-fct/cli.cpp \
tools/ot-fct/logging.cpp \
tools/ot-fct/main.cpp \
$(NULL)
include $(BUILD_EXECUTABLE)
ifneq ($(OPENTHREAD_PROJECT_ANDROID_MK),)
include $(OPENTHREAD_PROJECT_ANDROID_MK)
endif
+1
View File
@@ -209,6 +209,7 @@ if(OT_PLATFORM STREQUAL "simulation")
endif()
add_subdirectory(tests)
add_subdirectory(tools)
add_custom_target(print-ot-config ALL
COMMAND ${CMAKE_COMMAND}
@@ -265,4 +265,24 @@
#define OPENTHREAD_CONFIG_DETERMINISTIC_ECDSA_ENABLE 1
#endif
/**
* @def OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE
*
* Define as 1 to enable power calibration support.
*
*/
#ifndef OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE
#define OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE 1
#endif
/**
* @def OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
*
* Define as 1 to enable platform power calibration support.
*
*/
#ifndef OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
#define OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE 1
#endif
#endif // OPENTHREAD_CORE_SIMULATION_CONFIG_H_
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (270)
#define OPENTHREAD_API_VERSION (271)
/**
* @addtogroup api-instance
+94
View File
@@ -1144,6 +1144,100 @@ otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance,
otShortAddress aShortAddress,
const otExtAddress *aExtAddress);
/**
* Add a calibrated power of the specified channel to the power calibration table.
*
* @note This API is an optional radio platform API. It's up to the platform layer to implement it.
*
* The @p aActualPower is the actual measured output power when the parameters of the radio hardware modules
* are set to the @p aRawPowerSetting.
*
* The raw power setting is an opaque byte array. OpenThread doesn't define the format of the raw power setting.
* Its format is radio hardware related and it should be defined by the developers in the platform radio driver.
* For example, if the radio hardware contains both the radio chip and the FEM chip, the raw power setting can be
* a combination of the radio power register and the FEM gain value.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aChannel The radio channel.
* @param[in] aActualPower The actual power in 0.01dBm.
* @param[in] aRawPowerSetting A pointer to the raw power setting byte array.
* @param[in] aRawPowerSettingLength The length of the @p aRawPowerSetting.
*
* @retval OT_ERROR_NONE Successfully added the calibrated power to the power calibration table.
* @retval OT_ERROR_NO_BUFS No available entry in the power calibration table.
* @retval OT_ERROR_INVALID_ARGS The @p aChannel, @p aActualPower or @p aRawPowerSetting is invalid or the
* @p aActualPower already exists in the power calibration table.
* @retval OT_ERROR_NOT_IMPLEMENTED This feature is not implemented.
*
*/
otError otPlatRadioAddCalibratedPower(otInstance *aInstance,
uint8_t aChannel,
int16_t aActualPower,
const uint8_t *aRawPowerSetting,
uint16_t aRawPowerSettingLength);
/**
* Clear all calibrated powers from the power calibration table.
*
* @note This API is an optional radio platform API. It's up to the platform layer to implement it.
*
* @param[in] aInstance The OpenThread instance structure.
*
* @retval OT_ERROR_NONE Successfully cleared all calibrated powers from the power calibration table.
* @retval OT_ERROR_NOT_IMPLEMENTED This feature is not implemented.
*
*/
otError otPlatRadioClearCalibratedPowers(otInstance *aInstance);
/**
* Set the target power for the given channel.
*
* @note This API is an optional radio platform API. It's up to the platform layer to implement it.
* If this API is implemented, the function `otPlatRadioSetTransmitPower()` should be disabled.
*
* The radio driver should set the actual output power to be less than or equal to the target power and as close
* as possible to the target power.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aChannel The radio channel.
* @param[in] aTargetPower The target power in 0.01dBm. Passing `INT16_MAX` will disable this channel to use the
* target power.
*
* @retval OT_ERROR_NONE Successfully set the target power.
* @retval OT_ERROR_INVALID_ARGS The @p aChannel or @p aTargetPower is invalid.
* @retval OT_ERROR_NOT_IMPLEMENTED The feature is not implemented.
*
*/
otError otPlatRadioSetChannelTargetPower(otInstance *aInstance, uint8_t aChannel, int16_t aTargetPower);
/**
* Get the raw power setting for the given channel.
*
* @note OpenThread `src/core/utils` implements a default implementation of the API `otPlatRadioAddCalibratedPower()`,
* `otPlatRadioClearCalibratedPowers()` and `otPlatRadioSetChannelTargetPower()`. This API is provided by
* the default implementation to get the raw power setting for the given channel. If the platform doesn't
* use the default implementation, it can ignore this API.
*
* Platform radio layer should parse the raw power setting based on the radio layer defined format and set the
* parameters of each radio hardware module.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aChannel The radio channel.
* @param[out] aRawPowerSetting A pointer to the raw power setting byte array.
* @param[in,out] aRawPowerSettingLength On input, a pointer to the size of @p aRawPowerSetting.
* On output, a pointer to the length of the raw power setting data.
*
* @retval OT_ERROR_NONE Successfully got the target power.
* @retval OT_ERROR_INVALID_ARGS The @p aChannel is invalid, @p aRawPowerSetting or @p aRawPowerSettingLength is NULL
* or @aRawPowerSettingLength is too short.
* @retval OT_ERROR_NOT_FOUND The raw power setting for the @p aChannel was not found.
*
*/
extern otError otPlatRadioGetRawPowerSetting(otInstance *aInstance,
uint8_t aChannel,
uint8_t *aRawPowerSetting,
uint16_t *aRawPowerSettingLength);
/**
* @}
*
+4
View File
@@ -706,6 +706,8 @@ openthread_core_files = [
"utils/parse_cmdline.hpp",
"utils/ping_sender.cpp",
"utils/ping_sender.hpp",
"utils/power_calibration.cpp",
"utils/power_calibration.hpp",
"utils/slaac_address.cpp",
"utils/slaac_address.hpp",
"utils/srp_client_buffers.cpp",
@@ -745,6 +747,7 @@ openthread_radio_sources = [
"radio/radio_platform.cpp",
"thread/link_quality.cpp",
"utils/parse_cmdline.cpp",
"utils/power_calibration.cpp",
]
header_pattern = [
@@ -791,6 +794,7 @@ source_set("libopenthread_core_config") {
"config/parent_search.h",
"config/ping_sender.h",
"config/platform.h",
"config/power_calibration.h",
"config/radio_link.h",
"config/sntp_client.h",
"config/srp_client.h",
+2
View File
@@ -240,6 +240,7 @@ set(COMMON_SOURCES
utils/otns.cpp
utils/parse_cmdline.cpp
utils/ping_sender.cpp
utils/power_calibration.cpp
utils/slaac_address.cpp
utils/srp_client_buffers.cpp
)
@@ -276,6 +277,7 @@ set(RADIO_COMMON_SOURCES
radio/radio_platform.cpp
thread/link_quality.cpp
utils/parse_cmdline.cpp
utils/power_calibration.cpp
)
set(OT_VENDOR_EXTENSION "" CACHE STRING "specify a C++ source file built as part of OpenThread core library")
+4
View File
@@ -330,6 +330,7 @@ SOURCES_COMMON = \
utils/otns.cpp \
utils/parse_cmdline.cpp \
utils/ping_sender.cpp \
utils/power_calibration.cpp \
utils/slaac_address.cpp \
utils/srp_client_buffers.cpp \
$(NULL)
@@ -366,6 +367,7 @@ RADIO_SOURCES_COMMON = \
radio/radio_platform.cpp \
thread/link_quality.cpp \
utils/parse_cmdline.cpp \
utils/power_calibration.cpp \
$(NULL)
EXTRA_DIST = \
@@ -514,6 +516,7 @@ HEADERS_COMMON = \
config/parent_search.h \
config/ping_sender.h \
config/platform.h \
config/power_calibration.h \
config/radio_link.h \
config/sntp_client.h \
config/srp_client.h \
@@ -649,6 +652,7 @@ HEADERS_COMMON = \
utils/otns.hpp \
utils/parse_cmdline.hpp \
utils/ping_sender.hpp \
utils/power_calibration.hpp \
utils/slaac_address.hpp \
utils/srp_client_buffers.hpp \
$(NULL)
+3
View File
@@ -236,6 +236,9 @@ Instance::Instance(void)
#endif
#if OPENTHREAD_CONFIG_DIAG_ENABLE
, mDiags(*this)
#endif
#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
, mPowerCalibration(*this)
#endif
, mIsInitialized(false)
{
+8
View File
@@ -60,6 +60,7 @@
#include "mac/link_raw.hpp"
#include "radio/radio.hpp"
#include "utils/otns.hpp"
#include "utils/power_calibration.hpp"
#if OPENTHREAD_FTD || OPENTHREAD_MTD
#include "backbone_router/backbone_tmf.hpp"
@@ -618,6 +619,9 @@ private:
#if OPENTHREAD_CONFIG_DIAG_ENABLE
FactoryDiags::Diags mDiags;
#endif
#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
Utils::PowerCalibration mPowerCalibration;
#endif
bool mIsInitialized;
@@ -960,6 +964,10 @@ template <> inline Extension::ExtensionBase &Instance::Get(void) { return mExten
template <> inline FactoryDiags::Diags &Instance::Get(void) { return mDiags; }
#endif
#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
template <> inline Utils::PowerCalibration &Instance::Get(void) { return mPowerCalibration; }
#endif
/**
* @}
*
+10
View File
@@ -158,6 +158,16 @@
#define OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
*
* Define as 1 to enable platform power calibration support.
*
*/
#ifndef OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
#define OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE 0
#endif
#if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT
#if (!defined(OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE) || \
!defined(OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN) || \
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2022, 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 compile-time configurations for power calibration module.
*
*/
#ifndef CONFIG_POWER_CALIBRATION_H_
#define CONFIG_POWER_CALIBRATION_H_
/**
* @def OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE
*
* Define as 1 to enable power calibration support.
*
*/
#ifndef OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE
#define OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE
*
* The size of the raw power setting byte array.
*
*/
#ifndef OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE
#define OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE 16
#endif
/**
* @def OPENTHREAD_CONFIG_POWER_CALIBRATION_NUM_CALIBRATED_POWER_ENTRIES
*
* The number of the calibrated power entries for each channel.
*
*/
#ifndef OPENTHREAD_CONFIG_POWER_CALIBRATION_NUM_CALIBRATED_POWER_ENTRIES
#define OPENTHREAD_CONFIG_POWER_CALIBRATION_NUM_CALIBRATED_POWER_ENTRIES 6
#endif
#endif // CONFIG_POWER_CALIBRATION_H_
+1
View File
@@ -89,6 +89,7 @@
#include "config/parent_search.h"
#include "config/ping_sender.h"
#include "config/platform.h"
#include "config/power_calibration.h"
#include "config/radio_link.h"
#include "config/sntp_client.h"
#include "config/srp_client.h"
+214
View File
@@ -0,0 +1,214 @@
/*
* Copyright (c) 2022, 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.
*/
#include "power_calibration.hpp"
#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
#include "common/as_core_type.hpp"
#include "common/code_utils.hpp"
#include "common/locator_getters.hpp"
namespace ot {
namespace Utils {
PowerCalibration::PowerCalibration(Instance &aInstance)
: InstanceLocator(aInstance)
, mLastChannel(0)
, mCalibratedPowerIndex(kInvalidIndex)
{
for (int16_t &targetPower : mTargetPowerTable)
{
targetPower = kInvalidPower;
}
}
void PowerCalibration::CalibratedPowerEntry::Init(int16_t aActualPower,
const uint8_t *aRawPowerSetting,
uint16_t aRawPowerSettingLength)
{
AssertPointerIsNotNull(aRawPowerSetting);
OT_ASSERT(aRawPowerSettingLength <= kMaxRawPowerSettingSize);
mActualPower = aActualPower;
mLength = aRawPowerSettingLength;
memcpy(mSettings, aRawPowerSetting, aRawPowerSettingLength);
}
Error PowerCalibration::CalibratedPowerEntry::GetRawPowerSetting(uint8_t *aRawPowerSetting,
uint16_t *aRawPowerSettingLength)
{
Error error = kErrorNone;
AssertPointerIsNotNull(aRawPowerSetting);
AssertPointerIsNotNull(aRawPowerSettingLength);
VerifyOrExit(*aRawPowerSettingLength >= mLength, error = kErrorInvalidArgs);
memcpy(aRawPowerSetting, mSettings, mLength);
*aRawPowerSettingLength = mLength;
exit:
return error;
}
Error PowerCalibration::AddCalibratedPower(uint8_t aChannel,
int16_t aActualPower,
const uint8_t *aRawPowerSetting,
uint16_t aRawPowerSettingLength)
{
Error error = kErrorNone;
CalibratedPowerEntry entry;
uint8_t chIndex;
AssertPointerIsNotNull(aRawPowerSetting);
VerifyOrExit(IsChannelValid(aChannel) && aRawPowerSettingLength <= CalibratedPowerEntry::kMaxRawPowerSettingSize,
error = kErrorInvalidArgs);
chIndex = aChannel - Radio::kChannelMin;
VerifyOrExit(!mCalibratedPowerTables[chIndex].ContainsMatching(aActualPower), error = kErrorInvalidArgs);
VerifyOrExit(!mCalibratedPowerTables[chIndex].IsFull(), error = kErrorNoBufs);
entry.Init(aActualPower, aRawPowerSetting, aRawPowerSettingLength);
SuccessOrExit(error = mCalibratedPowerTables[chIndex].PushBack(entry));
if (aChannel == mLastChannel)
{
mCalibratedPowerIndex = kInvalidIndex;
}
exit:
return error;
}
void PowerCalibration::ClearCalibratedPowers(void)
{
for (CalibratedPowerTable &table : mCalibratedPowerTables)
{
table.Clear();
}
mCalibratedPowerIndex = kInvalidIndex;
}
Error PowerCalibration::SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower)
{
Error error = kErrorNone;
VerifyOrExit(IsChannelValid(aChannel), error = kErrorInvalidArgs);
mTargetPowerTable[aChannel - Radio::kChannelMin] = aTargetPower;
if (aChannel == mLastChannel)
{
mCalibratedPowerIndex = kInvalidIndex;
}
exit:
return error;
}
Error PowerCalibration::GetRawPowerSetting(uint8_t aChannel,
uint8_t *aRawPowerSetting,
uint16_t *aRawPowerSettingLength)
{
Error error = kErrorNone;
uint8_t chIndex;
uint8_t powerIndex = kInvalidIndex;
int16_t foundPower = kInvalidPower;
int16_t targetPower;
int16_t actualPower;
VerifyOrExit(IsChannelValid(aChannel), error = kErrorInvalidArgs);
VerifyOrExit((mLastChannel != aChannel) || IsPowerUpdated());
chIndex = aChannel - Radio::kChannelMin;
targetPower = mTargetPowerTable[chIndex];
VerifyOrExit(targetPower != kInvalidPower, error = kErrorNotFound);
for (uint8_t i = 0; i < mCalibratedPowerTables[chIndex].GetLength(); i++)
{
actualPower = mCalibratedPowerTables[chIndex][i].GetActualPower();
if ((actualPower <= targetPower) && ((foundPower == kInvalidPower) || (foundPower <= actualPower)))
{
foundPower = actualPower;
powerIndex = i;
}
}
VerifyOrExit(powerIndex != kInvalidIndex, error = kErrorNotFound);
mCalibratedPowerIndex = powerIndex;
mLastChannel = aChannel;
exit:
if (error == kErrorNone)
{
error = mCalibratedPowerTables[mLastChannel - Radio::kChannelMin][mCalibratedPowerIndex].GetRawPowerSetting(
aRawPowerSetting, aRawPowerSettingLength);
}
return error;
}
} // namespace Utils
} // namespace ot
using namespace ot;
otError otPlatRadioAddCalibratedPower(otInstance *aInstance,
uint8_t aChannel,
int16_t aActualPower,
const uint8_t *aRawPowerSetting,
uint16_t aRawPowerSettingLength)
{
return AsCoreType(aInstance).Get<Utils::PowerCalibration>().AddCalibratedPower(
aChannel, aActualPower, aRawPowerSetting, aRawPowerSettingLength);
}
otError otPlatRadioClearCalibratedPowers(otInstance *aInstance)
{
AsCoreType(aInstance).Get<Utils::PowerCalibration>().ClearCalibratedPowers();
return OT_ERROR_NONE;
}
otError otPlatRadioSetChannelTargetPower(otInstance *aInstance, uint8_t aChannel, int16_t aTargetPower)
{
return AsCoreType(aInstance).Get<Utils::PowerCalibration>().SetChannelTargetPower(aChannel, aTargetPower);
}
otError otPlatRadioGetRawPowerSetting(otInstance *aInstance,
uint8_t aChannel,
uint8_t *aRawPowerSetting,
uint16_t *aRawPowerSettingLength)
{
AssertPointerIsNotNull(aRawPowerSetting);
AssertPointerIsNotNull(aRawPowerSettingLength);
return AsCoreType(aInstance).Get<Utils::PowerCalibration>().GetRawPowerSetting(aChannel, aRawPowerSetting,
aRawPowerSettingLength);
}
#endif // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
+168
View File
@@ -0,0 +1,168 @@
/*
* Copyright (c) 2022, 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
* @brief
* This file includes definitions for the platform power calibration module.
*
*/
#ifndef POWER_CALIBRATION_HPP_
#define POWER_CALIBRATION_HPP_
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
#include <openthread/platform/radio.h>
#include "common/array.hpp"
#include "common/numeric_limits.hpp"
#include "radio/radio.hpp"
namespace ot {
namespace Utils {
/**
* This class implements power calibration module.
*
* The power calibration module implements the radio platform power calibration APIs. It mainly stores the calibrated
* power table and the target power table, provides an API for the platform to get the raw power setting of the
* specified channel.
*
*/
class PowerCalibration : public InstanceLocator, private NonCopyable
{
public:
explicit PowerCalibration(Instance &aInstance);
/**
* Add a calibrated power of the specificed channel to the power calibration table.
*
* @param[in] aChannel The radio channel.
* @param[in] aActualPower The actual power in 0.01dBm.
* @param[in] aRawPowerSetting A pointer to the raw power setting byte array.
* @param[in] aRawPowerSettingLength The length of the @p aRawPowerSetting.
*
* @retval kErrorNone Successfully added the calibrated power to the power calibration table.
* @retval kErrorNoBufs No available entry in the power calibration table.
* @retval kErrorInvalidArgs The @p aChannel, @p aActualPower or @p aRawPowerSetting is invalid or the
* @ aActualPower already exists in the power calibration table.
*
*/
Error AddCalibratedPower(uint8_t aChannel,
int16_t aActualPower,
const uint8_t *aRawPowerSetting,
uint16_t aRawPowerSettingLength);
/**
* Clear all calibrated powers from the power calibration table.
*
*/
void ClearCalibratedPowers(void);
/**
* Set the target power for the given channel.
*
* @param[in] aChannel The radio channel.
* @param[in] aTargetPower The target power in 0.01dBm. Passing `INT16_MAX` will disable this channel.
*
* @retval kErrorNone Successfully set the target power.
* @retval kErrorInvalidArgs The @p aChannel or @p aTargetPower is invalid.
*
*/
Error SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower);
/**
* Get the raw power setting for the given channel.
*
* Platform radio layer should parse the raw power setting based on the radio layer defined format and set the
* parameters of each radio hardware module.
*
* @param[in] aChannel The radio channel.
* @param[out] aRawPowerSetting A pointer to the raw power setting byte array.
* @param[in,out] aRawPowerSettingLength On input, a pointer to the size of @p aRawPowerSetting.
* On output, a pointer to the length of the raw power setting data.
*
* @retval kErrorNone Successfully got the target power.
* @retval kErrorInvalidArgs The @p aChannel is invalid or @p aRawPowerSetting is nullptr.
* @retval kErrorNotFound The raw power setting for the @p aChannel was not found.
*
*/
Error GetRawPowerSetting(uint8_t aChannel, uint8_t *aRawPowerSetting, uint16_t *aRawPowerSettingLength);
private:
class CalibratedPowerEntry
{
public:
static constexpr uint16_t kMaxRawPowerSettingSize = OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE;
CalibratedPowerEntry(void)
: mActualPower(kInvalidPower)
, mLength(0)
{
}
void Init(int16_t aActualPower, const uint8_t *aRawPowerSetting, uint16_t aRawPowerSettingLength);
Error GetRawPowerSetting(uint8_t *aRawPowerSetting, uint16_t *aRawPowerSettingLength);
int16_t GetActualPower(void) const { return mActualPower; }
bool Matches(int16_t aActualPower) const { return aActualPower == mActualPower; }
private:
int16_t mActualPower;
uint8_t mSettings[kMaxRawPowerSettingSize];
uint16_t mLength;
};
bool IsPowerUpdated(void) const { return mCalibratedPowerIndex == kInvalidIndex; }
bool IsChannelValid(uint8_t aChannel) const
{
return ((aChannel >= Radio::kChannelMin) && (aChannel <= Radio::kChannelMax));
}
static constexpr uint8_t kInvalidIndex = NumericLimits<uint8_t>::kMax;
static constexpr uint16_t kInvalidPower = NumericLimits<int16_t>::kMax;
static constexpr uint16_t kMaxNumCalibratedPowers =
OPENTHREAD_CONFIG_POWER_CALIBRATION_NUM_CALIBRATED_POWER_ENTRIES;
static constexpr uint16_t kNumChannels = Radio::kChannelMax - Radio::kChannelMin + 1;
static_assert(kMaxNumCalibratedPowers < NumericLimits<uint8_t>::kMax,
"kMaxNumCalibratedPowers is larger than or equal to max");
typedef Array<CalibratedPowerEntry, kMaxNumCalibratedPowers> CalibratedPowerTable;
uint8_t mLastChannel;
int16_t mTargetPowerTable[kNumChannels];
uint8_t mCalibratedPowerIndex;
CalibratedPowerTable mCalibratedPowerTables[kNumChannels];
};
} // namespace Utils
} // namespace ot
#endif // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
#endif // POWER_CALIBRATION_HPP_
+49
View File
@@ -886,6 +886,55 @@ public:
*/
const otRadioSpinelMetrics *GetRadioSpinelMetrics(void) const { return &mRadioSpinelMetrics; }
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
/**
* Add a calibrated power of the specificed channel to the power calibration table.
*
* @param[in] aChannel The radio channel.
* @param[in] aActualPower The actual power in 0.01dBm.
* @param[in] aRawPowerSetting A pointer to the raw power setting byte array.
* @param[in] aRawPowerSettingLength The length of the @p aRawPowerSetting.
*
* @retval OT_ERROR_NONE Successfully added the calibrated power to the power calibration table.
* @retval OT_ERROR_NO_BUFS No available entry in the power calibration table.
* @retval OT_ERROR_INVALID_ARGS The @p aChannel, @p aActualPower or @p aRawPowerSetting is invalid.
* @retval OT_ERROR_NOT_IMPLEMENTED This feature is not implemented.
* @retval OT_ERROR_BUSY Failed due to another operation is on going.
* @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver.
*
*/
otError AddCalibratedPower(uint8_t aChannel,
int16_t aActualPower,
const uint8_t *aRawPowerSetting,
uint16_t aRawPowerSettingLength);
/**
* Clear all calibrated powers from the power calibration table.
*
* @retval OT_ERROR_NONE Successfully cleared all calibrated powers from the power calibration table.
* @retval OT_ERROR_NOT_IMPLEMENTED This feature is not implemented.
* @retval OT_ERROR_BUSY Failed due to another operation is on going.
* @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver.
*
*/
otError ClearCalibratedPowers(void);
/**
* Set the target power for the given channel.
*
* @param[in] aChannel The radio channel.
* @param[in] aTargetPower The target power in 0.01dBm. Passing `INT16_MAX` will disable this channel.
*
* @retval OT_ERROR_NONE Successfully set the target power.
* @retval OT_ERROR_INVALID_ARGS The @p aChannel or @p aTargetPower is invalid..
* @retval OT_ERROR_NOT_IMPLEMENTED The feature is not implemented.
* @retval OT_ERROR_BUSY Failed due to another operation is on going.
* @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver.
*
*/
otError SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower);
#endif
private:
enum
{
+73
View File
@@ -2516,6 +2516,43 @@ uint8_t RadioSpinel<InterfaceType, ProcessContextType>::GetCslUncertainty(void)
}
#endif
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::AddCalibratedPower(uint8_t aChannel,
int16_t aActualPower,
const uint8_t *aRawPowerSetting,
uint16_t aRawPowerSettingLength)
{
otError error;
assert(aRawPowerSetting != nullptr);
SuccessOrExit(error = Insert(SPINEL_PROP_PHY_CALIBRATED_POWER,
SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_DATA_WLEN_S, aChannel,
aActualPower, aRawPowerSetting, aRawPowerSettingLength));
exit:
return error;
}
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::ClearCalibratedPowers(void)
{
return Set(SPINEL_PROP_PHY_CALIBRATED_POWER, nullptr);
}
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aChannel >= Radio::kChannelMin && aChannel <= Radio::kChannelMax, error = OT_ERROR_INVALID_ARGS);
error =
Set(SPINEL_PROP_PHY_CHAN_TARGET_POWER, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, aChannel, aTargetPower);
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
template <typename InterfaceType, typename ProcessContextType>
uint32_t RadioSpinel<InterfaceType, ProcessContextType>::Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...)
{
@@ -3090,6 +3127,42 @@ void RadioSpinel<InterfaceType, ProcessContextType>::LogSpinelFrame(const uint8_
m8[2], m8[3], m8[4], m8[5], m8[6], m8[7], flags);
}
break;
case SPINEL_PROP_PHY_CALIBRATED_POWER:
{
if (cmd == SPINEL_CMD_PROP_VALUE_INSERT)
{
uint8_t channel;
int16_t actualPower;
uint8_t *rawPowerSetting;
unsigned int rawPowerSettingLength;
unpacked = spinel_datatype_unpack(
data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_DATA_WLEN_S, &channel,
&actualPower, &rawPowerSetting, &rawPowerSettingLength);
VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
start += Snprintf(start, static_cast<uint32_t>(end - start),
", ch:%u, actualPower:%d, rawPowerSetting:", channel, actualPower);
for (uint16_t i = 0; i < rawPowerSettingLength; i++)
{
start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x", rawPowerSetting[i]);
}
}
}
break;
case SPINEL_PROP_PHY_CHAN_TARGET_POWER:
{
uint8_t channel;
int16_t targetPower;
unpacked =
spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, &channel, &targetPower);
VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
start += Snprintf(start, static_cast<uint32_t>(end - start), ", ch:%u, targetPower:%d", channel, targetPower);
}
break;
}
exit:
+2
View File
@@ -1236,6 +1236,8 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key)
{SPINEL_PROP_PHY_CHAN_PREFERRED, "PHY_CHAN_PREFERRED"},
{SPINEL_PROP_PHY_CHAN_MAX_POWER, "PHY_CHAN_MAX_POWER"},
{SPINEL_PROP_PHY_REGION_CODE, "PHY_REGION_CODE"},
{SPINEL_PROP_PHY_CALIBRATED_POWER, "PHY_CALIBRATED_POWER"},
{SPINEL_PROP_PHY_CHAN_TARGET_POWER, "PHY_CHAN_TARGET_POWER"},
{SPINEL_PROP_JAM_DETECT_ENABLE, "JAM_DETECT_ENABLE"},
{SPINEL_PROP_JAM_DETECTED, "JAM_DETECTED"},
{SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD, "JAM_DETECT_RSSI_THRESHOLD"},
+23 -1
View File
@@ -417,7 +417,7 @@
* Please see section "Spinel definition compatibility guideline" for more details.
*
*/
#define SPINEL_RCP_API_VERSION 6
#define SPINEL_RCP_API_VERSION 7
/**
* @def SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION
@@ -1743,6 +1743,28 @@ enum
*/
SPINEL_PROP_PHY_REGION_CODE = SPINEL_PROP_PHY__BEGIN + 12,
/// Calibrated Power Table
/** Format: `A(Csd)` - Insert/Set
*
* The `Insert` command on the property inserts a calibration power entry to the calibrated power table.
* The `Set` command on the property with empty payload clears the calibrated power table.
*
* Structure Parameters:
* `C`: Channel.
* `s`: Actual power in 0.01 dBm.
* `d`: Raw power setting.
*/
SPINEL_PROP_PHY_CALIBRATED_POWER = SPINEL_PROP_PHY__BEGIN + 13,
/// Target power for a channel
/** Format: `t(Cs)` - Write only
*
* Structure Parameters:
* `C`: Channel.
* `s`: Target power in 0.01 dBm.
*/
SPINEL_PROP_PHY_CHAN_TARGET_POWER = SPINEL_PROP_PHY__BEGIN + 14,
SPINEL_PROP_PHY__END = 0x30,
SPINEL_PROP_PHY_EXT__BEGIN = 0x1200,
+38
View File
@@ -2566,6 +2566,44 @@ exit:
}
#endif
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_CHAN_TARGET_POWER>(void)
{
otError error;
uint8_t channel;
int16_t targetPower;
SuccessOrExit(error = mDecoder.ReadUint8(channel));
SuccessOrExit(error = mDecoder.ReadInt16(targetPower));
error = otPlatRadioSetChannelTargetPower(mInstance, channel, targetPower);
exit:
return error;
}
template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_PHY_CALIBRATED_POWER>(void)
{
otError error;
uint8_t channel;
int16_t actualPower;
const uint8_t *dataPtr;
uint16_t dataLen;
SuccessOrExit(error = mDecoder.ReadUint8(channel));
SuccessOrExit(error = mDecoder.ReadInt16(actualPower));
SuccessOrExit(error = mDecoder.ReadDataWithLen(dataPtr, dataLen));
error = otPlatRadioAddCalibratedPower(mInstance, channel, actualPower, dataPtr, dataLen);
exit:
return error;
}
template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_CALIBRATED_POWER>(void)
{
return otPlatRadioClearCalibratedPowers(mInstance);
}
#endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
} // namespace Ncp
} // namespace ot
+7 -1
View File
@@ -419,6 +419,10 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey)
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_PHY_FEM_LNA_GAIN),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_PHY_CHAN_MAX_POWER),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_PHY_REGION_CODE),
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_PHY_CALIBRATED_POWER),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_PHY_CHAN_TARGET_POWER),
#endif
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_SCAN_STATE),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_SCAN_MASK),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_SCAN_PERIOD),
@@ -502,7 +506,6 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey)
#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_RADIO_COEX_ENABLE),
#endif
#if OPENTHREAD_MTD || OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_ALLOWLIST),
@@ -655,6 +658,9 @@ NcpBase::PropertyHandler NcpBase::FindInsertPropertyHandler(spinel_prop_key_t aK
}
constexpr static HandlerEntry sHandlerEntries[] = {
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
OT_NCP_INSERT_HANDLER_ENTRY(SPINEL_PROP_PHY_CALIBRATED_POWER),
#endif
#if OPENTHREAD_MTD || OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
OT_NCP_INSERT_HANDLER_ENTRY(SPINEL_PROP_THREAD_ON_MESH_NETS),
+3
View File
@@ -104,6 +104,7 @@ add_library(openthread-posix
alarm.cpp
backbone.cpp
backtrace.cpp
config_file.cpp
daemon.cpp
entropy.cpp
firewall.cpp
@@ -115,6 +116,8 @@ add_library(openthread-posix
misc.cpp
multicast_routing.cpp
netif.cpp
power.cpp
power_updater.cpp
radio.cpp
radio_url.cpp
settings.cpp
+3
View File
@@ -47,6 +47,7 @@ libopenthread_posix_a_SOURCES = \
alarm.cpp \
backbone.cpp \
backtrace.cpp \
config_file.cpp \
daemon.cpp \
entropy.cpp \
firewall.cpp \
@@ -58,6 +59,8 @@ libopenthread_posix_a_SOURCES = \
misc.cpp \
multicast_routing.cpp \
netif.cpp \
power.cpp \
power_updater.cpp \
radio.cpp \
radio_url.cpp \
settings.cpp \
+215
View File
@@ -0,0 +1,215 @@
/*
* Copyright (c) 2022, 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.
*/
#include "config_file.hpp"
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "utils.hpp"
#include <openthread/logging.h>
#include "common/code_utils.hpp"
#include "lib/platform/exit_code.h"
namespace ot {
namespace Posix {
ConfigFile::ConfigFile(const char *aFilePath)
: mFilePath(aFilePath)
{
assert(mFilePath != nullptr);
VerifyOrDie(strlen(mFilePath) + strlen(kSwapSuffix) < kFileNameMaxSize, OT_EXIT_FAILURE);
}
otError ConfigFile::Get(const char *aKey, int &aIterator, char *aValue, int aValueLength)
{
otError error = OT_ERROR_NONE;
char line[kLineMaxSize + 1];
FILE *fp = nullptr;
char *ret;
long int pos;
VerifyOrExit((aKey != nullptr) && (aValue != nullptr), error = OT_ERROR_INVALID_ARGS);
VerifyOrExit((fp = fopen(mFilePath, "r")) != nullptr, error = OT_ERROR_NOT_FOUND);
VerifyOrDie(fseek(fp, aIterator, SEEK_SET) == 0, OT_EXIT_ERROR_ERRNO);
while ((ret = fgets(line, sizeof(line), fp)) != nullptr)
{
char *str;
char *key;
char *value;
// If the string exceeds the `sizeof(line) - 1`, the string will be truncated to `sizeof(line) - 1` bytes string
// by the function `fgets()`.
if (strlen(line) + 1 == sizeof(line))
{
// The line is too long.
continue;
}
// Remove comments
strtok(line, kCommentDelimiter);
if ((str = strstr(line, "=")) == nullptr)
{
continue;
}
*str = '\0';
key = line;
Strip(key);
if (strcmp(aKey, key) == 0)
{
value = str + 1;
Strip(value);
aValueLength = OT_MIN(static_cast<int>(strlen(value)), (aValueLength - 1));
memcpy(aValue, value, static_cast<size_t>(aValueLength));
aValue[aValueLength] = '\0';
break;
}
}
VerifyOrExit(ret != nullptr, error = OT_ERROR_NOT_FOUND);
VerifyOrDie((pos = ftell(fp)) >= 0, OT_EXIT_ERROR_ERRNO);
aIterator = static_cast<int>(pos);
exit:
if (fp != nullptr)
{
fclose(fp);
}
return error;
}
otError ConfigFile::Add(const char *aKey, const char *aValue)
{
otError error = OT_ERROR_NONE;
FILE *fp = nullptr;
char *path = nullptr;
char *dir;
struct stat st;
VerifyOrExit((aKey != nullptr) && (aValue != nullptr), error = OT_ERROR_INVALID_ARGS);
VerifyOrDie((path = strdup(mFilePath)) != nullptr, OT_EXIT_ERROR_ERRNO);
dir = dirname(path);
if (stat(dir, &st) == -1)
{
VerifyOrDie(mkdir(dir, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == 0, OT_EXIT_ERROR_ERRNO);
}
VerifyOrDie((fp = fopen(mFilePath, "at")) != NULL, OT_EXIT_ERROR_ERRNO);
VerifyOrDie(fprintf(fp, "%s=%s\n", aKey, aValue) > 0, OT_EXIT_ERROR_ERRNO);
exit:
if (fp != nullptr)
{
fclose(fp);
}
if (path != nullptr)
{
free(path);
}
return error;
}
otError ConfigFile::Clear(const char *aKey)
{
otError error = OT_ERROR_NONE;
char swapFile[kFileNameMaxSize];
char line[kLineMaxSize];
FILE *fp = nullptr;
FILE *fpSwap = nullptr;
VerifyOrExit(aKey != nullptr, error = OT_ERROR_INVALID_ARGS);
VerifyOrDie((fp = fopen(mFilePath, "r")) != NULL, OT_EXIT_ERROR_ERRNO);
snprintf(swapFile, sizeof(swapFile), "%s%s", mFilePath, kSwapSuffix);
VerifyOrDie((fpSwap = fopen(swapFile, "w+")) != NULL, OT_EXIT_ERROR_ERRNO);
while (fgets(line, sizeof(line), fp) != nullptr)
{
bool containsKey;
char *str1;
char *str2;
str1 = strstr(line, kCommentDelimiter);
str2 = strstr(line, aKey);
// If only the comment contains the key string, ignore it.
containsKey = (str2 != nullptr) && (str1 == nullptr || str2 < str1);
if (!containsKey)
{
fputs(line, fpSwap);
}
}
exit:
if (fp != nullptr)
{
fclose(fp);
}
if (fpSwap != nullptr)
{
fclose(fpSwap);
}
if (error == OT_ERROR_NONE)
{
VerifyOrDie(rename(swapFile, mFilePath) == 0, OT_EXIT_ERROR_ERRNO);
}
return error;
}
void ConfigFile::Strip(char *aString)
{
int count = 0;
for (int i = 0; aString[i]; i++)
{
if (aString[i] != ' ' && aString[i] != '\r' && aString[i] != '\n')
{
aString[count++] = aString[i];
}
}
aString[count] = '\0';
}
} // namespace Posix
} // namespace ot
+107
View File
@@ -0,0 +1,107 @@
/*
* Copyright (c) 2022, 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.
*/
#ifndef POSIX_PLATFORM_CONFIG_FILE_HPP_
#define POSIX_PLATFORM_CONFIG_FILE_HPP_
#include <assert.h>
#include <stdint.h>
#include <openthread/error.h>
namespace ot {
namespace Posix {
/**
* This class provides read/write/clear methods for key/value configuration files.
*
*/
class ConfigFile
{
public:
/**
* This method initializes the configuration file path.
*
* @param[in] aFilePath A pointer to the null-terminated file path.
*
*/
explicit ConfigFile(const char *aFilePath);
/**
* This method gets a configuration from the configuration file.
*
* @param[in] aKey The key string associated with the requested configuration.
* @param[in,out] aIterator A reference to an iterator. MUST be initialized to 0 or the behavior is undefined.
* @param[out] aValue A pointer to where the new value string of the configuration should be read from.
* The @p aValue string will be terminated with `\0` if this method returns success.
* @param[in] aValueLength The max length of the data pointed to by @p aValue.
*
* @retval OT_ERROR_NONE The given configuration was found and fetched successfully.
* @retval OT_ERROR_NOT_FOUND The given key or iterator was not found in the configuration file.
* @retval OT_ERROR_INVALID_ARGS If @p aKey or @p aValue was NULL.
*
*/
otError Get(const char *aKey, int &aIterator, char *aValue, int aValueLength);
/**
* This method adds a configuration to the configuration file.
*
* @param[in] aKey The key string associated with the requested configuration.
* @param[in] aValue A pointer to where the new value string of the configuration should be written.
*
* @retval OT_ERROR_NONE The given key was found and removed successfully.
* @retval OT_ERROR_INVALID_ARGS If @p aKey or @p aValue was NULL.
*
*/
otError Add(const char *aKey, const char *aValue);
/**
* This function removes all configurations with the same key string from the configuration file.
*
* @param[in] aKey The key string associated with the requested configuration.
*
* @retval OT_ERROR_NONE The given key was found and removed successfully.
* @retval OT_ERROR_INVALID_ARGS If @p aKey was NULL.
*
*/
otError Clear(const char *aKey);
private:
const char *kCommentDelimiter = "#";
const char *kSwapSuffix = ".swap";
static constexpr uint16_t kLineMaxSize = 512;
static constexpr uint16_t kFileNameMaxSize = 255;
void Strip(char *aString);
const char *mFilePath;
};
} // namespace Posix
} // namespace ot
#endif // POSIX_PLATFORM_CONFIG_FILE_HPP_
@@ -309,4 +309,14 @@
#define OPENTHREAD_CONFIG_ASSERT_CHECK_API_POINTER_PARAM_FOR_NULL 1
#endif
/**
* @def OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
*
* Define as 1 to enable platform power calibration support.
*
*/
#ifndef OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
#define OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE 1
#endif
#endif // OPENTHREAD_CORE_POSIX_CONFIG_H_
@@ -335,4 +335,27 @@
#define OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#endif
/**
* @def OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE
*
* Define the path of the factory config file.
*
* Note: The factory config file contains the persist data that configured by the factory. And it won't be changed
* after a device firmware update OTA is done.
*
*/
#ifndef OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE
#define OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE "src/posix/platform/openthread.conf.example"
#endif
/**
* @def OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE
*
* Define the path of the product config file.
*
*/
#ifndef OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE
#define OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE "src/posix/platform/openthread.conf.example"
#endif
#endif // OPENTHREAD_PLATFORM_CONFIG_H_
@@ -0,0 +1,24 @@
#
# Sample configuration file
#
# Modify this to use your own configurations!
#
# Target power table entries.
# target_power=<RegulatoryDomain>,<ChannelStart>,<ChannelEnd>,<TargetPower>
target_power=ETSI,11,26,1000
target_power=FCC,11,14,1700
target_power=FCC,15,24,2000
target_power=FCC,25,26,1600
# Region domain mapping table entries.
# region_domain_mapping=<RegulatoryDomain>,<Region>,<Region>,...
region_domain_mapping=FCC,AU,CA,CL,CO,IN,MX,PE,TW,US
region_domain_mapping=ETSI,WW
# Power calibration table entries.
# calibrated_power=<ChannelStart>,<ChannelEnd>,<ActualPower>,<RawPowerSetting>
calibrated_power=11,25,1900,112233
calibrated_power=11,25,1000,223344
calibrated_power=26,26,1500,334455
calibrated_power=26,26,700,445566
+126
View File
@@ -0,0 +1,126 @@
/*
* Copyright (c) 2022, 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 strain 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.
*/
#include "power.hpp"
#include "common/code_utils.hpp"
#include "utils/parse_cmdline.hpp"
namespace ot {
namespace Power {
otError Domain::Set(const char *aDomain)
{
otError error = OT_ERROR_NONE;
uint16_t length = static_cast<uint16_t>(strlen(aDomain));
VerifyOrExit(length <= kDomainSize, error = OT_ERROR_INVALID_ARGS);
memcpy(m8, aDomain, length);
m8[length] = '\0';
exit:
return error;
}
otError TargetPower::FromString(char *aString)
{
otError error = OT_ERROR_NONE;
char *str;
VerifyOrExit((str = strtok(aString, ",")) != nullptr, error = OT_ERROR_PARSE);
SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(str, mChannelStart));
VerifyOrExit((str = strtok(nullptr, ",")) != nullptr, error = OT_ERROR_PARSE);
SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(str, mChannelEnd));
VerifyOrExit((str = strtok(nullptr, ",")) != nullptr, error = OT_ERROR_PARSE);
SuccessOrExit(error = Utils::CmdLineParser::ParseAsInt16(str, mTargetPower));
exit:
return error;
}
TargetPower::InfoString TargetPower::ToString(void) const
{
InfoString string;
string.Append("%u,%u,%d", mChannelStart, mChannelEnd, mTargetPower);
return string;
}
otError RawPowerSetting::Set(const char *aRawPowerSetting)
{
otError error;
uint16_t length = sizeof(mData);
SuccessOrExit(error = ot::Utils::CmdLineParser::ParseAsHexString(aRawPowerSetting, length, mData));
mLength = static_cast<uint8_t>(length);
exit:
return error;
}
RawPowerSetting::InfoString RawPowerSetting::ToString(void) const
{
InfoString string;
string.AppendHexBytes(mData, mLength);
return string;
}
otError CalibratedPower::FromString(char *aString)
{
otError error = OT_ERROR_NONE;
char *str;
char *pSave;
VerifyOrExit((str = strtok_r(aString, ",", &pSave)) != nullptr, error = OT_ERROR_PARSE);
SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(str, mChannelStart));
VerifyOrExit((str = strtok_r(nullptr, ",", &pSave)) != nullptr, error = OT_ERROR_PARSE);
SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(str, mChannelEnd));
VerifyOrExit((str = strtok_r(nullptr, ",", &pSave)) != nullptr, error = OT_ERROR_PARSE);
SuccessOrExit(error = Utils::CmdLineParser::ParseAsInt16(str, mActualPower));
SuccessOrExit(error = mRawPowerSetting.Set(pSave));
exit:
return error;
}
CalibratedPower::InfoString CalibratedPower::ToString(void) const
{
InfoString string;
string.Append("%u,%u,%d,%s", mChannelStart, mChannelEnd, mActualPower, mRawPowerSetting.ToString().AsCString());
return string;
}
} // namespace Power
} // namespace ot
+289
View File
@@ -0,0 +1,289 @@
/*
* Copyright (c) 2022, 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 strain 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.
*/
#ifndef POSIX_PLATFORM_POWER_H
#define POSIX_PLATFORM_POWER_H
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <openthread/error.h>
#include <openthread/platform/radio.h>
#include "common/string.hpp"
namespace ot {
namespace Power {
class Domain
{
public:
Domain(void) { m8[0] = '\0'; }
/**
* This method sets the regulatory domain from a given null terminated C string.
*
* @param[in] aDomain A regulatory domain name C string.
*
* @retval OT_ERROR_NONE Successfully set the regulatory domain.
* @retval OT_ERROR_INVALID_ARGS Given regulatory domain is too long.
*
*/
otError Set(const char *aDomain);
/**
* This method overloads operator `==` to evaluate whether or not two `Domain` instances are equal.
*
* @param[in] aOther The other `Domain` instance to compare with.
*
* @retval TRUE If the two `Domain` instances are equal.
* @retval FALSE If the two `Domain` instances not equal.
*
*/
bool operator==(const Domain &aOther) const { return strcmp(m8, aOther.m8) == 0; }
/**
* This method overloads operator `!=` to evaluate whether or not the `Domain` is unequal to a given C string.
*
* @param[in] aCString A C string to compare with. Can be `nullptr` which then returns 'TRUE'.
*
* @retval TRUE If the two regulatory domains are not equal.
* @retval FALSE If the two regulatory domains are equal.
*
*/
bool operator!=(const char *aCString) const { return (aCString == nullptr) ? true : strcmp(m8, aCString) != 0; }
/**
* This method gets the regulatory domain as a null terminated C string.
*
* @returns The regulatory domain as a null terminated C string array.
*
*/
const char *AsCString(void) const { return m8; }
private:
static constexpr uint8_t kDomainSize = 8;
char m8[kDomainSize + 1];
};
class TargetPower
{
public:
static constexpr uint16_t kInfoStringSize = 12; ///< Recommended buffer size to use with `ToString()`.
typedef String<kInfoStringSize> InfoString;
/**
* This method parses an target power string.
*
* The string MUST follow the format: "<channel_start>,<channel_end>,<target_power>".
* For example, "11,26,2000"
*
* @param[in] aString A pointer to the null-terminated string.
*
* @retval OT_ERROR_NONE Successfully parsed the target power string.
* @retval OT_ERROR_PARSE Failed to parse the target power string.
*
*/
otError FromString(char *aString);
/**
* This method returns the start channel.
*
* @returns The channel.
*
*/
uint8_t GetChannelStart(void) const { return mChannelStart; }
/**
* This method returns the end channel.
*
* @returns The channel.
*
*/
uint8_t GetChannelEnd(void) const { return mChannelEnd; }
/**
* This method returns the target power.
*
* @returns The target power, in 0.01 dBm.
*
*/
int16_t GetTargetPower(void) const { return mTargetPower; }
/**
* This method converts the target power into a human-readable string.
*
* @returns An `InfoString` object representing the target power.
*
*/
InfoString ToString(void) const;
private:
uint8_t mChannelStart;
uint8_t mChannelEnd;
int16_t mTargetPower;
};
class RawPowerSetting
{
public:
// Recommended buffer size to use with `ToString()`.
static constexpr uint16_t kInfoStringSize = OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE * 2 + 1;
typedef String<kInfoStringSize> InfoString;
/**
* This method sets the raw power setting from a given null terminated hex C string.
*
* @param[in] aRawPowerSetting A raw power setting hex C string.
*
* @retval OT_ERROR_NONE Successfully set the raw power setting.
* @retval OT_ERROR_INVALID_ARGS The given raw power setting is too long.
*
*/
otError Set(const char *aRawPowerSetting);
/**
* This method converts the raw power setting into a human-readable string.
*
* @returns An `InfoString` object representing the calibrated power.
*
*/
InfoString ToString(void) const;
const uint8_t *GetData(void) const { return mData; }
uint16_t GetLength(void) const { return mLength; }
private:
static constexpr uint16_t kMaxRawPowerSettingSize = OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE;
uint8_t mData[kMaxRawPowerSettingSize];
uint16_t mLength;
};
class CalibratedPower
{
public:
// Recommended buffer size to use with `ToString()`.
static constexpr uint16_t kInfoStringSize = 20 + RawPowerSetting::kInfoStringSize;
typedef String<kInfoStringSize> InfoString;
/**
* This method parses an calibrated power string.
*
* The string MUST follow the format: "<channel_start>,<channel_end>,<actual_power>,<raw_power_setting>".
* For example, "11,26,2000,1122aabb"
*
* @param[in] aString A pointer to the null-terminated string.
*
* @retval OT_ERROR_NONE Successfully parsed the calibrated power string.
* @retval OT_ERROR_PARSE Failed to parse the calibrated power string.
*
*/
otError FromString(char *aString);
/**
* This method returns the start channel.
*
* @returns The channel.
*
*/
uint8_t GetChannelStart(void) const { return mChannelStart; }
/**
* This method sets the start channel.
*
* @param[in] aChannelStart The start channel.
*
*/
void SetChannelStart(uint8_t aChannelStart) { mChannelStart = aChannelStart; }
/**
* This method returns the end channel.
*
* @returns The channel.
*
*/
uint8_t GetChannelEnd(void) const { return mChannelEnd; }
/**
* This method sets the end channel.
*
* @param[in] aChannelEnd The end channel.
*
*/
void SetChannelEnd(uint8_t aChannelEnd) { mChannelEnd = aChannelEnd; }
/**
* This method returns the actual power.
*
* @returns The actual measured power, in 0.01 dBm.
*
*/
int16_t GetActualPower(void) const { return mActualPower; }
/**
* This method sets the actual channel.
*
* @param[in] aActualPower The actual power in 0.01 dBm.
*
*/
void SetActualPower(int16_t aActualPower) { mActualPower = aActualPower; }
/**
* This method returns the raw power setting.
*
* @returns A reference to the raw power setting.
*
*/
const RawPowerSetting &GetRawPowerSetting(void) const { return mRawPowerSetting; }
/**
* This method sets the raw power setting.
*
* @param[in] aRawPowerSetting The raw power setting.
*
*/
void SetRawPowerSetting(const RawPowerSetting &aRawPowerSetting) { mRawPowerSetting = aRawPowerSetting; }
/**
* This method converts the calibrated power into a human-readable string.
*
* @returns An `InfoString` object representing the calibrated power.
*
*/
InfoString ToString(void) const;
private:
uint8_t mChannelStart;
uint8_t mChannelEnd;
int16_t mActualPower;
RawPowerSetting mRawPowerSetting;
};
} // namespace Power
} // namespace ot
#endif // POSIX_PLATFORM_POWER_H
+180
View File
@@ -0,0 +1,180 @@
/*
* Copyright (c) 2022, 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 strain 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.
*/
#include "power_updater.hpp"
#include "platform-posix.h"
#include <openthread/platform/radio.h>
#include "lib/platform/exit_code.h"
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
namespace ot {
namespace Posix {
otError PowerUpdater::SetRegion(uint16_t aRegionCode)
{
otError error = OT_ERROR_NONE;
int iterator = 0;
Power::Domain domain;
Power::TargetPower targetPower;
if (GetDomain(aRegionCode, domain) != OT_ERROR_NONE)
{
// If failed to find the domain for the region, use the world wide region as the default region.
VerifyOrExit(GetDomain(kRegionCodeWorldWide, domain) == OT_ERROR_NONE, error = OT_ERROR_FAILED);
}
while (GetNextTargetPower(domain, iterator, targetPower) == OT_ERROR_NONE)
{
otLogInfoPlat("Update target power: %s\r\n", targetPower.ToString().AsCString());
for (uint8_t ch = targetPower.GetChannelStart(); ch <= targetPower.GetChannelEnd(); ch++)
{
SuccessOrExit(error = otPlatRadioSetChannelTargetPower(gInstance, ch, targetPower.GetTargetPower()));
}
}
SuccessOrExit(error = UpdateCalibratedPower());
mRegionCode = aRegionCode;
exit:
if (error == OT_ERROR_NONE)
{
otLogInfoPlat("Set region \"%c%c\" successfully", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff));
}
else
{
otLogCritPlat("Set region \"%c%c\" failed, Error: %s", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff),
otThreadErrorToString(error));
}
return error;
}
otError PowerUpdater::UpdateCalibratedPower(void)
{
otError error = OT_ERROR_NONE;
int iterator = 0;
char value[kMaxValueSize];
Power::CalibratedPower calibratedPower;
ConfigFile *calibrationFile = &mFactoryConfigFile;
SuccessOrExit(error = otPlatRadioClearCalibratedPowers(gInstance));
// If the distribution of output power is large, the factory needs to measure the power calibration data
// for each device individually, and the power calibration data will be written to the factory config file.
// Otherwise, the power calibration data can be pre-configured in the product config file.
if (calibrationFile->Get(kKeyCalibratedPower, iterator, value, sizeof(value)) != OT_ERROR_NONE)
{
calibrationFile = &mProductConfigFile;
}
iterator = 0;
while (calibrationFile->Get(kKeyCalibratedPower, iterator, value, sizeof(value)) == OT_ERROR_NONE)
{
SuccessOrExit(error = calibratedPower.FromString(value));
otLogInfoPlat("Update calibrated power: %s\r\n", calibratedPower.ToString().AsCString());
for (uint8_t ch = calibratedPower.GetChannelStart(); ch <= calibratedPower.GetChannelEnd(); ch++)
{
SuccessOrExit(error = otPlatRadioAddCalibratedPower(gInstance, ch, calibratedPower.GetActualPower(),
calibratedPower.GetRawPowerSetting().GetData(),
calibratedPower.GetRawPowerSetting().GetLength()));
}
}
exit:
if (error != OT_ERROR_NONE)
{
otLogCritPlat("Update calibrated power table failed, Error: %s", otThreadErrorToString(error));
}
return error;
}
otError PowerUpdater::GetDomain(uint16_t aRegionCode, Power::Domain &aDomain)
{
otError error = OT_ERROR_NOT_FOUND;
int iterator = 0;
char value[kMaxValueSize];
char *str;
while (mProductConfigFile.Get(kKeyRegionDomainMapping, iterator, value, sizeof(value)) == OT_ERROR_NONE)
{
if ((str = strtok(value, kCommaDelimiter)) == nullptr)
{
continue;
}
while ((str = strtok(nullptr, kCommaDelimiter)) != nullptr)
{
if ((strlen(str) == 2) && (StringToRegionCode(str) == aRegionCode))
{
ExitNow(error = aDomain.Set(value));
}
}
}
exit:
if (error != OT_ERROR_NONE)
{
otLogCritPlat("Get domain failed, Error: %s", otThreadErrorToString(error));
}
return error;
}
otError PowerUpdater::GetNextTargetPower(const Power::Domain &aDomain, int &aIterator, Power::TargetPower &aTargetPower)
{
otError error = OT_ERROR_NOT_FOUND;
char value[kMaxValueSize];
char *domain;
char *psave;
while (mProductConfigFile.Get(kKeyTargetPower, aIterator, value, sizeof(value)) == OT_ERROR_NONE)
{
if (((domain = strtok_r(value, kCommaDelimiter, &psave)) == nullptr) || (aDomain != domain))
{
continue;
}
if ((error = aTargetPower.FromString(psave)) != OT_ERROR_NONE)
{
otLogCritPlat("Read target power failed, Error: %s", otThreadErrorToString(error));
}
break;
}
return error;
}
} // namespace Posix
} // namespace ot
#endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
+116
View File
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2022, 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 strain 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.
*/
#ifndef POSIX_PLATFORM_POWER_UPDATER_HPP_
#define POSIX_PLATFORM_POWER_UPDATER_HPP_
#include "openthread-posix-config.h"
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
#include <stdio.h>
#include <string.h>
#include <openthread/error.h>
#include <openthread/logging.h>
#include <openthread/platform/radio.h>
#include "config_file.hpp"
#include "power.hpp"
#include "common/code_utils.hpp"
namespace ot {
namespace Posix {
/**
* This class updates the target power table and calibrated powe table to the RCP.
*
*/
class PowerUpdater
{
public:
PowerUpdater(void)
: mFactoryConfigFile(kFactoryConfigFile)
, mProductConfigFile(kProductConfigFile)
, mRegionCode(0)
{
}
/**
* Set 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.
*
* @retval OT_ERROR_NONE Successfully set region code.
* @retval OT_ERROR_FAILED Failed to set the region code.
*
*/
otError 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.
*
* @returns The region code.
*
*/
uint16_t GetRegion(void) const { return mRegionCode; }
private:
const char *kFactoryConfigFile = OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE;
const char *kProductConfigFile = OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE;
const char *kKeyCalibratedPower = "calibrated_power";
const char *kKeyTargetPower = "target_power";
const char *kKeyRegionDomainMapping = "region_domain_mapping";
const char *kCommaDelimiter = ",";
static constexpr uint16_t kMaxValueSize = 512;
static constexpr uint16_t kRegionCodeWorldWide = 0x5757; // Region Code: "WW"
uint16_t StringToRegionCode(const char *aString) const
{
return static_cast<uint16_t>(((aString[0] & 0xFF) << 8) | ((aString[1] & 0xFF) << 0));
}
otError GetDomain(uint16_t aRegionCode, Power::Domain &aDomain);
otError GetNextTargetPower(const Power::Domain &aDomain, int &aIterator, Power::TargetPower &aTargetPower);
otError UpdateCalibratedPower(void);
ConfigFile mFactoryConfigFile;
ConfigFile mProductConfigFile;
uint16_t mRegionCode;
};
} // namespace Posix
} // namespace ot
#endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
#endif // POSIX_PLATFORM_POWER_UPDATER_HPP_
+39 -1
View File
@@ -63,6 +63,11 @@ static ot::Spinel::RadioSpinel<ot::Posix::VendorInterface, RadioProcessContext>
"OT_POSIX_RCP_BUS_VENDOR!"
#endif
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
#include "power_updater.hpp"
static ot::Posix::PowerUpdater sPowerUpdater;
#endif
namespace ot {
namespace Posix {
@@ -135,7 +140,7 @@ void Radio::Init(void)
VerifyOrDie(strnlen(region, 3) == 2, OT_EXIT_INVALID_ARGUMENTS);
regionCode = static_cast<uint16_t>(static_cast<uint16_t>(region[0]) << 8) + static_cast<uint16_t>(region[1]);
SuccessOrDie(sRadioSpinel.SetRadioRegion(regionCode));
SuccessOrDie(otPlatRadioSetRegion(gInstance, regionCode));
}
#if OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
@@ -701,16 +706,49 @@ otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aCh
return sRadioSpinel.SetChannelMaxTransmitPower(aChannel, aMaxPower);
}
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
otError otPlatRadioAddCalibratedPower(otInstance *aInstance,
uint8_t aChannel,
int16_t aActualPower,
const uint8_t *aRawPowerSetting,
uint16_t aRawPowerSettingLength)
{
OT_UNUSED_VARIABLE(aInstance);
return sRadioSpinel.AddCalibratedPower(aChannel, aActualPower, aRawPowerSetting, aRawPowerSettingLength);
}
otError otPlatRadioClearCalibratedPowers(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return sRadioSpinel.ClearCalibratedPowers();
}
otError otPlatRadioSetChannelTargetPower(otInstance *aInstance, uint8_t aChannel, int16_t aTargetPower)
{
OT_UNUSED_VARIABLE(aInstance);
return sRadioSpinel.SetChannelTargetPower(aChannel, aTargetPower);
}
#endif
otError otPlatRadioSetRegion(otInstance *aInstance, uint16_t aRegionCode)
{
OT_UNUSED_VARIABLE(aInstance);
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
return sPowerUpdater.SetRegion(aRegionCode);
#else
return sRadioSpinel.SetRadioRegion(aRegionCode);
#endif
}
otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode)
{
OT_UNUSED_VARIABLE(aInstance);
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
*aRegionCode = sPowerUpdater.GetRegion();
return OT_ERROR_NONE;
#else
return sRadioSpinel.GetRadioRegion(aRegionCode);
#endif
}
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/expect -f
#
# Copyright (c) 2022, 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.
#
source "tests/scripts/expect/_common.exp"
spawn build/posix/tools/ot-fct/ot-fct
send "targetpowertable\n"
expect_line "Done"
send "powercalibrationtable\n"
expect_line "Done"
send "powercalibrationtable add -b 11,25 -c 1900,112233/1000,223344 -b 26,26 -c 1500,334455/700,445566\n"
expect_line "Done"
send "powercalibrationtable clear\n"
expect_line "Done"
send "regiondomaintable\n"
expect "Done"
send "invalidcommand\n"
expect "failed"
expect "status 0x17"
send "\x04"
expect eof
+21
View File
@@ -812,6 +812,27 @@ target_link_libraries(ot-test-pool
add_test(NAME ot-test-pool COMMAND ot-test-pool)
add_executable(ot-test-power-calibration
test_power_calibration.cpp
)
target_include_directories(ot-test-power-calibration
PRIVATE
${COMMON_INCLUDES}
)
target_compile_options(ot-test-power-calibration
PRIVATE
${COMMON_COMPILE_OPTIONS}
)
target_link_libraries(ot-test-power-calibration
PRIVATE
${COMMON_LIBS}
)
add_test(NAME ot-test-power-calibration COMMAND ot-test-power-calibration)
add_executable(ot-test-priority-queue
test_priority_queue.cpp
)
+151
View File
@@ -0,0 +1,151 @@
/*
* Copyright (c) 2022, 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.
*/
#include <openthread/platform/radio.h>
#include "test_platform.h"
#include "test_util.h"
#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
namespace ot {
void TestPowerCalibration(void)
{
otInstance *instance;
uint8_t rawPowerSetting[2];
uint16_t rawPowerSettingLength;
struct CalibratedPowerEntry
{
uint8_t mChannel;
int16_t mActualPower;
uint8_t mRawPowerSetting[1];
uint16_t mRawPowerSettingLength;
};
constexpr CalibratedPowerEntry kCalibratedPowerTable[] = {
{11, 15000, {0x02}, 1},
{11, 5000, {0x00}, 1},
{11, 10000, {0x01}, 1},
};
instance = static_cast<otInstance *>(testInitInstance());
VerifyOrQuit(instance != nullptr, "Null OpenThread instance");
for (const CalibratedPowerEntry &calibratedPower : kCalibratedPowerTable)
{
SuccessOrQuit(otPlatRadioAddCalibratedPower(instance, calibratedPower.mChannel, calibratedPower.mActualPower,
calibratedPower.mRawPowerSetting,
calibratedPower.mRawPowerSettingLength));
}
SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 4999));
rawPowerSettingLength = sizeof(rawPowerSetting);
VerifyOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength) ==
OT_ERROR_NOT_FOUND);
SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 5000));
rawPowerSettingLength = sizeof(rawPowerSetting);
SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength));
VerifyOrQuit(rawPowerSettingLength == 1);
VerifyOrQuit(rawPowerSetting[0] == 0x00);
SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 9999));
rawPowerSettingLength = sizeof(rawPowerSetting);
SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength));
VerifyOrQuit(rawPowerSettingLength == 1);
VerifyOrQuit(rawPowerSetting[0] == 0x00);
SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 10000));
rawPowerSettingLength = sizeof(rawPowerSetting);
SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength));
VerifyOrQuit(rawPowerSettingLength == 1);
VerifyOrQuit(rawPowerSetting[0] == 0x01);
SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 14999));
rawPowerSettingLength = sizeof(rawPowerSetting);
SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength));
VerifyOrQuit(rawPowerSettingLength == 1);
VerifyOrQuit(rawPowerSetting[0] == 0x01);
SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 15000));
rawPowerSettingLength = sizeof(rawPowerSetting);
SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength));
VerifyOrQuit(rawPowerSettingLength == 1);
VerifyOrQuit(rawPowerSetting[0] == 0x02);
SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 15001));
rawPowerSettingLength = sizeof(rawPowerSetting);
SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength));
VerifyOrQuit(rawPowerSettingLength == 1);
VerifyOrQuit(rawPowerSetting[0] == 0x02);
rawPowerSettingLength = sizeof(rawPowerSetting);
VerifyOrQuit(otPlatRadioGetRawPowerSetting(instance, 12, rawPowerSetting, &rawPowerSettingLength) ==
OT_ERROR_NOT_FOUND);
SuccessOrQuit(otPlatRadioClearCalibratedPowers(instance));
rawPowerSettingLength = sizeof(rawPowerSetting);
VerifyOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength) ==
OT_ERROR_NOT_FOUND);
for (const CalibratedPowerEntry &calibratedPower : kCalibratedPowerTable)
{
SuccessOrQuit(otPlatRadioAddCalibratedPower(instance, calibratedPower.mChannel, calibratedPower.mActualPower,
calibratedPower.mRawPowerSetting,
calibratedPower.mRawPowerSettingLength));
}
SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 15000));
rawPowerSettingLength = sizeof(rawPowerSetting);
SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength));
VerifyOrQuit(rawPowerSettingLength == 1);
VerifyOrQuit(rawPowerSetting[0] == 0x02);
VerifyOrQuit(
otPlatRadioAddCalibratedPower(instance, kCalibratedPowerTable[0].mChannel,
kCalibratedPowerTable[0].mActualPower, kCalibratedPowerTable[0].mRawPowerSetting,
kCalibratedPowerTable[0].mRawPowerSettingLength) == OT_ERROR_INVALID_ARGS);
testFreeInstance(instance);
}
} // namespace ot
#endif // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
int main(void)
{
#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
ot::TestPowerCalibration();
printf("All tests passed\n");
#else // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
printf("Power calibration is not enabled\n");
#endif // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
return 0;
}
+31
View File
@@ -0,0 +1,31 @@
#
# Copyright (c) 2022, 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.
#
if(OT_PLATFORM STREQUAL "posix")
add_subdirectory(ot-fct)
endif()
+49
View File
@@ -0,0 +1,49 @@
#
# Copyright (c) 2022, 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.
#
project(ot-fct)
set(OPENTHREAD_DIR ${PROJECT_SOURCE_DIR}/../../)
include_directories(
${OPENTHREAD_DIR}/include
${OPENTHREAD_DIR}/src
${OPENTHREAD_DIR}/src/core
${OPENTHREAD_DIR}/src/posix/platform
)
add_executable(ot-fct
cli.cpp
main.cpp
logging.cpp
${OPENTHREAD_DIR}/src/core/common/string.cpp
${OPENTHREAD_DIR}/src/core/utils/parse_cmdline.cpp
${OPENTHREAD_DIR}/src/lib/platform/exit_code.c
${OPENTHREAD_DIR}/src/posix/platform/power.cpp
${OPENTHREAD_DIR}/src/posix/platform/config_file.cpp
)
+75
View File
@@ -0,0 +1,75 @@
# OpenThread Factory Tool Reference
## Overview
The ot-fct is used to store the power calibration table into the factory configuration file and show the power related tables.
## Command List
- [powercalibrationtable](#powercalibrationtable)
- [regiondomaintable](#regiondomaintable)
- [targetpowertable](#targetpowertable)
#### powercalibrationtable
Show the power calibration table.
```bash
> powercalibrationtable
| ChStart | ChEnd | ActualPower(0.01dBm) | RawPowerSetting |
+---------+---------+----------------------+-----------------+
| 11 | 25 | 1900 | 112233 |
| 11 | 25 | 1000 | 223344 |
| 26 | 26 | 1500 | 334455 |
| 26 | 26 | 700 | 445566 |
Done
```
#### powercalibrationtable add -b \<channelstart\>,\<channelend\> -c \<actualpower\>,\<rawpowersetting\>/... ...
Add power calibration table entry.
- channelstart: Sub-band start channel.
- channelend: Sub-band end channel.
- actualpower: The actual power in 0.01 dBm.
- rawpowersetting: The raw power setting hex string.
```bash
> powercalibrationtable add -b 11,25 -c 1900,112233/1000,223344 -b 26,26 -c 1500,334455/700,445566
Done
```
#### powercalibrationtable clear
Clear the power calibration table.
```bash
> powercalibrationtable clear
Done
```
#### regiondomaintable
Show the region and regulatory domain mapping table.
```bash
> regiondomaintable
FCC,AU,CA,CL,CO,IN,MX,PE,TW,US
ETSI,WW
Done
```
#### targetpowertable
Show the target power table.
```bash
> targetpowertable
| Domain | ChStart | ChEnd | TargetPower(0.01dBm) |
+----------+---------+---------+----------------------+
| FCC | 11 | 14 | 1700 |
| FCC | 15 | 24 | 2000 |
| FCC | 25 | 26 | 1600 |
| ETSI | 11 | 26 | 1000 |
Done
```
+329
View File
@@ -0,0 +1,329 @@
/*
* Copyright (c) 2022, 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 strain 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.
*/
#include "cli.hpp"
#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "power.hpp"
#include "common/code_utils.hpp"
namespace ot {
namespace Fct {
const struct Cli::Command Cli::sCommands[] = {
{"powercalibrationtable", &Cli::ProcessCalibrationTable},
{"targetpowertable", &Cli::ProcessTargetPowerTable},
{"regiondomaintable", &Cli::ProcessRegionDomainTable},
};
otError Cli::GetNextTargetPower(const Power::Domain &aDomain, int &aIterator, Power::TargetPower &aTargetPower)
{
otError error = OT_ERROR_NOT_FOUND;
char value[kMaxValueSize];
char *domain;
char *psave;
while (mProductConfigFile.Get(kKeyTargetPower, aIterator, value, sizeof(value)) == OT_ERROR_NONE)
{
if (((domain = strtok_r(value, kCommaDelimiter, &psave)) == nullptr) || (aDomain != domain))
{
continue;
}
error = aTargetPower.FromString(psave);
break;
}
return error;
}
otError Cli::GetNextDomain(int &aIterator, Power::Domain &aDomain)
{
otError error = OT_ERROR_NOT_FOUND;
char value[kMaxValueSize];
char *str;
while (mProductConfigFile.Get(kKeyRegionDomainMapping, aIterator, value, sizeof(value)) == OT_ERROR_NONE)
{
if ((str = strtok(value, kCommaDelimiter)) == nullptr)
{
continue;
}
error = aDomain.Set(str);
break;
}
exit:
return error;
}
otError Cli::ProcessTargetPowerTable(Utils::CmdLineParser::Arg aArgs[])
{
otError error = OT_ERROR_NONE;
int iterator = 0;
Power::Domain domain;
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
printf("| Domain | ChStart | ChEnd | TargetPower(0.01dBm) |\r\n");
printf("+----------+---------+---------+----------------------+\r\n");
while (GetNextDomain(iterator, domain) == OT_ERROR_NONE)
{
int iter = 0;
Power::TargetPower targetPower;
while (GetNextTargetPower(domain, iter, targetPower) == OT_ERROR_NONE)
{
printf("| %-8s | %-7d | %-7d | %-20d |\r\n", domain.AsCString(), targetPower.GetChannelStart(),
targetPower.GetChannelEnd(), targetPower.GetTargetPower());
}
}
exit:
return error;
}
otError Cli::ProcessRegionDomainTable(Utils::CmdLineParser::Arg aArgs[])
{
otError error = OT_ERROR_NONE;
int iterator = 0;
char value[kMaxValueSize];
char *domain;
char *psave;
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
while (mProductConfigFile.Get(kKeyRegionDomainMapping, iterator, value, sizeof(value)) == OT_ERROR_NONE)
{
printf("%s\r\n", value);
}
exit:
return error;
}
otError Cli::ParseNextCalibratedPower(char *aCalibratedPowerString,
uint16_t aLength,
uint16_t &aIterator,
Power::CalibratedPower &aCalibratedPower)
{
otError error = OT_ERROR_NONE;
char *start = aCalibratedPowerString + aIterator;
char *end;
char *subString;
int16_t actualPower;
ot::Power::RawPowerSetting rawPowerSetting;
VerifyOrExit(aIterator < aLength, error = OT_ERROR_PARSE);
end = strstr(start, "/");
if (end != nullptr)
{
aIterator = end - aCalibratedPowerString + 1; // +1 to skip '/'
*end = '\0';
}
else
{
aIterator = aLength;
end = aCalibratedPowerString + aLength;
}
subString = strstr(start, kCommaDelimiter);
VerifyOrExit(subString != nullptr, error = OT_ERROR_PARSE);
*subString = '\0';
subString++;
SuccessOrExit(error = Utils::CmdLineParser::ParseAsInt16(start, actualPower));
aCalibratedPower.SetActualPower(actualPower);
VerifyOrExit(subString < end, error = OT_ERROR_PARSE);
SuccessOrExit(error = rawPowerSetting.Set(subString));
aCalibratedPower.SetRawPowerSetting(rawPowerSetting);
exit:
return error;
}
otError Cli::ProcessCalibrationTable(Utils::CmdLineParser::Arg aArgs[])
{
otError error = OT_ERROR_NONE;
if (aArgs[0].IsEmpty())
{
int iterator = 0;
char value[kMaxValueSize];
ot::Power::CalibratedPower calibratedPower;
printf("| ChStart | ChEnd | ActualPower(0.01dBm) | RawPowerSetting |\r\n");
printf("+---------+---------+----------------------+-----------------+\r\n");
while (mFactoryConfigFile.Get(kKeyCalibratedPower, iterator, value, sizeof(value)) == OT_ERROR_NONE)
{
SuccessOrExit(error = calibratedPower.FromString(value));
printf("| %-7d | %-7d | %-20d | %-15s |\r\n", calibratedPower.GetChannelStart(),
calibratedPower.GetChannelEnd(), calibratedPower.GetActualPower(),
calibratedPower.GetRawPowerSetting().ToString().AsCString());
}
}
else if (aArgs[0] == "add")
{
constexpr uint16_t kStateSearchDomain = 0;
constexpr uint16_t kStateSearchPower = 1;
uint8_t state = kStateSearchDomain;
char *subString;
uint8_t channel;
Power::CalibratedPower calibratedPower;
for (Utils::CmdLineParser::Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++)
{
if ((state == kStateSearchDomain) && (*arg == "-b"))
{
arg++;
VerifyOrExit(!arg->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
subString = strtok(arg->GetCString(), kCommaDelimiter);
VerifyOrExit(subString != nullptr, error = OT_ERROR_PARSE);
SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(subString, channel));
calibratedPower.SetChannelStart(channel);
subString = strtok(NULL, kCommaDelimiter);
VerifyOrExit(subString != nullptr, error = OT_ERROR_PARSE);
SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(subString, channel));
calibratedPower.SetChannelEnd(channel);
VerifyOrExit(calibratedPower.GetChannelStart() <= calibratedPower.GetChannelEnd(),
error = OT_ERROR_INVALID_ARGS);
state = kStateSearchPower;
}
else if ((state == kStateSearchPower) && (*arg == "-c"))
{
uint16_t length;
uint16_t iterator = 0;
arg++;
VerifyOrExit(!arg->IsEmpty(), error = OT_ERROR_INVALID_ARGS);
length = strlen(arg->GetCString());
while (ParseNextCalibratedPower(arg->GetCString(), length, iterator, calibratedPower) == OT_ERROR_NONE)
{
SuccessOrExit(
error = mFactoryConfigFile.Add(kKeyCalibratedPower, calibratedPower.ToString().AsCString()));
}
state = kStateSearchDomain;
}
else
{
error = OT_ERROR_INVALID_ARGS;
break;
}
}
if (state == kStateSearchPower)
{
error = OT_ERROR_INVALID_ARGS;
}
}
else if (aArgs[0] == "clear")
{
error = mFactoryConfigFile.Clear(kKeyCalibratedPower);
}
else
{
error = OT_ERROR_INVALID_ARGS;
}
exit:
return error;
}
void Cli::ProcessCommand(Utils::CmdLineParser::Arg aArgs[])
{
otError error = OT_ERROR_NOT_FOUND;
int i;
for (i = 0; i < (sizeof(sCommands) / sizeof(sCommands[0])); i++)
{
if (strcmp(aArgs[0].GetCString(), sCommands[i].mName) == 0)
{
error = (this->*sCommands[i].mCommand)(aArgs + 1);
break;
}
}
exit:
AppendErrorResult(error);
}
void Cli::ProcessLine(char *aLine)
{
const int kMaxArgs = 20;
Utils::CmdLineParser::Arg args[kMaxArgs + 1];
SuccessOrExit(ot::Utils::CmdLineParser::ParseCmd(aLine, args, kMaxArgs));
VerifyOrExit(!args[0].IsEmpty());
ProcessCommand(args);
exit:
OutputPrompt();
}
void Cli::OutputPrompt(void)
{
printf("> ");
fflush(stdout);
}
void Cli::AppendErrorResult(otError aError)
{
if (aError != OT_ERROR_NONE)
{
printf("failed\r\nstatus %#x\r\n", aError);
}
else
{
printf("Done\r\n");
}
fflush(stdout);
}
} // namespace Fct
} // namespace ot
+117
View File
@@ -0,0 +1,117 @@
/*
* Copyright (c) 2022, 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 strain 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.
*/
#ifndef CLI_H
#define CLI_H
#include "openthread-posix-config.h"
#include <stdint.h>
#include <stdio.h>
#include "config_file.hpp"
#include "power.hpp"
#include "utils/parse_cmdline.hpp"
#include <openthread/error.h>
#include <openthread/platform/radio.h>
namespace ot {
namespace Fct {
class Cli;
/**
* This class implements the factory CLI.
*
*/
class Cli
{
public:
Cli(void)
: mFactoryConfigFile(OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE)
, mProductConfigFile(OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE)
{
}
/**
* This method processes a factory command.
*
* @param[in] aArgs The arguments of command line.
* @param[in] aArgsLength The number of args in @p aArgs.
*
*/
void ProcessCommand(Utils::CmdLineParser::Arg aArgs[]);
/**
* This method processes the command line.
*
* @param[in] aLine A pointer to a command line string.
*
*/
void ProcessLine(char *aLine);
/**
* This method outputs the prompt.
*
*/
void OutputPrompt(void);
private:
static constexpr uint16_t kMaxValueSize = 512;
const char *kKeyCalibratedPower = "calibrated_power";
const char *kKeyTargetPower = "target_power";
const char *kKeyRegionDomainMapping = "region_domain_mapping";
const char *kCommaDelimiter = ",";
struct Command
{
const char *mName;
otError (Cli::*mCommand)(Utils::CmdLineParser::Arg aArgs[]);
};
otError ParseNextCalibratedPower(char *aCalibratedPowerString,
uint16_t aLength,
uint16_t &aIterator,
Power::CalibratedPower &aCalibratedPower);
otError ProcessCalibrationTable(Utils::CmdLineParser::Arg aArgs[]);
otError ProcessTargetPowerTable(Utils::CmdLineParser::Arg aArgs[]);
otError ProcessRegionDomainTable(Utils::CmdLineParser::Arg aArgs[]);
otError GetNextDomain(int &aIterator, Power::Domain &aDomain);
otError GetNextTargetPower(const Power::Domain &aDomain, int &aIterator, Power::TargetPower &aTargetPower);
void AppendErrorResult(otError aError);
static const struct Command sCommands[];
ot::Posix::ConfigFile mFactoryConfigFile;
ot::Posix::ConfigFile mProductConfigFile;
};
} // namespace Fct
} // namespace ot
#endif
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2022, 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 strain 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <openthread/logging.h>
void otLogCritPlat(const char *aFormat, ...)
{
va_list args;
va_start(args, aFormat);
vprintf(aFormat, args);
va_end(args);
}
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2022, 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 strain 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.
*/
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>
#include "cli.hpp"
static ot::Fct::Cli sCli;
int main(int argc, char *argv[])
{
if (argc >= 2)
{
const int kMaxArgs = 20;
ot::Utils::CmdLineParser::Arg args[kMaxArgs + 1];
if (argc - 1 > kMaxArgs)
{
fprintf(stderr, "Too many arguments!\r\n");
exit(EXIT_FAILURE);
}
for (int i = 0; i < argc - 1; i++)
{
args[i].SetCString(argv[i + 1]);
}
args[argc - 1].Clear();
sCli.ProcessCommand(args);
}
else
{
fd_set rset;
int maxFd;
int ret;
sCli.OutputPrompt();
while (true)
{
FD_ZERO(&rset);
FD_SET(STDIN_FILENO, &rset);
maxFd = STDIN_FILENO + 1;
ret = select(maxFd, &rset, nullptr, nullptr, nullptr);
if ((ret == -1) && (errno != EINTR))
{
fprintf(stderr, "select: %s\n", strerror(errno));
break;
}
else if (ret > 0)
{
if (FD_ISSET(STDIN_FILENO, &rset))
{
const int kBufferSize = 512;
char buffer[kBufferSize];
if (fgets(buffer, sizeof(buffer), stdin) != nullptr)
{
sCli.ProcessLine(buffer);
}
else
{
break;
}
}
}
}
}
return EXIT_SUCCESS;
}