1 /*
2 * Copyright (c) 2022, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "power_calibration.hpp"
30
31 #if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
32 #include <openthread/platform/diag.h>
33
34 #include "common/as_core_type.hpp"
35 #include "common/code_utils.hpp"
36 #include "common/locator_getters.hpp"
37
38 namespace ot {
39 namespace Utils {
40
PowerCalibration(Instance & aInstance)41 PowerCalibration::PowerCalibration(Instance &aInstance)
42 : InstanceLocator(aInstance)
43 , mLastChannel(0)
44 , mCalibratedPowerIndex(kInvalidIndex)
45 {
46 for (int16_t &targetPower : mTargetPowerTable)
47 {
48 targetPower = kInvalidPower;
49 }
50 }
51
Init(int16_t aActualPower,const uint8_t * aRawPowerSetting,uint16_t aRawPowerSettingLength)52 void PowerCalibration::CalibratedPowerEntry::Init(int16_t aActualPower,
53 const uint8_t *aRawPowerSetting,
54 uint16_t aRawPowerSettingLength)
55 {
56 AssertPointerIsNotNull(aRawPowerSetting);
57 OT_ASSERT(aRawPowerSettingLength <= kMaxRawPowerSettingSize);
58
59 mActualPower = aActualPower;
60 mLength = aRawPowerSettingLength;
61 memcpy(mSettings, aRawPowerSetting, aRawPowerSettingLength);
62 }
63
GetRawPowerSetting(uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)64 Error PowerCalibration::CalibratedPowerEntry::GetRawPowerSetting(uint8_t *aRawPowerSetting,
65 uint16_t *aRawPowerSettingLength)
66 {
67 Error error = kErrorNone;
68
69 AssertPointerIsNotNull(aRawPowerSetting);
70 AssertPointerIsNotNull(aRawPowerSettingLength);
71 VerifyOrExit(*aRawPowerSettingLength >= mLength, error = kErrorInvalidArgs);
72
73 memcpy(aRawPowerSetting, mSettings, mLength);
74 *aRawPowerSettingLength = mLength;
75
76 exit:
77 return error;
78 }
79
AddCalibratedPower(uint8_t aChannel,int16_t aActualPower,const uint8_t * aRawPowerSetting,uint16_t aRawPowerSettingLength)80 Error PowerCalibration::AddCalibratedPower(uint8_t aChannel,
81 int16_t aActualPower,
82 const uint8_t *aRawPowerSetting,
83 uint16_t aRawPowerSettingLength)
84 {
85 Error error = kErrorNone;
86 CalibratedPowerEntry entry;
87 uint8_t chIndex;
88
89 AssertPointerIsNotNull(aRawPowerSetting);
90 VerifyOrExit(IsChannelValid(aChannel) && aRawPowerSettingLength <= CalibratedPowerEntry::kMaxRawPowerSettingSize,
91 error = kErrorInvalidArgs);
92
93 chIndex = aChannel - Radio::kChannelMin;
94 VerifyOrExit(!mCalibratedPowerTables[chIndex].ContainsMatching(aActualPower), error = kErrorInvalidArgs);
95 VerifyOrExit(!mCalibratedPowerTables[chIndex].IsFull(), error = kErrorNoBufs);
96
97 entry.Init(aActualPower, aRawPowerSetting, aRawPowerSettingLength);
98 SuccessOrExit(error = mCalibratedPowerTables[chIndex].PushBack(entry));
99
100 if (aChannel == mLastChannel)
101 {
102 mCalibratedPowerIndex = kInvalidIndex;
103 }
104
105 exit:
106 return error;
107 }
108
ClearCalibratedPowers(void)109 void PowerCalibration::ClearCalibratedPowers(void)
110 {
111 for (CalibratedPowerTable &table : mCalibratedPowerTables)
112 {
113 table.Clear();
114 }
115
116 mCalibratedPowerIndex = kInvalidIndex;
117 }
118
SetChannelTargetPower(uint8_t aChannel,int16_t aTargetPower)119 Error PowerCalibration::SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower)
120 {
121 Error error = kErrorNone;
122
123 VerifyOrExit(IsChannelValid(aChannel), error = kErrorInvalidArgs);
124 mTargetPowerTable[aChannel - Radio::kChannelMin] = aTargetPower;
125
126 if (aChannel == mLastChannel)
127 {
128 mCalibratedPowerIndex = kInvalidIndex;
129 }
130
131 exit:
132 return error;
133 }
134
GetPowerSettings(uint8_t aChannel,int16_t * aTargetPower,int16_t * aActualPower,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)135 Error PowerCalibration::GetPowerSettings(uint8_t aChannel,
136 int16_t *aTargetPower,
137 int16_t *aActualPower,
138 uint8_t *aRawPowerSetting,
139 uint16_t *aRawPowerSettingLength)
140 {
141 Error error = kErrorNone;
142 uint8_t chIndex;
143 uint8_t powerIndex = kInvalidIndex;
144 int16_t foundPower = kInvalidPower;
145 int16_t targetPower;
146 int16_t actualPower;
147 int16_t minPower = NumericLimits<int16_t>::kMax;
148 uint8_t minPowerIndex = kInvalidIndex;
149
150 VerifyOrExit(IsChannelValid(aChannel), error = kErrorInvalidArgs);
151 VerifyOrExit((mLastChannel != aChannel) || IsPowerUpdated());
152
153 chIndex = aChannel - Radio::kChannelMin;
154 targetPower = mTargetPowerTable[chIndex];
155 VerifyOrExit(targetPower != kInvalidPower, error = kErrorNotFound);
156 VerifyOrExit(mCalibratedPowerTables[chIndex].GetLength() > 0, error = kErrorNotFound);
157
158 for (uint8_t i = 0; i < mCalibratedPowerTables[chIndex].GetLength(); i++)
159 {
160 actualPower = mCalibratedPowerTables[chIndex][i].GetActualPower();
161
162 if ((actualPower <= targetPower) && ((foundPower == kInvalidPower) || (foundPower <= actualPower)))
163 {
164 foundPower = actualPower;
165 powerIndex = i;
166 }
167 else if (actualPower < minPower)
168 {
169 minPower = actualPower;
170 minPowerIndex = i;
171 }
172 }
173
174 mCalibratedPowerIndex = (powerIndex != kInvalidIndex) ? powerIndex : minPowerIndex;
175 mLastChannel = aChannel;
176
177 exit:
178 if (error == kErrorNone)
179 {
180 chIndex = mLastChannel - Radio::kChannelMin;
181
182 if (aTargetPower != nullptr)
183 {
184 *aTargetPower = mTargetPowerTable[chIndex];
185 }
186
187 if (aActualPower != nullptr)
188 {
189 *aActualPower = mCalibratedPowerTables[chIndex][mCalibratedPowerIndex].GetActualPower();
190 }
191
192 error = mCalibratedPowerTables[chIndex][mCalibratedPowerIndex].GetRawPowerSetting(aRawPowerSetting,
193 aRawPowerSettingLength);
194 }
195
196 return error;
197 }
198 } // namespace Utils
199 } // namespace ot
200
201 using namespace ot;
202
otPlatRadioAddCalibratedPower(otInstance * aInstance,uint8_t aChannel,int16_t aActualPower,const uint8_t * aRawPowerSetting,uint16_t aRawPowerSettingLength)203 otError otPlatRadioAddCalibratedPower(otInstance *aInstance,
204 uint8_t aChannel,
205 int16_t aActualPower,
206 const uint8_t *aRawPowerSetting,
207 uint16_t aRawPowerSettingLength)
208 {
209 return AsCoreType(aInstance).Get<Utils::PowerCalibration>().AddCalibratedPower(
210 aChannel, aActualPower, aRawPowerSetting, aRawPowerSettingLength);
211 }
212
otPlatRadioClearCalibratedPowers(otInstance * aInstance)213 otError otPlatRadioClearCalibratedPowers(otInstance *aInstance)
214 {
215 AsCoreType(aInstance).Get<Utils::PowerCalibration>().ClearCalibratedPowers();
216 return OT_ERROR_NONE;
217 }
218
otPlatRadioSetChannelTargetPower(otInstance * aInstance,uint8_t aChannel,int16_t aTargetPower)219 otError otPlatRadioSetChannelTargetPower(otInstance *aInstance, uint8_t aChannel, int16_t aTargetPower)
220 {
221 return AsCoreType(aInstance).Get<Utils::PowerCalibration>().SetChannelTargetPower(aChannel, aTargetPower);
222 }
223
otPlatRadioGetRawPowerSetting(otInstance * aInstance,uint8_t aChannel,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)224 otError otPlatRadioGetRawPowerSetting(otInstance *aInstance,
225 uint8_t aChannel,
226 uint8_t *aRawPowerSetting,
227 uint16_t *aRawPowerSettingLength)
228 {
229 AssertPointerIsNotNull(aRawPowerSetting);
230 AssertPointerIsNotNull(aRawPowerSettingLength);
231
232 return AsCoreType(aInstance).Get<Utils::PowerCalibration>().GetPowerSettings(
233 aChannel, nullptr, nullptr, aRawPowerSetting, aRawPowerSettingLength);
234 }
235
otPlatDiagRadioGetPowerSettings(otInstance * aInstance,uint8_t aChannel,int16_t * aTargetPower,int16_t * aActualPower,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)236 otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance,
237 uint8_t aChannel,
238 int16_t *aTargetPower,
239 int16_t *aActualPower,
240 uint8_t *aRawPowerSetting,
241 uint16_t *aRawPowerSettingLength)
242 {
243 OT_UNUSED_VARIABLE(aInstance);
244
245 AssertPointerIsNotNull(aRawPowerSetting);
246 AssertPointerIsNotNull(aRawPowerSettingLength);
247
248 return AsCoreType(aInstance).Get<Utils::PowerCalibration>().GetPowerSettings(
249 aChannel, aTargetPower, aActualPower, aRawPowerSetting, aRawPowerSettingLength);
250 }
251 #endif // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
252