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 "instance/instance.hpp"
35
36 namespace ot {
37 namespace Utils {
38
PowerCalibration(Instance & aInstance)39 PowerCalibration::PowerCalibration(Instance &aInstance)
40 : InstanceLocator(aInstance)
41 , mLastChannel(0)
42 , mCalibratedPowerIndex(kInvalidIndex)
43 {
44 for (int16_t &targetPower : mTargetPowerTable)
45 {
46 targetPower = kInvalidPower;
47 }
48 }
49
Init(int16_t aActualPower,const uint8_t * aRawPowerSetting,uint16_t aRawPowerSettingLength)50 void PowerCalibration::CalibratedPowerEntry::Init(int16_t aActualPower,
51 const uint8_t *aRawPowerSetting,
52 uint16_t aRawPowerSettingLength)
53 {
54 AssertPointerIsNotNull(aRawPowerSetting);
55 OT_ASSERT(aRawPowerSettingLength <= kMaxRawPowerSettingSize);
56
57 mActualPower = aActualPower;
58 mLength = aRawPowerSettingLength;
59 memcpy(mSettings, aRawPowerSetting, aRawPowerSettingLength);
60 }
61
GetRawPowerSetting(uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)62 Error PowerCalibration::CalibratedPowerEntry::GetRawPowerSetting(uint8_t *aRawPowerSetting,
63 uint16_t *aRawPowerSettingLength)
64 {
65 Error error = kErrorNone;
66
67 AssertPointerIsNotNull(aRawPowerSetting);
68 AssertPointerIsNotNull(aRawPowerSettingLength);
69 VerifyOrExit(*aRawPowerSettingLength >= mLength, error = kErrorInvalidArgs);
70
71 memcpy(aRawPowerSetting, mSettings, mLength);
72 *aRawPowerSettingLength = mLength;
73
74 exit:
75 return error;
76 }
77
AddCalibratedPower(uint8_t aChannel,int16_t aActualPower,const uint8_t * aRawPowerSetting,uint16_t aRawPowerSettingLength)78 Error PowerCalibration::AddCalibratedPower(uint8_t aChannel,
79 int16_t aActualPower,
80 const uint8_t *aRawPowerSetting,
81 uint16_t aRawPowerSettingLength)
82 {
83 Error error = kErrorNone;
84 CalibratedPowerEntry entry;
85 uint8_t chIndex;
86
87 AssertPointerIsNotNull(aRawPowerSetting);
88 VerifyOrExit(IsChannelValid(aChannel) && aRawPowerSettingLength <= CalibratedPowerEntry::kMaxRawPowerSettingSize,
89 error = kErrorInvalidArgs);
90
91 chIndex = aChannel - Radio::kChannelMin;
92 VerifyOrExit(!mCalibratedPowerTables[chIndex].ContainsMatching(aActualPower), error = kErrorInvalidArgs);
93 VerifyOrExit(!mCalibratedPowerTables[chIndex].IsFull(), error = kErrorNoBufs);
94
95 entry.Init(aActualPower, aRawPowerSetting, aRawPowerSettingLength);
96 SuccessOrExit(error = mCalibratedPowerTables[chIndex].PushBack(entry));
97
98 if (aChannel == mLastChannel)
99 {
100 mCalibratedPowerIndex = kInvalidIndex;
101 }
102
103 exit:
104 return error;
105 }
106
ClearCalibratedPowers(void)107 void PowerCalibration::ClearCalibratedPowers(void)
108 {
109 for (CalibratedPowerTable &table : mCalibratedPowerTables)
110 {
111 table.Clear();
112 }
113
114 mCalibratedPowerIndex = kInvalidIndex;
115 }
116
SetChannelTargetPower(uint8_t aChannel,int16_t aTargetPower)117 Error PowerCalibration::SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower)
118 {
119 Error error = kErrorNone;
120
121 VerifyOrExit(IsChannelValid(aChannel), error = kErrorInvalidArgs);
122 mTargetPowerTable[aChannel - Radio::kChannelMin] = aTargetPower;
123
124 if (aChannel == mLastChannel)
125 {
126 mCalibratedPowerIndex = kInvalidIndex;
127 }
128
129 exit:
130 return error;
131 }
132
GetPowerSettings(uint8_t aChannel,int16_t * aTargetPower,int16_t * aActualPower,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)133 Error PowerCalibration::GetPowerSettings(uint8_t aChannel,
134 int16_t *aTargetPower,
135 int16_t *aActualPower,
136 uint8_t *aRawPowerSetting,
137 uint16_t *aRawPowerSettingLength)
138 {
139 Error error = kErrorNone;
140 uint8_t chIndex;
141 uint8_t powerIndex = kInvalidIndex;
142 int16_t foundPower = kInvalidPower;
143 int16_t targetPower;
144 int16_t actualPower;
145 int16_t minPower = NumericLimits<int16_t>::kMax;
146 uint8_t minPowerIndex = kInvalidIndex;
147
148 VerifyOrExit(IsChannelValid(aChannel), error = kErrorInvalidArgs);
149 VerifyOrExit((mLastChannel != aChannel) || IsPowerUpdated());
150
151 chIndex = aChannel - Radio::kChannelMin;
152 targetPower = mTargetPowerTable[chIndex];
153 VerifyOrExit(targetPower != kInvalidPower, error = kErrorNotFound);
154 VerifyOrExit(mCalibratedPowerTables[chIndex].GetLength() > 0, error = kErrorNotFound);
155
156 for (uint8_t i = 0; i < mCalibratedPowerTables[chIndex].GetLength(); i++)
157 {
158 actualPower = mCalibratedPowerTables[chIndex][i].GetActualPower();
159
160 if ((actualPower <= targetPower) && ((foundPower == kInvalidPower) || (foundPower <= actualPower)))
161 {
162 foundPower = actualPower;
163 powerIndex = i;
164 }
165 else if (actualPower < minPower)
166 {
167 minPower = actualPower;
168 minPowerIndex = i;
169 }
170 }
171
172 mCalibratedPowerIndex = (powerIndex != kInvalidIndex) ? powerIndex : minPowerIndex;
173 mLastChannel = aChannel;
174
175 exit:
176 if (error == kErrorNone)
177 {
178 chIndex = mLastChannel - Radio::kChannelMin;
179
180 if (aTargetPower != nullptr)
181 {
182 *aTargetPower = mTargetPowerTable[chIndex];
183 }
184
185 if (aActualPower != nullptr)
186 {
187 *aActualPower = mCalibratedPowerTables[chIndex][mCalibratedPowerIndex].GetActualPower();
188 }
189
190 error = mCalibratedPowerTables[chIndex][mCalibratedPowerIndex].GetRawPowerSetting(aRawPowerSetting,
191 aRawPowerSettingLength);
192 }
193
194 return error;
195 }
196 } // namespace Utils
197 } // namespace ot
198
199 using namespace ot;
200
otPlatRadioAddCalibratedPower(otInstance * aInstance,uint8_t aChannel,int16_t aActualPower,const uint8_t * aRawPowerSetting,uint16_t aRawPowerSettingLength)201 otError otPlatRadioAddCalibratedPower(otInstance *aInstance,
202 uint8_t aChannel,
203 int16_t aActualPower,
204 const uint8_t *aRawPowerSetting,
205 uint16_t aRawPowerSettingLength)
206 {
207 return AsCoreType(aInstance).Get<Utils::PowerCalibration>().AddCalibratedPower(
208 aChannel, aActualPower, aRawPowerSetting, aRawPowerSettingLength);
209 }
210
otPlatRadioClearCalibratedPowers(otInstance * aInstance)211 otError otPlatRadioClearCalibratedPowers(otInstance *aInstance)
212 {
213 AsCoreType(aInstance).Get<Utils::PowerCalibration>().ClearCalibratedPowers();
214 return OT_ERROR_NONE;
215 }
216
otPlatRadioSetChannelTargetPower(otInstance * aInstance,uint8_t aChannel,int16_t aTargetPower)217 otError otPlatRadioSetChannelTargetPower(otInstance *aInstance, uint8_t aChannel, int16_t aTargetPower)
218 {
219 return AsCoreType(aInstance).Get<Utils::PowerCalibration>().SetChannelTargetPower(aChannel, aTargetPower);
220 }
221
otPlatRadioGetRawPowerSetting(otInstance * aInstance,uint8_t aChannel,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)222 otError otPlatRadioGetRawPowerSetting(otInstance *aInstance,
223 uint8_t aChannel,
224 uint8_t *aRawPowerSetting,
225 uint16_t *aRawPowerSettingLength)
226 {
227 AssertPointerIsNotNull(aRawPowerSetting);
228 AssertPointerIsNotNull(aRawPowerSettingLength);
229
230 return AsCoreType(aInstance).Get<Utils::PowerCalibration>().GetPowerSettings(
231 aChannel, nullptr, nullptr, aRawPowerSetting, aRawPowerSettingLength);
232 }
233
otPlatDiagRadioGetPowerSettings(otInstance * aInstance,uint8_t aChannel,int16_t * aTargetPower,int16_t * aActualPower,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)234 otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance,
235 uint8_t aChannel,
236 int16_t *aTargetPower,
237 int16_t *aActualPower,
238 uint8_t *aRawPowerSetting,
239 uint16_t *aRawPowerSettingLength)
240 {
241 OT_UNUSED_VARIABLE(aInstance);
242
243 AssertPointerIsNotNull(aRawPowerSetting);
244 AssertPointerIsNotNull(aRawPowerSettingLength);
245
246 return AsCoreType(aInstance).Get<Utils::PowerCalibration>().GetPowerSettings(
247 aChannel, aTargetPower, aActualPower, aRawPowerSetting, aRawPowerSettingLength);
248 }
249 #endif // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
250