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 "logger.hpp"
30 
31 #include "openthread-spinel-config.h"
32 
33 #include <assert.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 
37 #include <openthread/error.h>
38 #include <openthread/logging.h>
39 #include <openthread/platform/radio.h>
40 
41 #include "lib/spinel/spinel.h"
42 #include "lib/utils/math.hpp"
43 #include "lib/utils/utils.hpp"
44 
45 namespace ot {
46 namespace Spinel {
47 
48 using Lib::Utils::Min;
49 using Lib::Utils::ToUlong;
50 
Logger(const char * aModuleName)51 Logger::Logger(const char *aModuleName)
52     : mModuleName(aModuleName)
53 {
54 }
55 
LogIfFail(const char * aText,otError aError)56 void Logger::LogIfFail(const char *aText, otError aError)
57 {
58     OT_UNUSED_VARIABLE(aText);
59 
60     if (aError != OT_ERROR_NONE && aError != OT_ERROR_NO_ACK)
61     {
62         LogWarn("%s: %s", aText, otThreadErrorToString(aError));
63     }
64 }
65 
LogCrit(const char * aFormat,...)66 void Logger::LogCrit(const char *aFormat, ...)
67 {
68     va_list args;
69 
70     va_start(args, aFormat);
71     otLogPlatArgs(OT_LOG_LEVEL_CRIT, mModuleName, aFormat, args);
72     va_end(args);
73 }
74 
LogWarn(const char * aFormat,...)75 void Logger::LogWarn(const char *aFormat, ...)
76 {
77     va_list args;
78 
79     va_start(args, aFormat);
80     otLogPlatArgs(OT_LOG_LEVEL_WARN, mModuleName, aFormat, args);
81     va_end(args);
82 }
83 
LogNote(const char * aFormat,...)84 void Logger::LogNote(const char *aFormat, ...)
85 {
86     va_list args;
87 
88     va_start(args, aFormat);
89     otLogPlatArgs(OT_LOG_LEVEL_NOTE, mModuleName, aFormat, args);
90     va_end(args);
91 }
92 
LogInfo(const char * aFormat,...)93 void Logger::LogInfo(const char *aFormat, ...)
94 {
95     va_list args;
96 
97     va_start(args, aFormat);
98     otLogPlatArgs(OT_LOG_LEVEL_INFO, mModuleName, aFormat, args);
99     va_end(args);
100 }
101 
LogDebg(const char * aFormat,...)102 void Logger::LogDebg(const char *aFormat, ...)
103 {
104     va_list args;
105 
106     va_start(args, aFormat);
107     otLogPlatArgs(OT_LOG_LEVEL_DEBG, mModuleName, aFormat, args);
108     va_end(args);
109 }
110 
Snprintf(char * aDest,uint32_t aSize,const char * aFormat,...)111 uint32_t Logger::Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...)
112 {
113     int     len;
114     va_list args;
115 
116     va_start(args, aFormat);
117     len = vsnprintf(aDest, static_cast<size_t>(aSize), aFormat, args);
118     va_end(args);
119 
120     return (len < 0) ? 0 : Min(static_cast<uint32_t>(len), aSize - 1);
121 }
122 
LogSpinelFrame(const uint8_t * aFrame,uint16_t aLength,bool aTx)123 void Logger::LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx)
124 {
125     otError           error                                   = OT_ERROR_NONE;
126     char              buf[OPENTHREAD_LIB_SPINEL_LOG_MAX_SIZE] = {0};
127     spinel_ssize_t    unpacked;
128     uint8_t           header;
129     uint32_t          cmd;
130     spinel_prop_key_t key;
131     uint8_t          *data;
132     spinel_size_t     len;
133     const char       *prefix = nullptr;
134     char             *start  = buf;
135     char             *end    = buf + sizeof(buf);
136 
137     EXPECT(otLoggingGetLevel() >= OT_LOG_LEVEL_DEBG, NO_ACTION);
138 
139     prefix   = aTx ? "Sent spinel frame" : "Received spinel frame";
140     unpacked = spinel_datatype_unpack(aFrame, aLength, "CiiD", &header, &cmd, &key, &data, &len);
141     EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
142 
143     start += Snprintf(start, static_cast<uint32_t>(end - start), "%s, flg:0x%x, iid:%d, tid:%u, cmd:%s", prefix,
144                       SPINEL_HEADER_GET_FLAG(header), SPINEL_HEADER_GET_IID(header), SPINEL_HEADER_GET_TID(header),
145                       spinel_command_to_cstr(cmd));
146     EXPECT(cmd != SPINEL_CMD_RESET, NO_ACTION);
147 
148     start += Snprintf(start, static_cast<uint32_t>(end - start), ", key:%s", spinel_prop_key_to_cstr(key));
149     EXPECT(cmd != SPINEL_CMD_PROP_VALUE_GET, NO_ACTION);
150 
151     switch (key)
152     {
153     case SPINEL_PROP_LAST_STATUS:
154     {
155         spinel_status_t status;
156 
157         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &status);
158         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
159         start += Snprintf(start, static_cast<uint32_t>(end - start), ", status:%s", spinel_status_to_cstr(status));
160     }
161     break;
162 
163     case SPINEL_PROP_MAC_RAW_STREAM_ENABLED:
164     case SPINEL_PROP_MAC_SRC_MATCH_ENABLED:
165     case SPINEL_PROP_PHY_ENABLED:
166     case SPINEL_PROP_RADIO_COEX_ENABLE:
167     {
168         bool enabled;
169 
170         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_BOOL_S, &enabled);
171         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
172         start += Snprintf(start, static_cast<uint32_t>(end - start), ", enabled:%u", enabled);
173     }
174     break;
175 
176     case SPINEL_PROP_PHY_CCA_THRESHOLD:
177     case SPINEL_PROP_PHY_FEM_LNA_GAIN:
178     case SPINEL_PROP_PHY_RX_SENSITIVITY:
179     case SPINEL_PROP_PHY_RSSI:
180     case SPINEL_PROP_PHY_TX_POWER:
181     {
182         const char *name = nullptr;
183         int8_t      value;
184 
185         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_INT8_S, &value);
186         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
187 
188         switch (key)
189         {
190         case SPINEL_PROP_PHY_TX_POWER:
191             name = "power";
192             break;
193         case SPINEL_PROP_PHY_CCA_THRESHOLD:
194             name = "threshold";
195             break;
196         case SPINEL_PROP_PHY_FEM_LNA_GAIN:
197             name = "gain";
198             break;
199         case SPINEL_PROP_PHY_RX_SENSITIVITY:
200             name = "sensitivity";
201             break;
202         case SPINEL_PROP_PHY_RSSI:
203             name = "rssi";
204             break;
205         }
206 
207         start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%d", name, value);
208     }
209     break;
210 
211     case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
212     case SPINEL_PROP_MAC_SCAN_STATE:
213     case SPINEL_PROP_PHY_CHAN:
214     case SPINEL_PROP_RCP_CSL_ACCURACY:
215     case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
216     {
217         const char *name = nullptr;
218         uint8_t     value;
219 
220         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &value);
221         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
222 
223         switch (key)
224         {
225         case SPINEL_PROP_MAC_SCAN_STATE:
226             name = "state";
227             break;
228         case SPINEL_PROP_RCP_CSL_ACCURACY:
229             name = "accuracy";
230             break;
231         case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
232             name = "uncertainty";
233             break;
234         case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
235             name = "mode";
236             break;
237         case SPINEL_PROP_PHY_CHAN:
238             name = "channel";
239             break;
240         }
241 
242         start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
243     }
244     break;
245 
246     case SPINEL_PROP_MAC_15_4_PANID:
247     case SPINEL_PROP_MAC_15_4_SADDR:
248     case SPINEL_PROP_MAC_SCAN_PERIOD:
249     case SPINEL_PROP_PHY_REGION_CODE:
250     {
251         const char *name = nullptr;
252         uint16_t    value;
253 
254         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &value);
255         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
256 
257         switch (key)
258         {
259         case SPINEL_PROP_MAC_SCAN_PERIOD:
260             name = "period";
261             break;
262         case SPINEL_PROP_PHY_REGION_CODE:
263             name = "region";
264             break;
265         case SPINEL_PROP_MAC_15_4_SADDR:
266             name = "saddr";
267             break;
268         case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
269             name = "saddr";
270             break;
271         case SPINEL_PROP_MAC_15_4_PANID:
272             name = "panid";
273             break;
274         }
275 
276         start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:0x%04x", name, value);
277     }
278     break;
279 
280     case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
281     {
282         uint16_t saddr;
283 
284         start += Snprintf(start, static_cast<uint32_t>(end - start), ", saddr:");
285 
286         if (len < sizeof(saddr))
287         {
288             start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
289         }
290         else
291         {
292             while (len >= sizeof(saddr))
293             {
294                 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &saddr);
295                 EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
296                 data += unpacked;
297                 len -= static_cast<spinel_size_t>(unpacked);
298                 start += Snprintf(start, static_cast<uint32_t>(end - start), "0x%04x ", saddr);
299             }
300         }
301     }
302     break;
303 
304     case SPINEL_PROP_RCP_MAC_FRAME_COUNTER:
305     case SPINEL_PROP_RCP_TIMESTAMP:
306     {
307         const char *name;
308         uint32_t    value;
309 
310         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT32_S, &value);
311         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
312 
313         name = (key == SPINEL_PROP_RCP_TIMESTAMP) ? "timestamp" : "counter";
314         start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
315     }
316     break;
317 
318     case SPINEL_PROP_RADIO_CAPS:
319     case SPINEL_PROP_RCP_API_VERSION:
320     case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
321     {
322         const char  *name;
323         unsigned int value;
324 
325         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &value);
326         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
327 
328         switch (key)
329         {
330         case SPINEL_PROP_RADIO_CAPS:
331             name = "caps";
332             break;
333         case SPINEL_PROP_RCP_API_VERSION:
334             name = "version";
335             break;
336         case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
337             name = "min-host-version";
338             break;
339         default:
340             name = "";
341             break;
342         }
343 
344         start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
345     }
346     break;
347 
348     case SPINEL_PROP_RCP_LOG_CRASH_DUMP:
349     {
350         const char *name;
351         name = "log-crash-dump";
352 
353         start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s", name);
354     }
355     break;
356 
357     case SPINEL_PROP_MAC_ENERGY_SCAN_RESULT:
358     case SPINEL_PROP_PHY_CHAN_MAX_POWER:
359     {
360         const char *name;
361         uint8_t     channel;
362         int8_t      value;
363 
364         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S, &channel, &value);
365         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
366 
367         name = (key == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT) ? "rssi" : "power";
368         start += Snprintf(start, static_cast<uint32_t>(end - start), ", channel:%u, %s:%d", channel, name, value);
369     }
370     break;
371 
372     case SPINEL_PROP_CAPS:
373     {
374         unsigned int capability;
375 
376         start += Snprintf(start, static_cast<uint32_t>(end - start), ", caps:");
377 
378         while (len > 0)
379         {
380             unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &capability);
381             EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
382             data += unpacked;
383             len -= static_cast<spinel_size_t>(unpacked);
384             start += Snprintf(start, static_cast<uint32_t>(end - start), "%s ", spinel_capability_to_cstr(capability));
385         }
386     }
387     break;
388 
389     case SPINEL_PROP_PROTOCOL_VERSION:
390     {
391         unsigned int major;
392         unsigned int minor;
393 
394         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S,
395                                           &major, &minor);
396         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
397         start += Snprintf(start, static_cast<uint32_t>(end - start), ", major:%u, minor:%u", major, minor);
398     }
399     break;
400 
401     case SPINEL_PROP_PHY_CHAN_PREFERRED:
402     case SPINEL_PROP_PHY_CHAN_SUPPORTED:
403     {
404         uint8_t        maskBuffer[kChannelMaskBufferSize];
405         uint32_t       channelMask = 0;
406         const uint8_t *maskData    = maskBuffer;
407         spinel_size_t  maskLength  = sizeof(maskBuffer);
408 
409         unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength);
410         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
411 
412         while (maskLength > 0)
413         {
414             uint8_t channel;
415 
416             unpacked = spinel_datatype_unpack(maskData, maskLength, SPINEL_DATATYPE_UINT8_S, &channel);
417             EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
418             EXPECT(channel < kChannelMaskBufferSize, error = OT_ERROR_PARSE);
419             channelMask |= (1UL << channel);
420 
421             maskData += unpacked;
422             maskLength -= static_cast<spinel_size_t>(unpacked);
423         }
424 
425         start += Snprintf(start, static_cast<uint32_t>(end - start), ", channelMask:0x%08x", channelMask);
426     }
427     break;
428 
429     case SPINEL_PROP_NCP_VERSION:
430     {
431         const char *version;
432 
433         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &version);
434         EXPECT(unpacked >= 0, error = OT_ERROR_PARSE);
435         start += Snprintf(start, static_cast<uint32_t>(end - start), ", version:%s", version);
436     }
437     break;
438 
439     case SPINEL_PROP_STREAM_RAW:
440     {
441         otRadioFrame frame;
442 
443         if (cmd == SPINEL_CMD_PROP_VALUE_IS)
444         {
445             uint16_t     flags;
446             int8_t       noiseFloor;
447             unsigned int receiveError;
448 
449             unpacked = spinel_datatype_unpack(data, len,
450                                               SPINEL_DATATYPE_DATA_WLEN_S                          // Frame
451                                                   SPINEL_DATATYPE_INT8_S                           // RSSI
452                                                       SPINEL_DATATYPE_INT8_S                       // Noise Floor
453                                                           SPINEL_DATATYPE_UINT16_S                 // Flags
454                                                               SPINEL_DATATYPE_STRUCT_S(            // PHY-data
455                                                                   SPINEL_DATATYPE_UINT8_S          // 802.15.4 channel
456                                                                       SPINEL_DATATYPE_UINT8_S      // 802.15.4 LQI
457                                                                           SPINEL_DATATYPE_UINT64_S // Timestamp (us).
458                                                                   ) SPINEL_DATATYPE_STRUCT_S(      // Vendor-data
459                                                                   SPINEL_DATATYPE_UINT_PACKED_S    // Receive error
460                                                                   ),
461                                               &frame.mPsdu, &frame.mLength, &frame.mInfo.mRxInfo.mRssi, &noiseFloor,
462                                               &flags, &frame.mChannel, &frame.mInfo.mRxInfo.mLqi,
463                                               &frame.mInfo.mRxInfo.mTimestamp, &receiveError);
464             EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
465             start += Snprintf(start, static_cast<uint32_t>(end - start), ", len:%u, rssi:%d ...", frame.mLength,
466                               frame.mInfo.mRxInfo.mRssi);
467             OT_UNUSED_VARIABLE(start); // Avoid static analysis error
468             LogDebg("%s", buf);
469 
470             start = buf;
471             start += Snprintf(start, static_cast<uint32_t>(end - start),
472                               "... noise:%d, flags:0x%04x, channel:%u, lqi:%u, timestamp:%lu, rxerr:%u", noiseFloor,
473                               flags, frame.mChannel, frame.mInfo.mRxInfo.mLqi,
474                               static_cast<unsigned long>(frame.mInfo.mRxInfo.mTimestamp), receiveError);
475         }
476         else if (cmd == SPINEL_CMD_PROP_VALUE_SET)
477         {
478             bool csmaCaEnabled;
479             bool isHeaderUpdated;
480             bool isARetx;
481             bool skipAes;
482 
483             unpacked = spinel_datatype_unpack(
484                 data, len,
485                 SPINEL_DATATYPE_DATA_WLEN_S                                   // Frame data
486                     SPINEL_DATATYPE_UINT8_S                                   // Channel
487                         SPINEL_DATATYPE_UINT8_S                               // MaxCsmaBackoffs
488                             SPINEL_DATATYPE_UINT8_S                           // MaxFrameRetries
489                                 SPINEL_DATATYPE_BOOL_S                        // CsmaCaEnabled
490                                     SPINEL_DATATYPE_BOOL_S                    // IsHeaderUpdated
491                                         SPINEL_DATATYPE_BOOL_S                // IsARetx
492                                             SPINEL_DATATYPE_BOOL_S            // SkipAes
493                                                 SPINEL_DATATYPE_UINT32_S      // TxDelay
494                                                     SPINEL_DATATYPE_UINT32_S, // TxDelayBaseTime
495                 &frame.mPsdu, &frame.mLength, &frame.mChannel, &frame.mInfo.mTxInfo.mMaxCsmaBackoffs,
496                 &frame.mInfo.mTxInfo.mMaxFrameRetries, &csmaCaEnabled, &isHeaderUpdated, &isARetx, &skipAes,
497                 &frame.mInfo.mTxInfo.mTxDelay, &frame.mInfo.mTxInfo.mTxDelayBaseTime);
498 
499             EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
500             start += Snprintf(start, static_cast<uint32_t>(end - start),
501                               ", len:%u, channel:%u, maxbackoffs:%u, maxretries:%u ...", frame.mLength, frame.mChannel,
502                               frame.mInfo.mTxInfo.mMaxCsmaBackoffs, frame.mInfo.mTxInfo.mMaxFrameRetries);
503             OT_UNUSED_VARIABLE(start); // Avoid static analysis error
504             LogDebg("%s", buf);
505 
506             start = buf;
507             start += Snprintf(start, static_cast<uint32_t>(end - start),
508                               "... csmaCaEnabled:%u, isHeaderUpdated:%u, isARetx:%u, skipAes:%u"
509                               ", txDelay:%u, txDelayBase:%u",
510                               csmaCaEnabled, isHeaderUpdated, isARetx, skipAes, frame.mInfo.mTxInfo.mTxDelay,
511                               frame.mInfo.mTxInfo.mTxDelayBaseTime);
512         }
513     }
514     break;
515 
516     case SPINEL_PROP_STREAM_DEBUG:
517     {
518         char          debugString[OPENTHREAD_LIB_SPINEL_NCP_LOG_MAX_SIZE + 1];
519         spinel_size_t stringLength = sizeof(debugString);
520 
521         unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, debugString, &stringLength);
522         assert(stringLength < sizeof(debugString));
523         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
524         debugString[stringLength] = '\0';
525         start += Snprintf(start, static_cast<uint32_t>(end - start), ", debug:%s", debugString);
526     }
527     break;
528 
529     case SPINEL_PROP_STREAM_LOG:
530     {
531         const char *logString;
532         uint8_t     logLevel;
533 
534         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &logString);
535         EXPECT(unpacked >= 0, error = OT_ERROR_PARSE);
536         data += unpacked;
537         len -= static_cast<spinel_size_t>(unpacked);
538 
539         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &logLevel);
540         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
541         start += Snprintf(start, static_cast<uint32_t>(end - start), ", level:%u, log:%s", logLevel, logString);
542     }
543     break;
544 
545     case SPINEL_PROP_NEST_STREAM_MFG:
546     {
547         const char *output;
548         size_t      outputLen;
549 
550         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &output, &outputLen);
551         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
552         start += Snprintf(start, static_cast<uint32_t>(end - start), ", diag:%s", output);
553     }
554     break;
555 
556     case SPINEL_PROP_RCP_MAC_KEY:
557     {
558         uint8_t      keyIdMode;
559         uint8_t      keyId;
560         otMacKey     prevKey;
561         unsigned int prevKeyLen = sizeof(otMacKey);
562         otMacKey     currKey;
563         unsigned int currKeyLen = sizeof(otMacKey);
564         otMacKey     nextKey;
565         unsigned int nextKeyLen = sizeof(otMacKey);
566 
567         unpacked = spinel_datatype_unpack(data, len,
568                                           SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S
569                                               SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_WLEN_S,
570                                           &keyIdMode, &keyId, prevKey.m8, &prevKeyLen, currKey.m8, &currKeyLen,
571                                           nextKey.m8, &nextKeyLen);
572         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
573         start += Snprintf(start, static_cast<uint32_t>(end - start),
574                           ", keyIdMode:%u, keyId:%u, prevKey:***, currKey:***, nextKey:***", keyIdMode, keyId);
575     }
576     break;
577 
578     case SPINEL_PROP_HWADDR:
579     case SPINEL_PROP_MAC_15_4_LADDR:
580     {
581         const char *name                    = nullptr;
582         uint8_t     m8[OT_EXT_ADDRESS_SIZE] = {0};
583 
584         unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, &m8[0]);
585         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
586 
587         name = (key == SPINEL_PROP_HWADDR) ? "eui64" : "laddr";
588         start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%02x%02x%02x%02x%02x%02x%02x%02x", name,
589                           m8[0], m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
590     }
591     break;
592 
593     case SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES:
594     {
595         uint8_t m8[OT_EXT_ADDRESS_SIZE];
596 
597         start += Snprintf(start, static_cast<uint32_t>(end - start), ", extaddr:");
598 
599         if (len < sizeof(m8))
600         {
601             start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
602         }
603         else
604         {
605             while (len >= sizeof(m8))
606             {
607                 unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, m8);
608                 EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
609                 data += unpacked;
610                 len -= static_cast<spinel_size_t>(unpacked);
611                 start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x%02x%02x%02x%02x%02x%02x%02x ", m8[0],
612                                   m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
613             }
614         }
615     }
616     break;
617 
618     case SPINEL_PROP_RADIO_COEX_METRICS:
619     {
620         otRadioCoexMetrics metrics;
621         unpacked = spinel_datatype_unpack(
622             data, len,
623             SPINEL_DATATYPE_STRUCT_S(                                    // Tx Coex Metrics Structure
624                 SPINEL_DATATYPE_UINT32_S                                 // NumTxRequest
625                     SPINEL_DATATYPE_UINT32_S                             // NumTxGrantImmediate
626                         SPINEL_DATATYPE_UINT32_S                         // NumTxGrantWait
627                             SPINEL_DATATYPE_UINT32_S                     // NumTxGrantWaitActivated
628                                 SPINEL_DATATYPE_UINT32_S                 // NumTxGrantWaitTimeout
629                                     SPINEL_DATATYPE_UINT32_S             // NumTxGrantDeactivatedDuringRequest
630                                         SPINEL_DATATYPE_UINT32_S         // NumTxDelayedGrant
631                                             SPINEL_DATATYPE_UINT32_S     // AvgTxRequestToGrantTime
632                 ) SPINEL_DATATYPE_STRUCT_S(                              // Rx Coex Metrics Structure
633                 SPINEL_DATATYPE_UINT32_S                                 // NumRxRequest
634                     SPINEL_DATATYPE_UINT32_S                             // NumRxGrantImmediate
635                         SPINEL_DATATYPE_UINT32_S                         // NumRxGrantWait
636                             SPINEL_DATATYPE_UINT32_S                     // NumRxGrantWaitActivated
637                                 SPINEL_DATATYPE_UINT32_S                 // NumRxGrantWaitTimeout
638                                     SPINEL_DATATYPE_UINT32_S             // NumRxGrantDeactivatedDuringRequest
639                                         SPINEL_DATATYPE_UINT32_S         // NumRxDelayedGrant
640                                             SPINEL_DATATYPE_UINT32_S     // AvgRxRequestToGrantTime
641                                                 SPINEL_DATATYPE_UINT32_S // NumRxGrantNone
642                 ) SPINEL_DATATYPE_BOOL_S                                 // Stopped
643                 SPINEL_DATATYPE_UINT32_S,                                // NumGrantGlitch
644             &metrics.mNumTxRequest, &metrics.mNumTxGrantImmediate, &metrics.mNumTxGrantWait,
645             &metrics.mNumTxGrantWaitActivated, &metrics.mNumTxGrantWaitTimeout,
646             &metrics.mNumTxGrantDeactivatedDuringRequest, &metrics.mNumTxDelayedGrant,
647             &metrics.mAvgTxRequestToGrantTime, &metrics.mNumRxRequest, &metrics.mNumRxGrantImmediate,
648             &metrics.mNumRxGrantWait, &metrics.mNumRxGrantWaitActivated, &metrics.mNumRxGrantWaitTimeout,
649             &metrics.mNumRxGrantDeactivatedDuringRequest, &metrics.mNumRxDelayedGrant,
650             &metrics.mAvgRxRequestToGrantTime, &metrics.mNumRxGrantNone, &metrics.mStopped, &metrics.mNumGrantGlitch);
651 
652         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
653 
654         LogDebg("%s ...", buf);
655         LogDebg(" txRequest:%lu", ToUlong(metrics.mNumTxRequest));
656         LogDebg(" txGrantImmediate:%lu", ToUlong(metrics.mNumTxGrantImmediate));
657         LogDebg(" txGrantWait:%lu", ToUlong(metrics.mNumTxGrantWait));
658         LogDebg(" txGrantWaitActivated:%lu", ToUlong(metrics.mNumTxGrantWaitActivated));
659         LogDebg(" txGrantWaitTimeout:%lu", ToUlong(metrics.mNumTxGrantWaitTimeout));
660         LogDebg(" txGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumTxGrantDeactivatedDuringRequest));
661         LogDebg(" txDelayedGrant:%lu", ToUlong(metrics.mNumTxDelayedGrant));
662         LogDebg(" avgTxRequestToGrantTime:%lu", ToUlong(metrics.mAvgTxRequestToGrantTime));
663         LogDebg(" rxRequest:%lu", ToUlong(metrics.mNumRxRequest));
664         LogDebg(" rxGrantImmediate:%lu", ToUlong(metrics.mNumRxGrantImmediate));
665         LogDebg(" rxGrantWait:%lu", ToUlong(metrics.mNumRxGrantWait));
666         LogDebg(" rxGrantWaitActivated:%lu", ToUlong(metrics.mNumRxGrantWaitActivated));
667         LogDebg(" rxGrantWaitTimeout:%lu", ToUlong(metrics.mNumRxGrantWaitTimeout));
668         LogDebg(" rxGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumRxGrantDeactivatedDuringRequest));
669         LogDebg(" rxDelayedGrant:%lu", ToUlong(metrics.mNumRxDelayedGrant));
670         LogDebg(" avgRxRequestToGrantTime:%lu", ToUlong(metrics.mAvgRxRequestToGrantTime));
671         LogDebg(" rxGrantNone:%lu", ToUlong(metrics.mNumRxGrantNone));
672         LogDebg(" stopped:%u", metrics.mStopped);
673 
674         start = buf;
675         start += Snprintf(start, static_cast<uint32_t>(end - start), " grantGlitch:%u", metrics.mNumGrantGlitch);
676     }
677     break;
678 
679     case SPINEL_PROP_MAC_SCAN_MASK:
680     {
681         constexpr uint8_t kNumChannels = 16;
682         uint8_t           channels[kNumChannels];
683         spinel_size_t     size;
684 
685         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_DATA_S, channels, &size);
686         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
687         start += Snprintf(start, static_cast<uint32_t>(end - start), ", channels:");
688 
689         for (spinel_size_t i = 0; i < size; i++)
690         {
691             start += Snprintf(start, static_cast<uint32_t>(end - start), "%u ", channels[i]);
692         }
693     }
694     break;
695 
696     case SPINEL_PROP_RCP_ENH_ACK_PROBING:
697     {
698         uint16_t saddr;
699         uint8_t  m8[OT_EXT_ADDRESS_SIZE];
700         uint8_t  flags;
701 
702         unpacked = spinel_datatype_unpack(
703             data, len, SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S, &saddr, m8, &flags);
704 
705         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
706         start += Snprintf(start, static_cast<uint32_t>(end - start),
707                           ", saddr:%04x, extaddr:%02x%02x%02x%02x%02x%02x%02x%02x, flags:0x%02x", saddr, m8[0], m8[1],
708                           m8[2], m8[3], m8[4], m8[5], m8[6], m8[7], flags);
709     }
710     break;
711 
712     case SPINEL_PROP_PHY_CALIBRATED_POWER:
713     {
714         if (cmd == SPINEL_CMD_PROP_VALUE_INSERT)
715         {
716             uint8_t      channel;
717             int16_t      actualPower;
718             uint8_t     *rawPowerSetting;
719             unsigned int rawPowerSettingLength;
720 
721             unpacked = spinel_datatype_unpack(
722                 data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_DATA_WLEN_S, &channel,
723                 &actualPower, &rawPowerSetting, &rawPowerSettingLength);
724             EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
725 
726             start += Snprintf(start, static_cast<uint32_t>(end - start),
727                               ", ch:%u, actualPower:%d, rawPowerSetting:", channel, actualPower);
728             for (unsigned int i = 0; i < rawPowerSettingLength; i++)
729             {
730                 start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x", rawPowerSetting[i]);
731             }
732         }
733     }
734     break;
735 
736     case SPINEL_PROP_PHY_CHAN_TARGET_POWER:
737     {
738         uint8_t channel;
739         int16_t targetPower;
740 
741         unpacked =
742             spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, &channel, &targetPower);
743         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
744         start += Snprintf(start, static_cast<uint32_t>(end - start), ", ch:%u, targetPower:%d", channel, targetPower);
745     }
746     break;
747     }
748 
749 exit:
750     OT_UNUSED_VARIABLE(start); // Avoid static analysis error
751     if (error == OT_ERROR_NONE)
752     {
753         LogDebg("%s", buf);
754     }
755     else if (prefix != nullptr)
756     {
757         LogDebg("%s, failed to parse spinel frame !", prefix);
758     }
759 }
760 
761 } // namespace Spinel
762 } // namespace ot
763