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