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
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
155 for (uint8_t i = 0; i < mCalibratedPowerTables[chIndex].GetLength(); i++)
156 {
157 actualPower = mCalibratedPowerTables[chIndex][i].GetActualPower();
158
159 if ((actualPower <= targetPower) && ((foundPower == kInvalidPower) || (foundPower <= actualPower)))
160 {
161 foundPower = actualPower;
162 powerIndex = i;
163 }
164 }
165
166 VerifyOrExit(powerIndex != kInvalidIndex, error = kErrorNotFound);
167
168 mCalibratedPowerIndex = powerIndex;
169 mLastChannel = aChannel;
170
171 exit:
172 if (error == kErrorNone)
173 {
174 chIndex = mLastChannel - Radio::kChannelMin;
175
176 if (aTargetPower != nullptr)
177 {
178 *aTargetPower = mTargetPowerTable[chIndex];
179 }
180
181 if (aActualPower != nullptr)
182 {
183 *aActualPower = mCalibratedPowerTables[chIndex][mCalibratedPowerIndex].GetActualPower();
184 }
185
186 error = mCalibratedPowerTables[chIndex][mCalibratedPowerIndex].GetRawPowerSetting(aRawPowerSetting,
187 aRawPowerSettingLength);
188 }
189
190 return error;
191 }
192 } // namespace Utils
193 } // namespace ot
194
195 using namespace ot;
196
otPlatRadioAddCalibratedPower(otInstance * aInstance,uint8_t aChannel,int16_t aActualPower,const uint8_t * aRawPowerSetting,uint16_t aRawPowerSettingLength)197 otError otPlatRadioAddCalibratedPower(otInstance *aInstance,
198 uint8_t aChannel,
199 int16_t aActualPower,
200 const uint8_t *aRawPowerSetting,
201 uint16_t aRawPowerSettingLength)
202 {
203 return AsCoreType(aInstance).Get<Utils::PowerCalibration>().AddCalibratedPower(
204 aChannel, aActualPower, aRawPowerSetting, aRawPowerSettingLength);
205 }
206
otPlatRadioClearCalibratedPowers(otInstance * aInstance)207 otError otPlatRadioClearCalibratedPowers(otInstance *aInstance)
208 {
209 AsCoreType(aInstance).Get<Utils::PowerCalibration>().ClearCalibratedPowers();
210 return OT_ERROR_NONE;
211 }
212
otPlatRadioSetChannelTargetPower(otInstance * aInstance,uint8_t aChannel,int16_t aTargetPower)213 otError otPlatRadioSetChannelTargetPower(otInstance *aInstance, uint8_t aChannel, int16_t aTargetPower)
214 {
215 return AsCoreType(aInstance).Get<Utils::PowerCalibration>().SetChannelTargetPower(aChannel, aTargetPower);
216 }
217
otPlatRadioGetRawPowerSetting(otInstance * aInstance,uint8_t aChannel,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)218 otError otPlatRadioGetRawPowerSetting(otInstance *aInstance,
219 uint8_t aChannel,
220 uint8_t *aRawPowerSetting,
221 uint16_t *aRawPowerSettingLength)
222 {
223 AssertPointerIsNotNull(aRawPowerSetting);
224 AssertPointerIsNotNull(aRawPowerSettingLength);
225
226 return AsCoreType(aInstance).Get<Utils::PowerCalibration>().GetPowerSettings(
227 aChannel, nullptr, nullptr, aRawPowerSetting, aRawPowerSettingLength);
228 }
229
otPlatDiagRadioGetPowerSettings(otInstance * aInstance,uint8_t aChannel,int16_t * aTargetPower,int16_t * aActualPower,uint8_t * aRawPowerSetting,uint16_t * aRawPowerSettingLength)230 otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance,
231 uint8_t aChannel,
232 int16_t *aTargetPower,
233 int16_t *aActualPower,
234 uint8_t *aRawPowerSetting,
235 uint16_t *aRawPowerSettingLength)
236 {
237 OT_UNUSED_VARIABLE(aInstance);
238
239 AssertPointerIsNotNull(aRawPowerSetting);
240 AssertPointerIsNotNull(aRawPowerSettingLength);
241
242 return AsCoreType(aInstance).Get<Utils::PowerCalibration>().GetPowerSettings(
243 aChannel, aTargetPower, aActualPower, aRawPowerSetting, aRawPowerSettingLength);
244 }
245 #endif // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
246