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