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, &regionCode);
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