1 /*
2 * Copyright (c) 2020, 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 "link_metrics.h"
30
31 #include <openthread/link_metrics.h>
32
33 #include "common/clearable.hpp"
34 #include "common/linked_list.hpp"
35 #include "common/pool.hpp"
36 #include "thread/link_quality.hpp"
37
38 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
39
40 using namespace ot;
41
42 static int8_t sNoiseFloor; ///< The noise floor used by Link Metrics. It should be set to the platform's
43 ///< noise floor (measured noise floor, receiver sensitivity or a constant).
44
45 class LinkMetricsDataInfo : public LinkedListEntry<LinkMetricsDataInfo>, public Clearable<LinkMetricsDataInfo>
46 {
47 friend class LinkedList<LinkMetricsDataInfo>;
48 friend class LinkedListEntry<LinkMetricsDataInfo>;
49
50 public:
51 /**
52 * Constructor.
53 *
54 */
LinkMetricsDataInfo(void)55 LinkMetricsDataInfo(void) { Clear(); };
56
57 /**
58 * Set the information for this object.
59 *
60 * @param[in] aLinkMetrics Flags specifying what metrics to query.
61 * @param[in] aShortAddress Short Address of the Probing Initiator tracked by this object.
62 * @param[in] aExtAddress A reference to the Extended Address of the Probing Initiator tracked by this
63 * object.
64 *
65 */
Set(otLinkMetrics aLinkMetrics,otShortAddress aShortAddress,const otExtAddress & aExtAddress)66 void Set(otLinkMetrics aLinkMetrics, otShortAddress aShortAddress, const otExtAddress &aExtAddress)
67 {
68 mLinkMetrics = aLinkMetrics;
69 mShortAddress = aShortAddress;
70 memcpy(mExtAddress.m8, aExtAddress.m8, sizeof(aExtAddress));
71 }
72
73 /**
74 * Gets Link Metrics data stored in this object.
75 *
76 * TODO: Currently the order of Link Metircs data is fixed. Will update it to follow the order specified in TLV.
77 *
78 * @param[in] aLqi LQI value of the acknowledeged frame.
79 * @param[in] aRssi RSSI value of the acknowledged frame.
80 * @param[out] aData A pointer to the output buffer. @p aData MUST NOT be `nullptr`. The buffer must have
81 * at least 2 bytes (per spec 4.11.3.4.4.6). Otherwise the behavior would be undefined.
82 *
83 * @returns The number of bytes written. `0` on failure.
84 *
85 */
GetEnhAckData(uint8_t aLqi,int8_t aRssi,uint8_t * aData) const86 uint8_t GetEnhAckData(uint8_t aLqi, int8_t aRssi, uint8_t *aData) const
87 {
88 enum
89 {
90 kEnhAckProbingDataMaxLen = 2,
91 };
92
93 uint8_t bytes = 0;
94
95 VerifyOrExit(aData != nullptr);
96
97 if (mLinkMetrics.mLqi)
98 {
99 aData[bytes++] = aLqi;
100 }
101 if (mLinkMetrics.mLinkMargin)
102 {
103 aData[bytes++] = static_cast<uint8_t>(GetLinkMargin(aRssi) * 255 /
104 130); // Linear scale Link Margin from [0, 130] to [0, 255]
105 }
106 if (bytes < kEnhAckProbingDataMaxLen && mLinkMetrics.mRssi)
107 {
108 aData[bytes++] =
109 static_cast<uint8_t>((aRssi + 130) * 255 / 130); // Linear scale RSSI from [-130, 0] to [0, 255]
110 }
111
112 exit:
113 return bytes;
114 }
115
116 /**
117 * Gets the length of Link Metrics Data.
118 *
119 * @returns The number of bytes for the data.
120 *
121 */
GetEnhAckDataLen() const122 uint8_t GetEnhAckDataLen() const
123 {
124 return static_cast<uint8_t>(mLinkMetrics.mLqi) + static_cast<uint8_t>(mLinkMetrics.mLinkMargin) +
125 static_cast<uint8_t>(mLinkMetrics.mRssi);
126 }
127
128 /**
129 * Gets the metrics configured for the Enhanced-ACK Based Probing.
130 *
131 * @returns The metrics configured.
132 *
133 */
GetLinkMetrics(void) const134 otLinkMetrics GetLinkMetrics(void) const { return mLinkMetrics; }
135
136 private:
GetLinkMargin(int8_t aRssi) const137 uint8_t GetLinkMargin(int8_t aRssi) const { return ComputeLinkMargin(sNoiseFloor, aRssi); }
138
Matches(const otShortAddress & aShortAddress) const139 bool Matches(const otShortAddress &aShortAddress) const { return mShortAddress == aShortAddress; };
140
Matches(const otExtAddress & aExtAddress) const141 bool Matches(const otExtAddress &aExtAddress) const
142 {
143 return memcmp(&mExtAddress, &aExtAddress, sizeof(otExtAddress)) == 0;
144 };
145
146 LinkMetricsDataInfo *mNext;
147
148 otLinkMetrics mLinkMetrics;
149
150 otShortAddress mShortAddress;
151 otExtAddress mExtAddress;
152 };
153
154 enum
155 {
156 kMaxEnhAckProbingInitiator = OPENTHREAD_CONFIG_MLE_LINK_METRICS_MAX_SERIES_SUPPORTED,
157 };
158
159 typedef Pool<LinkMetricsDataInfo, kMaxEnhAckProbingInitiator> LinkMetricsDataInfoPool;
160
161 typedef LinkedList<LinkMetricsDataInfo> LinkMetricsDataInfoList;
162
GetLinkMetricsDataInfoPool(void)163 static LinkMetricsDataInfoPool &GetLinkMetricsDataInfoPool(void)
164 {
165 static LinkMetricsDataInfoPool sDataInfoPool;
166 return sDataInfoPool;
167 }
168
GetLinkMetricsDataInfoActiveList(void)169 static LinkMetricsDataInfoList &GetLinkMetricsDataInfoActiveList(void)
170 {
171 static LinkMetricsDataInfoList sDataInfoActiveList;
172 return sDataInfoActiveList;
173 }
174
IsLinkMetricsClear(otLinkMetrics aLinkMetrics)175 static inline bool IsLinkMetricsClear(otLinkMetrics aLinkMetrics)
176 {
177 return !aLinkMetrics.mPduCount && !aLinkMetrics.mLqi && !aLinkMetrics.mLinkMargin && !aLinkMetrics.mRssi;
178 }
179
otLinkMetricsInit(int8_t aNoiseFloor)180 void otLinkMetricsInit(int8_t aNoiseFloor)
181 {
182 sNoiseFloor = aNoiseFloor;
183 otLinkMetricsResetEnhAckProbing();
184 }
185
otLinkMetricsConfigureEnhAckProbing(otShortAddress aShortAddress,const otExtAddress * aExtAddress,otLinkMetrics aLinkMetrics)186 otError otLinkMetricsConfigureEnhAckProbing(otShortAddress aShortAddress,
187 const otExtAddress *aExtAddress,
188 otLinkMetrics aLinkMetrics)
189 {
190 otError error = OT_ERROR_NONE;
191 LinkMetricsDataInfo *dataInfo = nullptr;
192
193 VerifyOrExit(aExtAddress != nullptr, error = OT_ERROR_INVALID_ARGS);
194
195 if (IsLinkMetricsClear(aLinkMetrics)) ///< Remove the entry
196 {
197 dataInfo = GetLinkMetricsDataInfoActiveList().RemoveMatching(aShortAddress);
198 VerifyOrExit(dataInfo != nullptr, error = OT_ERROR_NOT_FOUND);
199 GetLinkMetricsDataInfoPool().Free(*dataInfo);
200 }
201 else
202 {
203 dataInfo = GetLinkMetricsDataInfoActiveList().FindMatching(aShortAddress);
204
205 if (dataInfo == nullptr)
206 {
207 dataInfo = GetLinkMetricsDataInfoPool().Allocate();
208 VerifyOrExit(dataInfo != nullptr, error = OT_ERROR_NO_BUFS);
209 dataInfo->Clear();
210 GetLinkMetricsDataInfoActiveList().Push(*dataInfo);
211 }
212
213 // Overwrite the previous configuration if it already existed.
214 dataInfo->Set(aLinkMetrics, aShortAddress, *aExtAddress);
215 }
216
217 exit:
218 return error;
219 }
220
GetLinkMetricsInfoByMacAddress(const otMacAddress * aMacAddress)221 LinkMetricsDataInfo *GetLinkMetricsInfoByMacAddress(const otMacAddress *aMacAddress)
222 {
223 LinkMetricsDataInfo *dataInfo = nullptr;
224
225 VerifyOrExit(aMacAddress != nullptr);
226
227 if (aMacAddress->mType == OT_MAC_ADDRESS_TYPE_SHORT)
228 {
229 dataInfo = GetLinkMetricsDataInfoActiveList().FindMatching(aMacAddress->mAddress.mShortAddress);
230 }
231 else if (aMacAddress->mType == OT_MAC_ADDRESS_TYPE_EXTENDED)
232 {
233 dataInfo = GetLinkMetricsDataInfoActiveList().FindMatching(aMacAddress->mAddress.mExtAddress);
234 }
235
236 exit:
237 return dataInfo;
238 }
239
otLinkMetricsEnhAckGenData(const otMacAddress * aMacAddress,uint8_t aLqi,int8_t aRssi,uint8_t * aData)240 uint8_t otLinkMetricsEnhAckGenData(const otMacAddress *aMacAddress, uint8_t aLqi, int8_t aRssi, uint8_t *aData)
241 {
242 uint8_t bytes = 0;
243 LinkMetricsDataInfo *dataInfo = GetLinkMetricsInfoByMacAddress(aMacAddress);
244
245 VerifyOrExit(dataInfo != nullptr);
246
247 bytes = dataInfo->GetEnhAckData(aLqi, aRssi, aData);
248
249 exit:
250 return bytes;
251 }
252
otLinkMetricsEnhAckGetDataLen(const otMacAddress * aMacAddress)253 uint8_t otLinkMetricsEnhAckGetDataLen(const otMacAddress *aMacAddress)
254 {
255 uint8_t len = 0;
256 LinkMetricsDataInfo *dataInfo = GetLinkMetricsInfoByMacAddress(aMacAddress);
257
258 VerifyOrExit(dataInfo != nullptr);
259 len = dataInfo->GetEnhAckDataLen();
260
261 exit:
262 return len;
263 }
264
otLinkMetricsResetEnhAckProbing(void)265 void otLinkMetricsResetEnhAckProbing(void)
266 {
267 GetLinkMetricsDataInfoActiveList().Clear();
268 GetLinkMetricsDataInfoPool().FreeAll();
269 }
270 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
271