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