1 /*
2 * Copyright (c) 2024, 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 "rcp_caps_diag.hpp"
30
31 #include "lib/utils/math.hpp"
32
33 #if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
34 namespace ot {
35 namespace Posix {
36
37 #define SPINEL_ENTRY(aCategory, aCommand, aKey) \
38 { \
39 aCategory, aCommand, aKey, &RcpCapsDiag::HandleSpinelCommand<aCommand, aKey> \
40 }
41
HandleSpinelCommand(void)42 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_CAPS>(void)
43 {
44 static constexpr uint8_t kCapsBufferSize = 100;
45 uint8_t capsBuffer[kCapsBufferSize];
46 spinel_size_t capsLength = sizeof(capsBuffer);
47
48 return mRadioSpinel.Get(SPINEL_PROP_CAPS, SPINEL_DATATYPE_DATA_S, capsBuffer, &capsLength);
49 }
50
HandleSpinelCommand(void)51 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PROTOCOL_VERSION>(void)
52 {
53 unsigned int versionMajor;
54 unsigned int versionMinor;
55
56 return mRadioSpinel.Get(SPINEL_PROP_PROTOCOL_VERSION, (SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S),
57 &versionMajor, &versionMinor);
58 }
59
HandleSpinelCommand(void)60 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RADIO_CAPS>(void)
61 {
62 unsigned int radioCaps;
63
64 return mRadioSpinel.Get(SPINEL_PROP_RADIO_CAPS, SPINEL_DATATYPE_UINT_PACKED_S, &radioCaps);
65 }
66
HandleSpinelCommand(void)67 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_API_VERSION>(void)
68 {
69 unsigned int rcpApiVersion;
70
71 return mRadioSpinel.Get(SPINEL_PROP_RCP_API_VERSION, SPINEL_DATATYPE_UINT_PACKED_S, &rcpApiVersion);
72 }
73
HandleSpinelCommand(void)74 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_NCP_VERSION>(void)
75 {
76 static constexpr uint16_t kVersionStringSize = 128;
77 char mVersion[kVersionStringSize];
78
79 return mRadioSpinel.Get(SPINEL_PROP_NCP_VERSION, SPINEL_DATATYPE_UTF8_S, mVersion, sizeof(mVersion));
80 }
81
HandleSpinelCommand(void)82 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CHAN>(void)
83 {
84 static constexpr uint8_t kPhyChannel = 22;
85
86 return mRadioSpinel.Set(SPINEL_PROP_PHY_CHAN, SPINEL_DATATYPE_UINT8_S, kPhyChannel);
87 }
88
HandleSpinelCommand(void)89 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_ENABLED>(void)
90 {
91 return mRadioSpinel.Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, true /* aEnable*/);
92 }
93
HandleSpinelCommand(void)94 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_15_4_PANID>(void)
95 {
96 static constexpr uint16_t kPanId = 0x1234;
97
98 return mRadioSpinel.SetPanId(kPanId);
99 }
100
HandleSpinelCommand(void)101 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_15_4_LADDR>(void)
102 {
103 static constexpr otExtAddress kExtAddress = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}};
104
105 return mRadioSpinel.Set(SPINEL_PROP_MAC_15_4_LADDR, SPINEL_DATATYPE_EUI64_S, kExtAddress.m8);
106 }
107
HandleSpinelCommand(void)108 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_15_4_SADDR>(void)
109 {
110 static constexpr uint16_t kShortAddress = 0x1100;
111
112 return mRadioSpinel.Set(SPINEL_PROP_MAC_15_4_SADDR, SPINEL_DATATYPE_UINT16_S, kShortAddress);
113 }
114
115 template <>
HandleSpinelCommand(void)116 otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_RAW_STREAM_ENABLED>(void)
117 {
118 return mRadioSpinel.Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true);
119 }
120
HandleSpinelCommand(void)121 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SCAN_MASK>(void)
122 {
123 static constexpr uint8_t kScanChannel = 20;
124
125 return mRadioSpinel.Set(SPINEL_PROP_MAC_SCAN_MASK, SPINEL_DATATYPE_DATA_S, &kScanChannel, sizeof(uint8_t));
126 }
127
HandleSpinelCommand(void)128 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SCAN_PERIOD>(void)
129 {
130 static constexpr uint16_t kScanDuration = 1;
131
132 return mRadioSpinel.Set(SPINEL_PROP_MAC_SCAN_PERIOD, SPINEL_DATATYPE_UINT16_S, kScanDuration);
133 }
134
HandleSpinelCommand(void)135 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SCAN_STATE>(void)
136 {
137 return mRadioSpinel.Set(SPINEL_PROP_MAC_SCAN_STATE, SPINEL_DATATYPE_UINT8_S, SPINEL_SCAN_STATE_ENERGY);
138 }
139
HandleSpinelCommand(void)140 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SRC_MATCH_ENABLED>(void)
141 {
142 return mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_ENABLED, SPINEL_DATATYPE_BOOL_S, true);
143 }
144
145 template <>
HandleSpinelCommand(void)146 otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES>(void)
147 {
148 return mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, nullptr);
149 }
150
151 template <>
HandleSpinelCommand(void)152 otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES>(void)
153 {
154 return mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, nullptr);
155 }
156
HandleSpinelCommand(void)157 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_HWADDR>(void)
158 {
159 otExtAddress ieeeEui64;
160
161 return mRadioSpinel.Get(SPINEL_PROP_HWADDR, SPINEL_DATATYPE_EUI64_S, ieeeEui64.m8);
162 }
163
HandleSpinelCommand(void)164 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CHAN_PREFERRED>(void)
165 {
166 static constexpr uint8_t kChannelMaskBufferSize = 32;
167 uint8_t maskBuffer[kChannelMaskBufferSize];
168 spinel_size_t maskLength = sizeof(maskBuffer);
169
170 return mRadioSpinel.Get(SPINEL_PROP_PHY_CHAN_PREFERRED, SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength);
171 }
172
HandleSpinelCommand(void)173 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CHAN_SUPPORTED>(void)
174 {
175 static constexpr uint8_t kChannelMaskBufferSize = 32;
176 uint8_t maskBuffer[kChannelMaskBufferSize];
177 spinel_size_t maskLength = sizeof(maskBuffer);
178
179 return mRadioSpinel.Get(SPINEL_PROP_PHY_CHAN_SUPPORTED, SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength);
180 }
181
HandleSpinelCommand(void)182 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_RSSI>(void)
183 {
184 int8_t rssi;
185
186 return mRadioSpinel.Get(SPINEL_PROP_PHY_RSSI, SPINEL_DATATYPE_INT8_S, &rssi);
187 }
188
HandleSpinelCommand(void)189 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_RX_SENSITIVITY>(void)
190 {
191 int8_t rxSensitivity;
192
193 return mRadioSpinel.Get(SPINEL_PROP_PHY_RX_SENSITIVITY, SPINEL_DATATYPE_INT8_S, &rxSensitivity);
194 }
195
196 template <>
HandleSpinelCommand(void)197 otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_INSERT, SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES>(void)
198 {
199 static constexpr uint16_t kShortAddress = 0x1122;
200
201 return mRadioSpinel.Insert(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, kShortAddress);
202 }
203
204 template <>
HandleSpinelCommand(void)205 otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_INSERT, SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES>(
206 void)
207 {
208 static constexpr otExtAddress kExtAddress = {{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}};
209
210 return mRadioSpinel.Insert(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, kExtAddress.m8);
211 }
212
213 template <>
HandleSpinelCommand(void)214 otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_REMOVE, SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES>(void)
215 {
216 static constexpr uint16_t kShortAddress = 0x1122;
217
218 return mRadioSpinel.Remove(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, kShortAddress);
219 }
220
221 template <>
HandleSpinelCommand(void)222 otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_REMOVE, SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES>(
223 void)
224 {
225 static constexpr otExtAddress extAddress = {{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}};
226
227 return mRadioSpinel.Remove(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, extAddress.m8);
228 }
229
HandleSpinelCommand(void)230 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RCP_ENH_ACK_PROBING>(void)
231 {
232 static constexpr uint16_t kShortAddress = 0x1122;
233 static constexpr otExtAddress kExtAddress = {{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}};
234 static constexpr uint8_t kFlags = SPINEL_THREAD_LINK_METRIC_PDU_COUNT | SPINEL_THREAD_LINK_METRIC_LQI |
235 SPINEL_THREAD_LINK_METRIC_LINK_MARGIN | SPINEL_THREAD_LINK_METRIC_RSSI;
236
237 return mRadioSpinel.Set(SPINEL_PROP_RCP_ENH_ACK_PROBING,
238 SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S, kShortAddress,
239 kExtAddress.m8, kFlags);
240 }
241
HandleSpinelCommand(void)242 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RCP_MAC_FRAME_COUNTER>(void)
243 {
244 static constexpr uint32_t kMacFrameCounter = 1;
245
246 return mRadioSpinel.Set(SPINEL_PROP_RCP_MAC_FRAME_COUNTER, SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_BOOL_S,
247 kMacFrameCounter, true /*aSetIfLarger*/);
248 }
249
HandleSpinelCommand(void)250 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RCP_MAC_KEY>(void)
251 {
252 static constexpr uint8_t keyIdMode1 = 1 << 3;
253 static constexpr uint8_t keyId = 100;
254 otMacKeyMaterial prevKey;
255 otMacKeyMaterial curKey;
256 otMacKeyMaterial nextKey;
257
258 memset(prevKey.mKeyMaterial.mKey.m8, 0x11, OT_MAC_KEY_SIZE);
259 memset(curKey.mKeyMaterial.mKey.m8, 0x22, OT_MAC_KEY_SIZE);
260 memset(nextKey.mKeyMaterial.mKey.m8, 0x33, OT_MAC_KEY_SIZE);
261 return mRadioSpinel.SetMacKey(keyIdMode1, keyId, &prevKey, &curKey, &nextKey);
262 }
263
HandleSpinelCommand(void)264 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_CSL_ACCURACY>(void)
265 {
266 uint8_t accuracy;
267
268 return mRadioSpinel.Get(SPINEL_PROP_RCP_CSL_ACCURACY, SPINEL_DATATYPE_UINT8_S, &accuracy);
269 }
270
HandleSpinelCommand(void)271 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_CSL_UNCERTAINTY>(void)
272 {
273 uint8_t uncertainty;
274
275 return mRadioSpinel.Get(SPINEL_PROP_RCP_CSL_UNCERTAINTY, SPINEL_DATATYPE_UINT8_S, &uncertainty);
276 }
277
HandleSpinelCommand(void)278 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_TIMESTAMP>(void)
279 {
280 uint64_t remoteTimestamp = 0;
281 uint8_t buffer[sizeof(remoteTimestamp)];
282 spinel_ssize_t packed;
283
284 packed = spinel_datatype_pack(buffer, sizeof(buffer), SPINEL_DATATYPE_UINT64_S, remoteTimestamp);
285 return mRadioSpinel.GetWithParam(SPINEL_PROP_RCP_TIMESTAMP, buffer, static_cast<spinel_size_t>(packed),
286 SPINEL_DATATYPE_UINT64_S, &remoteTimestamp);
287 }
288
HandleSpinelCommand(void)289 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_PROMISCUOUS_MODE>(void)
290 {
291 return mRadioSpinel.SetPromiscuous(false /* aEnable*/);
292 }
293
HandleSpinelCommand(void)294 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CCA_THRESHOLD>(void)
295 {
296 int8_t ccaThreshold;
297
298 return mRadioSpinel.Get(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S, &ccaThreshold);
299 }
300
HandleSpinelCommand(void)301 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_FEM_LNA_GAIN>(void)
302 {
303 int8_t gain;
304
305 return mRadioSpinel.Get(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, &gain);
306 }
307
HandleSpinelCommand(void)308 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_REGION_CODE>(void)
309 {
310 uint16_t regionCode;
311
312 return mRadioSpinel.Get(SPINEL_PROP_PHY_REGION_CODE, SPINEL_DATATYPE_UINT16_S, ®ionCode);
313 }
314
HandleSpinelCommand(void)315 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_TX_POWER>(void)
316 {
317 int8_t power;
318
319 return mRadioSpinel.Get(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, &power);
320 }
321
HandleSpinelCommand(void)322 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RADIO_COEX_ENABLE>(void)
323 {
324 bool enabled;
325
326 return mRadioSpinel.Get(SPINEL_PROP_RADIO_COEX_ENABLE, SPINEL_DATATYPE_BOOL_S, &enabled);
327 }
328
HandleSpinelCommand(void)329 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RADIO_COEX_METRICS>(void)
330 {
331 otRadioCoexMetrics coexMetrics;
332
333 return mRadioSpinel.GetCoexMetrics(coexMetrics);
334 }
335
336 template <>
HandleSpinelCommand(void)337 otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_MIN_HOST_API_VERSION>(void)
338 {
339 unsigned int minHostRcpApiVersion;
340
341 return mRadioSpinel.Get(SPINEL_PROP_RCP_MIN_HOST_API_VERSION, SPINEL_DATATYPE_UINT_PACKED_S, &minHostRcpApiVersion);
342 }
343
HandleSpinelCommand(void)344 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CCA_THRESHOLD>(void)
345 {
346 static constexpr int8_t kCcaThreshold = -75;
347
348 return mRadioSpinel.Set(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S, kCcaThreshold);
349 }
350
HandleSpinelCommand(void)351 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CHAN_MAX_POWER>(void)
352 {
353 static constexpr uint8_t kChannel = 20;
354 static constexpr uint8_t kMaxPower = 10;
355
356 return mRadioSpinel.Set(SPINEL_PROP_PHY_CHAN_MAX_POWER, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S, kChannel,
357 kMaxPower);
358 }
359
HandleSpinelCommand(void)360 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CHAN_TARGET_POWER>(void)
361 {
362 static constexpr uint8_t kChannel = 20;
363 static constexpr uint16_t kTargetPower = 1000;
364
365 return mRadioSpinel.Set(SPINEL_PROP_PHY_CHAN_TARGET_POWER, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S,
366 kChannel, kTargetPower);
367 }
368
HandleSpinelCommand(void)369 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_FEM_LNA_GAIN>(void)
370 {
371 static constexpr int8_t kFemLnaGain = 0;
372
373 return mRadioSpinel.Set(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, kFemLnaGain);
374 }
375
HandleSpinelCommand(void)376 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_REGION_CODE>(void)
377 {
378 static constexpr uint16_t kRegionCode = 0x5757;
379
380 return mRadioSpinel.Set(SPINEL_PROP_PHY_REGION_CODE, SPINEL_DATATYPE_UINT16_S, kRegionCode);
381 }
382
HandleSpinelCommand(void)383 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_TX_POWER>(void)
384 {
385 static constexpr int8_t kTransmitPower = 10;
386
387 return mRadioSpinel.Set(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, kTransmitPower);
388 }
389
HandleSpinelCommand(void)390 template <> otError RcpCapsDiag::HandleSpinelCommand<SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RADIO_COEX_ENABLE>(void)
391 {
392 return mRadioSpinel.Set(SPINEL_PROP_RADIO_COEX_ENABLE, SPINEL_DATATYPE_BOOL_S, true /* aEnabled*/);
393 }
394
395 const struct RcpCapsDiag::SpinelEntry RcpCapsDiag::sSpinelEntries[] = {
396 // Basic Spinel commands
397 SPINEL_ENTRY(kCategoryBasic, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_CAPS),
398 SPINEL_ENTRY(kCategoryBasic, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PROTOCOL_VERSION),
399 SPINEL_ENTRY(kCategoryBasic, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RADIO_CAPS),
400 SPINEL_ENTRY(kCategoryBasic, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_API_VERSION),
401 SPINEL_ENTRY(kCategoryBasic, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_NCP_VERSION),
402
403 // Thread Version >= 1.1
404 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CHAN),
405 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_ENABLED),
406 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_15_4_PANID),
407 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_15_4_LADDR),
408 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_15_4_SADDR),
409 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_RAW_STREAM_ENABLED),
410 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SCAN_MASK),
411 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SCAN_PERIOD),
412 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SCAN_STATE),
413 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SRC_MATCH_ENABLED),
414 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES),
415 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES),
416 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_HWADDR),
417 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CHAN_PREFERRED),
418 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CHAN_SUPPORTED),
419 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_RSSI),
420 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_RX_SENSITIVITY),
421 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_INSERT, SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES),
422 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_INSERT, SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES),
423 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_REMOVE, SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES),
424 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_REMOVE, SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES),
425
426 // Thread Version >= 1.2
427 SPINEL_ENTRY(kCategoryThread1_2, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RCP_ENH_ACK_PROBING),
428 SPINEL_ENTRY(kCategoryThread1_2, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RCP_MAC_FRAME_COUNTER),
429 SPINEL_ENTRY(kCategoryThread1_2, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RCP_MAC_KEY),
430 SPINEL_ENTRY(kCategoryThread1_2, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_CSL_ACCURACY),
431 SPINEL_ENTRY(kCategoryThread1_2, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_CSL_UNCERTAINTY),
432 SPINEL_ENTRY(kCategoryThread1_2, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_TIMESTAMP),
433
434 // Utils
435 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_PROMISCUOUS_MODE),
436 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CCA_THRESHOLD),
437 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_FEM_LNA_GAIN),
438 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_REGION_CODE),
439 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_TX_POWER),
440 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RADIO_COEX_ENABLE),
441 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RADIO_COEX_METRICS),
442 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_MIN_HOST_API_VERSION),
443 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CCA_THRESHOLD),
444 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CHAN_MAX_POWER),
445 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CHAN_TARGET_POWER),
446 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_FEM_LNA_GAIN),
447 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_REGION_CODE),
448 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_TX_POWER),
449 SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RADIO_COEX_ENABLE),
450 };
451
DiagProcess(char * aArgs[],uint8_t aArgsLength)452 otError RcpCapsDiag::DiagProcess(char *aArgs[], uint8_t aArgsLength)
453 {
454 otError error = OT_ERROR_NONE;
455
456 VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS);
457
458 if (strcmp(aArgs[1], "capflags") == 0)
459 {
460 ProcessCapabilityFlags();
461 }
462 else if (strcmp(aArgs[1], "srcmatchtable") == 0)
463 {
464 ProcessSrcMatchTable();
465 }
466 else if (strcmp(aArgs[1], "spinel") == 0)
467 {
468 ProcessSpinel();
469 }
470 else if (strcmp(aArgs[1], "spinelspeed") == 0)
471 {
472 ProcessSpinelSpeed();
473 }
474 else
475 {
476 error = OT_ERROR_INVALID_COMMAND;
477 }
478
479 exit:
480 return error;
481 }
482
ProcessSpinel(void)483 void RcpCapsDiag::ProcessSpinel(void)
484 {
485 for (uint8_t i = 0; i < kNumCategories; i++)
486 {
487 TestSpinelCommands(static_cast<Category>(i));
488 }
489 }
490
TestSpinelCommands(Category aCategory)491 void RcpCapsDiag::TestSpinelCommands(Category aCategory)
492 {
493 otError error;
494
495 Output("\r\n%s :\r\n", CategoryToString(aCategory));
496
497 for (const SpinelEntry &entry : sSpinelEntries)
498 {
499 if (entry.mCategory != aCategory)
500 {
501 continue;
502 }
503
504 error = (this->*entry.mHandler)();
505 OutputResult(entry, error);
506 }
507 }
508
SetDiagOutputCallback(otPlatDiagOutputCallback aCallback,void * aContext)509 void RcpCapsDiag::SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void *aContext)
510 {
511 mOutputCallback = aCallback;
512 mOutputContext = aContext;
513 }
514
ProcessCapabilityFlags(void)515 void RcpCapsDiag::ProcessCapabilityFlags(void)
516 {
517 TestRadioCapbilityFlags();
518 TestSpinelCapbilityFlags();
519 }
520
TestRadioCapbilityFlags(void)521 void RcpCapsDiag::TestRadioCapbilityFlags(void)
522 {
523 static constexpr uint32_t kRadioThread11Flags[] = {OT_RADIO_CAPS_ACK_TIMEOUT, OT_RADIO_CAPS_TRANSMIT_RETRIES,
524 OT_RADIO_CAPS_CSMA_BACKOFF};
525 static constexpr uint32_t kRadioThread12Flags[] = {OT_RADIO_CAPS_TRANSMIT_SEC, OT_RADIO_CAPS_TRANSMIT_TIMING};
526 static constexpr uint32_t kRadioUtilsFlags[] = {OT_RADIO_CAPS_ENERGY_SCAN, OT_RADIO_CAPS_SLEEP_TO_TX,
527 OT_RADIO_CAPS_RECEIVE_TIMING, OT_RADIO_CAPS_RX_ON_WHEN_IDLE};
528 otError error;
529 unsigned int radioCaps;
530
531 SuccessOrExit(error = mRadioSpinel.Get(SPINEL_PROP_RADIO_CAPS, SPINEL_DATATYPE_UINT_PACKED_S, &radioCaps));
532
533 Output("\r\nRadio Capbility Flags :\r\n");
534
535 OutputRadioCapFlags(kCategoryThread1_1, static_cast<uint32_t>(radioCaps), kRadioThread11Flags,
536 OT_ARRAY_LENGTH(kRadioThread11Flags));
537 OutputRadioCapFlags(kCategoryThread1_2, static_cast<uint32_t>(radioCaps), kRadioThread12Flags,
538 OT_ARRAY_LENGTH(kRadioThread12Flags));
539 OutputRadioCapFlags(kCategoryUtils, static_cast<uint32_t>(radioCaps), kRadioUtilsFlags,
540 OT_ARRAY_LENGTH(kRadioUtilsFlags));
541
542 exit:
543 if (error != OT_ERROR_NONE)
544 {
545 Output("Failed to get radio capability flags: %s", otThreadErrorToString(error));
546 }
547
548 return;
549 }
550
OutputRadioCapFlags(Category aCategory,uint32_t aRadioCaps,const uint32_t * aFlags,uint16_t aNumbFlags)551 void RcpCapsDiag::OutputRadioCapFlags(Category aCategory,
552 uint32_t aRadioCaps,
553 const uint32_t *aFlags,
554 uint16_t aNumbFlags)
555 {
556 Output("\r\n%s :\r\n", CategoryToString(aCategory));
557 for (uint16_t i = 0; i < aNumbFlags; i++)
558 {
559 OutputFormat(RadioCapbilityToString(aFlags[i]), SupportToString((aRadioCaps & aFlags[i]) > 0));
560 }
561 }
562
TestSpinelCapbilityFlags(void)563 void RcpCapsDiag::TestSpinelCapbilityFlags(void)
564 {
565 static constexpr uint8_t kCapsBufferSize = 100;
566 static constexpr uint32_t kSpinelBasicFlags[] = {SPINEL_CAP_CONFIG_RADIO, SPINEL_CAP_MAC_RAW,
567 SPINEL_CAP_RCP_API_VERSION};
568 static constexpr uint32_t kSpinelUtilsFlags[] = {
569 SPINEL_CAP_OPENTHREAD_LOG_METADATA, SPINEL_CAP_RCP_MIN_HOST_API_VERSION, SPINEL_CAP_RCP_RESET_TO_BOOTLOADER};
570 otError error;
571 uint8_t capsBuffer[kCapsBufferSize];
572 spinel_size_t capsLength = sizeof(capsBuffer);
573
574 SuccessOrExit(error = mRadioSpinel.Get(SPINEL_PROP_CAPS, SPINEL_DATATYPE_DATA_S, capsBuffer, &capsLength));
575
576 Output("\r\nSpinel Capbility Flags :\r\n");
577
578 OutputSpinelCapFlags(kCategoryBasic, capsBuffer, capsLength, kSpinelBasicFlags, OT_ARRAY_LENGTH(kSpinelBasicFlags));
579 OutputSpinelCapFlags(kCategoryUtils, capsBuffer, capsLength, kSpinelUtilsFlags, OT_ARRAY_LENGTH(kSpinelUtilsFlags));
580
581 exit:
582 if (error != OT_ERROR_NONE)
583 {
584 Output("Failed to get Spinel capbility flags: %s", otThreadErrorToString(error));
585 }
586
587 return;
588 }
589
OutputSpinelCapFlags(Category aCategory,const uint8_t * aCapsData,spinel_size_t aCapsLength,const uint32_t * aFlags,uint16_t aNumbFlags)590 void RcpCapsDiag::OutputSpinelCapFlags(Category aCategory,
591 const uint8_t *aCapsData,
592 spinel_size_t aCapsLength,
593 const uint32_t *aFlags,
594 uint16_t aNumbFlags)
595 {
596 static constexpr uint8_t kCapsNameSize = 40;
597 char capName[kCapsNameSize];
598
599 Output("\r\n%s :\r\n", CategoryToString(aCategory));
600
601 for (uint16_t i = 0; i < aNumbFlags; i++)
602 {
603 snprintf(capName, sizeof(capName), "SPINEL_CAPS_%s", spinel_capability_to_cstr(aFlags[i]));
604 OutputFormat(capName, SupportToString(IsSpinelCapabilitySupported(aCapsData, aCapsLength, aFlags[i])));
605 }
606 }
607
IsSpinelCapabilitySupported(const uint8_t * aCapsData,spinel_size_t aCapsLength,uint32_t aCapability)608 bool RcpCapsDiag::IsSpinelCapabilitySupported(const uint8_t *aCapsData, spinel_size_t aCapsLength, uint32_t aCapability)
609 {
610 bool ret = false;
611
612 while (aCapsLength > 0)
613 {
614 unsigned int capability;
615 spinel_ssize_t unpacked;
616
617 unpacked = spinel_datatype_unpack(aCapsData, aCapsLength, SPINEL_DATATYPE_UINT_PACKED_S, &capability);
618 VerifyOrExit(unpacked > 0);
619 VerifyOrExit(capability != aCapability, ret = true);
620
621 aCapsData += unpacked;
622 aCapsLength -= static_cast<spinel_size_t>(unpacked);
623 }
624
625 exit:
626 return ret;
627 }
628
ProcessSrcMatchTable(void)629 void RcpCapsDiag::ProcessSrcMatchTable(void)
630 {
631 OutputShortSrcMatchTableSize();
632 OutputExtendedSrcMatchTableSize();
633 }
634
OutputShortSrcMatchTableSize(void)635 void RcpCapsDiag::OutputShortSrcMatchTableSize(void)
636 {
637 constexpr uint8_t kRouterIdOffset = 10;
638 constexpr uint8_t kRouterId = 5;
639 uint16_t num = 0;
640 uint16_t shortAddress;
641
642 SuccessOrExit(mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_ENABLED, SPINEL_DATATYPE_BOOL_S, true /* aEnable */));
643 SuccessOrExit(mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, nullptr));
644
645 for (num = 0; num < kMaxNumChildren; num++)
646 {
647 shortAddress = num | (kRouterId << kRouterIdOffset);
648 SuccessOrExit(
649 mRadioSpinel.Insert(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, shortAddress));
650 }
651
652 exit:
653 if (num != 0)
654 {
655 IgnoreReturnValue(mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, nullptr));
656 IgnoreReturnValue(
657 mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_ENABLED, SPINEL_DATATYPE_BOOL_S, false /* aEnable */));
658 }
659
660 OutputFormat("ShortSrcMatchTableSize", num);
661 }
662
OutputExtendedSrcMatchTableSize(void)663 void RcpCapsDiag::OutputExtendedSrcMatchTableSize(void)
664 {
665 otExtAddress extAddress = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
666 uint16_t num = 0;
667
668 SuccessOrExit(mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_ENABLED, SPINEL_DATATYPE_BOOL_S, true /* aEnable */));
669 SuccessOrExit(mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, nullptr));
670
671 for (num = 0; num < kMaxNumChildren; num++)
672 {
673 *reinterpret_cast<uint16_t *>(extAddress.m8) = num;
674 SuccessOrExit(
675 mRadioSpinel.Insert(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, extAddress.m8));
676 }
677
678 exit:
679 if (num != 0)
680 {
681 IgnoreReturnValue(mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, nullptr));
682 IgnoreReturnValue(
683 mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_ENABLED, SPINEL_DATATYPE_BOOL_S, false /* aEnable */));
684 }
685
686 OutputFormat("ExtendedSrcMatchTableSize", num);
687 }
688
HandleDiagOutput(const char * aFormat,va_list aArguments,void * aContext)689 void RcpCapsDiag::HandleDiagOutput(const char *aFormat, va_list aArguments, void *aContext)
690 {
691 static_cast<RcpCapsDiag *>(aContext)->HandleDiagOutput(aFormat, aArguments);
692 }
693
HandleDiagOutput(const char * aFormat,va_list aArguments)694 void RcpCapsDiag::HandleDiagOutput(const char *aFormat, va_list aArguments)
695 {
696 int rval;
697
698 VerifyOrExit(mDiagOutput != nullptr && mDiagOutputLength != 0);
699 rval = vsnprintf(mDiagOutput, mDiagOutputLength, aFormat, aArguments);
700 VerifyOrExit(rval >= 0);
701
702 rval = (rval > mDiagOutputLength) ? mDiagOutputLength : rval;
703 mDiagOutput += rval;
704 mDiagOutputLength -= rval;
705
706 exit:
707 return;
708 }
709
ProcessSpinelSpeed(void)710 void RcpCapsDiag::ProcessSpinelSpeed(void)
711 {
712 static constexpr uint32_t kUsPerSec = 1000000;
713 static constexpr uint8_t kBitsPerByte = 8;
714 static constexpr uint8_t kSpinelHeaderSize = 4;
715 static constexpr uint8_t kZeroTerminatorSize = 1;
716 static constexpr char kEchoCmd[] = "echo ";
717 static constexpr uint8_t kEchoPayloadLength = 200;
718 static constexpr uint8_t kNumTests = 100;
719
720 otError error = OT_ERROR_NONE;
721 char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {0};
722 char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE];
723 uint16_t echoPayloadLength;
724 uint64_t startTimestamp;
725 uint64_t endTimestamp;
726 uint64_t sumTime = 0;
727 uint64_t sumLength = 0;
728 uint32_t speed;
729 otPlatDiagOutputCallback callback;
730 void *context;
731
732 mRadioSpinel.GetDiagOutputCallback(callback, context);
733 mRadioSpinel.SetDiagOutputCallback(HandleDiagOutput, this);
734
735 strncpy(cmd, kEchoCmd, sizeof(cmd) - 1);
736 echoPayloadLength = static_cast<uint16_t>(sizeof(cmd) - strlen(cmd) - 1);
737 echoPayloadLength = Lib::Utils::Min<uint16_t>(kEchoPayloadLength, echoPayloadLength);
738 memset(cmd + strlen(cmd), '1', echoPayloadLength);
739
740 for (uint16_t i = 0; i < kNumTests; i++)
741 {
742 output[0] = '\0';
743 mDiagOutput = output;
744 mDiagOutputLength = sizeof(output);
745 startTimestamp = otPlatTimeGet();
746
747 SuccessOrExit(error = mRadioSpinel.PlatDiagProcess(cmd));
748
749 endTimestamp = otPlatTimeGet();
750 sumTime += endTimestamp - startTimestamp;
751 sumLength += kSpinelHeaderSize + strlen(cmd) + kZeroTerminatorSize + kSpinelHeaderSize + strlen(output) +
752 kZeroTerminatorSize;
753 }
754
755 mRadioSpinel.SetDiagOutputCallback(callback, context);
756
757 exit:
758 if (error == OT_ERROR_NONE)
759 {
760 speed = static_cast<uint32_t>((sumLength * kBitsPerByte * kUsPerSec) / sumTime);
761 snprintf(output, sizeof(output), "%lu bps", ToUlong(speed));
762 OutputFormat("SpinelSpeed", output);
763 }
764 else
765 {
766 Output("Failed to test the Spinel speed: %s", otThreadErrorToString(error));
767 }
768 }
769
OutputFormat(const char * aName,const char * aValue)770 void RcpCapsDiag::OutputFormat(const char *aName, const char *aValue)
771 {
772 static constexpr uint8_t kMaxNameLength = 56;
773 static const char kPadding[] = "----------------------------------------------------------";
774 uint16_t actualLength = static_cast<uint16_t>(strlen(aName));
775 uint16_t paddingOffset = (actualLength > kMaxNameLength) ? kMaxNameLength : actualLength;
776
777 static_assert(kMaxNameLength < sizeof(kPadding), "Padding bytes are too short");
778
779 Output("%.*s %s %s\r\n", kMaxNameLength, aName, &kPadding[paddingOffset], aValue);
780 }
781
OutputFormat(const char * aName,uint32_t aValue)782 void RcpCapsDiag::OutputFormat(const char *aName, uint32_t aValue)
783 {
784 static constexpr uint16_t kValueLength = 11;
785 char value[kValueLength];
786
787 snprintf(value, sizeof(value), "%u", aValue);
788 OutputFormat(aName, value);
789 }
790
OutputResult(const SpinelEntry & aEntry,otError error)791 void RcpCapsDiag::OutputResult(const SpinelEntry &aEntry, otError error)
792 {
793 static constexpr uint8_t kSpaceLength = 1;
794 static constexpr uint8_t kMaxCommandStringLength = 20;
795 static constexpr uint8_t kMaxKeyStringLength = 35;
796 static constexpr uint16_t kMaxBufferLength =
797 kMaxCommandStringLength + kMaxKeyStringLength + kSpaceLength + 1 /* size of '\0' */;
798 char buffer[kMaxBufferLength] = {0};
799 const char *commandString = spinel_command_to_cstr(aEntry.mCommand);
800 const char *keyString = spinel_prop_key_to_cstr(aEntry.mKey);
801
802 snprintf(buffer, sizeof(buffer), "%.*s %.*s", kMaxCommandStringLength, commandString, kMaxKeyStringLength,
803 keyString);
804 OutputFormat(buffer, otThreadErrorToString(error));
805 }
806
Output(const char * aFormat,...)807 void RcpCapsDiag::Output(const char *aFormat, ...)
808 {
809 va_list args;
810
811 va_start(args, aFormat);
812
813 if (mOutputCallback != nullptr)
814 {
815 mOutputCallback(aFormat, args, mOutputContext);
816 }
817
818 va_end(args);
819 }
820
CategoryToString(Category aCategory)821 const char *RcpCapsDiag::CategoryToString(Category aCategory)
822 {
823 static const char *const kCategoryStrings[] = {
824 "Basic", // (0) kCategoryBasic
825 "Thread Version >= 1.1", // (1) kCategoryThread1_1
826 "Thread Version >= 1.2", // (2) kCategoryThread1_2
827 "Utils", // (3) kCategoryUtils
828 };
829
830 static_assert(kCategoryBasic == 0, "kCategoryBasic value is incorrect");
831 static_assert(kCategoryThread1_1 == 1, "kCategoryThread1_1 value is incorrect");
832 static_assert(kCategoryThread1_2 == 2, "kCategoryThread1_2 value is incorrect");
833 static_assert(kCategoryUtils == 3, "kCategoryUtils value is incorrect");
834
835 return (aCategory < OT_ARRAY_LENGTH(kCategoryStrings)) ? kCategoryStrings[aCategory] : "invalid";
836 }
837
SupportToString(bool aSupport)838 const char *RcpCapsDiag::SupportToString(bool aSupport) { return aSupport ? "OK" : "NotSupported"; }
839
RadioCapbilityToString(uint32_t aCapability)840 const char *RcpCapsDiag::RadioCapbilityToString(uint32_t aCapability)
841 {
842 static const char *const kCapbilityStrings[] = {
843 "RADIO_CAPS_ACK_TIMEOUT", // (1 << 0) OT_RADIO_CAPS_ACK_TIMEOUT
844 "RADIO_CAPS_ENERGY_SCAN", // (1 << 1) OT_RADIO_CAPS_ENERGY_SCAN
845 "RADIO_CAPS_TRANSMIT_RETRIES", // (1 << 2) OT_RADIO_CAPS_TRANSMIT_RETRIES
846 "RADIO_CAPS_CSMA_BACKOFF", // (1 << 3) OT_RADIO_CAPS_CSMA_BACKOFF
847 "RADIO_CAPS_SLEEP_TO_TX", // (1 << 4) OT_RADIO_CAPS_SLEEP_TO_TX
848 "RADIO_CAPS_TRANSMIT_SEC", // (1 << 5) OT_RADIO_CAPS_TRANSMIT_SEC
849 "RADIO_CAPS_TRANSMIT_TIMING", // (1 << 6) OT_RADIO_CAPS_TRANSMIT_TIMING
850 "RADIO_CAPS_RECEIVE_TIMING", // (1 << 7) OT_RADIO_CAPS_RECEIVE_TIMING
851 "RADIO_CAPS_RX_ON_WHEN_IDLE", // (1 << 8) OT_RADIO_CAPS_RX_ON_WHEN_IDLE
852 };
853 const char *string = "invalid";
854 uint16_t index = 0;
855
856 static_assert(OT_RADIO_CAPS_ACK_TIMEOUT == 1 << 0, "OT_RADIO_CAPS_ACK_TIMEOUT value is incorrect");
857 static_assert(OT_RADIO_CAPS_ENERGY_SCAN == 1 << 1, "OT_RADIO_CAPS_ENERGY_SCAN value is incorrect");
858 static_assert(OT_RADIO_CAPS_TRANSMIT_RETRIES == 1 << 2, "OT_RADIO_CAPS_TRANSMIT_RETRIES value is incorrect");
859 static_assert(OT_RADIO_CAPS_CSMA_BACKOFF == 1 << 3, "OT_RADIO_CAPS_CSMA_BACKOFF value is incorrect");
860 static_assert(OT_RADIO_CAPS_SLEEP_TO_TX == 1 << 4, "OT_RADIO_CAPS_SLEEP_TO_TX value is incorrect");
861 static_assert(OT_RADIO_CAPS_TRANSMIT_SEC == 1 << 5, "OT_RADIO_CAPS_TRANSMIT_SEC value is incorrect");
862 static_assert(OT_RADIO_CAPS_TRANSMIT_TIMING == 1 << 6, "OT_RADIO_CAPS_TRANSMIT_TIMING value is incorrect");
863 static_assert(OT_RADIO_CAPS_RECEIVE_TIMING == 1 << 7, "OT_RADIO_CAPS_RECEIVE_TIMING value is incorrect");
864 static_assert(OT_RADIO_CAPS_RX_ON_WHEN_IDLE == 1 << 8, "OT_RADIO_CAPS_RX_ON_WHEN_IDLE value is incorrect");
865
866 for (; !(aCapability & 0x1); (aCapability >>= 1), index++)
867 {
868 VerifyOrExit(index < OT_ARRAY_LENGTH(kCapbilityStrings));
869 }
870
871 string = kCapbilityStrings[index];
872
873 exit:
874 return string;
875 }
876
877 } // namespace Posix
878 } // namespace ot
879 #endif // OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE
880