1 /*
2  *  Copyright (c) 2020, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements the spinel based radio transceiver.
32  */
33 
34 #include "radio_spinel.hpp"
35 
36 #include <assert.h>
37 #include <errno.h>
38 #include <stdarg.h>
39 #include <stdlib.h>
40 
41 #include <openthread/logging.h>
42 #include <openthread/platform/diag.h>
43 #include <openthread/platform/time.h>
44 
45 #include "common/code_utils.hpp"
46 #include "common/encoding.hpp"
47 #include "common/new.hpp"
48 #include "lib/platform/exit_code.h"
49 #include "lib/spinel/spinel_decoder.hpp"
50 
51 namespace ot {
52 namespace Spinel {
53 
54 char RadioSpinel::sVersion[kVersionStringSize] = "";
55 
56 otExtAddress RadioSpinel::sIeeeEui64;
57 
58 bool RadioSpinel::sIsReady = false; ///< NCP ready.
59 
60 bool RadioSpinel::sSupportsLogStream =
61     false; ///< RCP supports `LOG_STREAM` property with OpenThread log meta-data format.
62 
63 bool RadioSpinel::sSupportsResetToBootloader = false; ///< RCP supports resetting into bootloader mode.
64 
65 otRadioCaps RadioSpinel::sRadioCaps = OT_RADIO_CAPS_NONE;
66 
IsFrameForUs(spinel_iid_t aIid)67 inline bool RadioSpinel::IsFrameForUs(spinel_iid_t aIid)
68 {
69     bool found = false;
70 
71     for (spinel_iid_t iid : mIidList)
72     {
73         if (aIid == iid)
74         {
75             ExitNow(found = true);
76         }
77     }
78 
79 exit:
80     return found;
81 }
82 
RadioSpinel(void)83 RadioSpinel::RadioSpinel(void)
84     : mInstance(nullptr)
85     , mSpinelInterface(nullptr)
86     , mCmdTidsInUse(0)
87     , mCmdNextTid(1)
88     , mTxRadioTid(0)
89     , mWaitingTid(0)
90     , mWaitingKey(SPINEL_PROP_LAST_STATUS)
91     , mPropertyFormat(nullptr)
92     , mExpectedCommand(0)
93     , mError(OT_ERROR_NONE)
94     , mIid(SPINEL_HEADER_INVALID_IID)
95     , mTransmitFrame(nullptr)
96     , mShortAddress(0)
97     , mPanId(0xffff)
98     , mChannel(0)
99     , mRxSensitivity(0)
100     , mState(kStateDisabled)
101     , mIsPromiscuous(false)
102     , mRxOnWhenIdle(true)
103     , mIsTimeSynced(false)
104 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
105     , mRcpFailureCount(0)
106     , mRcpFailure(kRcpFailureNone)
107     , mSrcMatchShortEntryCount(0)
108     , mSrcMatchExtEntryCount(0)
109     , mMacKeySet(false)
110     , mCcaEnergyDetectThresholdSet(false)
111     , mTransmitPowerSet(false)
112     , mCoexEnabledSet(false)
113     , mFemLnaGainSet(false)
114     , mEnergyScanning(false)
115     , mMacFrameCounterSet(false)
116 #endif
117 #if OPENTHREAD_CONFIG_DIAG_ENABLE
118     , mDiagMode(false)
119     , mDiagOutput(nullptr)
120     , mDiagOutputMaxLen(0)
121 #endif
122     , mTxRadioEndUs(UINT64_MAX)
123     , mRadioTimeRecalcStart(UINT64_MAX)
124     , mRadioTimeOffset(UINT64_MAX)
125 {
126     memset(mIidList, SPINEL_HEADER_INVALID_IID, sizeof(mIidList));
127     memset(&mRadioSpinelMetrics, 0, sizeof(mRadioSpinelMetrics));
128     memset(&mCallbacks, 0, sizeof(mCallbacks));
129 }
130 
Init(SpinelInterface & aSpinelInterface,bool aResetRadio,bool aSkipRcpCompatibilityCheck,const spinel_iid_t * aIidList,uint8_t aIidListLength)131 void RadioSpinel::Init(SpinelInterface    &aSpinelInterface,
132                        bool                aResetRadio,
133                        bool                aSkipRcpCompatibilityCheck,
134                        const spinel_iid_t *aIidList,
135                        uint8_t             aIidListLength)
136 {
137     otError error = OT_ERROR_NONE;
138     bool    supportsRcpApiVersion;
139     bool    supportsRcpMinHostApiVersion;
140 
141 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
142     mResetRadioOnStartup = aResetRadio;
143 #endif
144 
145     mSpinelInterface = &aSpinelInterface;
146     SuccessOrDie(mSpinelInterface->Init(HandleReceivedFrame, this, mRxFrameBuffer));
147 
148     VerifyOrDie(aIidList != nullptr, OT_EXIT_INVALID_ARGUMENTS);
149     VerifyOrDie(aIidListLength != 0 && aIidListLength <= OT_ARRAY_LENGTH(mIidList), OT_EXIT_INVALID_ARGUMENTS);
150     mIid = aIidList[0];
151     memset(mIidList, SPINEL_HEADER_INVALID_IID, sizeof(mIidList));
152     memcpy(mIidList, aIidList, aIidListLength * sizeof(spinel_iid_t));
153 
154     ResetRcp(aResetRadio);
155     SuccessOrExit(error = CheckSpinelVersion());
156     SuccessOrExit(error = Get(SPINEL_PROP_NCP_VERSION, SPINEL_DATATYPE_UTF8_S, sVersion, sizeof(sVersion)));
157     SuccessOrExit(error = Get(SPINEL_PROP_HWADDR, SPINEL_DATATYPE_EUI64_S, sIeeeEui64.m8));
158 
159     VerifyOrDie(IsRcp(supportsRcpApiVersion, supportsRcpMinHostApiVersion), OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
160 
161     if (!aSkipRcpCompatibilityCheck)
162     {
163         SuccessOrDie(CheckRcpApiVersion(supportsRcpApiVersion, supportsRcpMinHostApiVersion));
164         SuccessOrDie(CheckRadioCapabilities());
165     }
166 
167     mRxRadioFrame.mPsdu  = mRxPsdu;
168     mTxRadioFrame.mPsdu  = mTxPsdu;
169     mAckRadioFrame.mPsdu = mAckPsdu;
170 
171 exit:
172     SuccessOrDie(error);
173 }
174 
SetCallbacks(const struct RadioSpinelCallbacks & aCallbacks)175 void RadioSpinel::SetCallbacks(const struct RadioSpinelCallbacks &aCallbacks)
176 {
177 #if OPENTHREAD_CONFIG_DIAG_ENABLE
178     assert(aCallbacks.mDiagReceiveDone != nullptr);
179     assert(aCallbacks.mDiagTransmitDone != nullptr);
180 #endif
181     assert(aCallbacks.mEnergyScanDone != nullptr);
182     assert(aCallbacks.mReceiveDone != nullptr);
183     assert(aCallbacks.mTransmitDone != nullptr);
184     assert(aCallbacks.mTxStarted != nullptr);
185 
186     mCallbacks = aCallbacks;
187 }
188 
ResetRcp(bool aResetRadio)189 void RadioSpinel::ResetRcp(bool aResetRadio)
190 {
191     bool hardwareReset;
192     bool resetDone = false;
193 
194     // Avoid resetting the device twice in a row in Multipan RCP architecture
195     VerifyOrExit(!sIsReady, resetDone = true);
196 
197     mWaitingKey = SPINEL_PROP_LAST_STATUS;
198 
199     if (aResetRadio && (SendReset(SPINEL_RESET_STACK) == OT_ERROR_NONE) && (!sIsReady) &&
200         (WaitResponse(false) == OT_ERROR_NONE))
201     {
202         VerifyOrExit(sIsReady, resetDone = false);
203         LogInfo("Software reset RCP successfully");
204         ExitNow(resetDone = true);
205     }
206 
207     hardwareReset = (mSpinelInterface->HardwareReset() == OT_ERROR_NONE);
208 
209     if (hardwareReset)
210     {
211         SuccessOrExit(WaitResponse(false));
212     }
213 
214     resetDone = true;
215 
216     if (hardwareReset)
217     {
218         LogInfo("Hardware reset RCP successfully");
219     }
220     else
221     {
222         LogInfo("RCP self reset successfully");
223     }
224 
225 exit:
226     if (!resetDone)
227     {
228         LogCrit("Failed to reset RCP!");
229         DieNow(OT_EXIT_FAILURE);
230     }
231 }
232 
CheckSpinelVersion(void)233 otError RadioSpinel::CheckSpinelVersion(void)
234 {
235     otError      error = OT_ERROR_NONE;
236     unsigned int versionMajor;
237     unsigned int versionMinor;
238 
239     SuccessOrExit(error =
240                       Get(SPINEL_PROP_PROTOCOL_VERSION, (SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S),
241                           &versionMajor, &versionMinor));
242 
243     if ((versionMajor != SPINEL_PROTOCOL_VERSION_THREAD_MAJOR) ||
244         (versionMinor != SPINEL_PROTOCOL_VERSION_THREAD_MINOR))
245     {
246         LogCrit("Spinel version mismatch - Posix:%d.%d, RCP:%d.%d", SPINEL_PROTOCOL_VERSION_THREAD_MAJOR,
247                 SPINEL_PROTOCOL_VERSION_THREAD_MINOR, versionMajor, versionMinor);
248         DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
249     }
250 
251 exit:
252     return error;
253 }
254 
IsRcp(bool & aSupportsRcpApiVersion,bool & aSupportsRcpMinHostApiVersion)255 bool RadioSpinel::IsRcp(bool &aSupportsRcpApiVersion, bool &aSupportsRcpMinHostApiVersion)
256 {
257     uint8_t        capsBuffer[kCapsBufferSize];
258     const uint8_t *capsData         = capsBuffer;
259     spinel_size_t  capsLength       = sizeof(capsBuffer);
260     bool           supportsRawRadio = false;
261     bool           isRcp            = false;
262 
263     aSupportsRcpApiVersion        = false;
264     aSupportsRcpMinHostApiVersion = false;
265 
266     SuccessOrDie(Get(SPINEL_PROP_CAPS, SPINEL_DATATYPE_DATA_S, capsBuffer, &capsLength));
267 
268     while (capsLength > 0)
269     {
270         unsigned int   capability;
271         spinel_ssize_t unpacked;
272 
273         unpacked = spinel_datatype_unpack(capsData, capsLength, SPINEL_DATATYPE_UINT_PACKED_S, &capability);
274         VerifyOrDie(unpacked > 0, OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
275 
276         if (capability == SPINEL_CAP_MAC_RAW)
277         {
278             supportsRawRadio = true;
279         }
280 
281         if (capability == SPINEL_CAP_CONFIG_RADIO)
282         {
283             isRcp = true;
284         }
285 
286         if (capability == SPINEL_CAP_OPENTHREAD_LOG_METADATA)
287         {
288             sSupportsLogStream = true;
289         }
290 
291         if (capability == SPINEL_CAP_RCP_API_VERSION)
292         {
293             aSupportsRcpApiVersion = true;
294         }
295 
296         if (capability == SPINEL_CAP_RCP_RESET_TO_BOOTLOADER)
297         {
298             sSupportsResetToBootloader = true;
299         }
300 
301         if (capability == SPINEL_PROP_RCP_MIN_HOST_API_VERSION)
302         {
303             aSupportsRcpMinHostApiVersion = true;
304         }
305 
306         capsData += unpacked;
307         capsLength -= static_cast<spinel_size_t>(unpacked);
308     }
309 
310     if (!supportsRawRadio && isRcp)
311     {
312         LogCrit("RCP capability list does not include support for radio/raw mode");
313         DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
314     }
315 
316     return isRcp;
317 }
318 
CheckRadioCapabilities(void)319 otError RadioSpinel::CheckRadioCapabilities(void)
320 {
321     const otRadioCaps kRequiredRadioCaps =
322 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
323         OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING |
324 #endif
325         OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_TRANSMIT_RETRIES | OT_RADIO_CAPS_CSMA_BACKOFF;
326 
327     otError      error = OT_ERROR_NONE;
328     unsigned int radioCaps;
329 
330     SuccessOrExit(error = Get(SPINEL_PROP_RADIO_CAPS, SPINEL_DATATYPE_UINT_PACKED_S, &radioCaps));
331     sRadioCaps = static_cast<otRadioCaps>(radioCaps);
332 
333     if ((sRadioCaps & kRequiredRadioCaps) != kRequiredRadioCaps)
334     {
335         otRadioCaps missingCaps = (sRadioCaps & kRequiredRadioCaps) ^ kRequiredRadioCaps;
336 
337         // missingCaps may be an unused variable when LogCrit is blank
338         // avoid compiler warning in that case
339         OT_UNUSED_VARIABLE(missingCaps);
340 
341         LogCrit("RCP is missing required capabilities: %s%s%s%s%s",
342                 (missingCaps & OT_RADIO_CAPS_ACK_TIMEOUT) ? "ack-timeout " : "",
343                 (missingCaps & OT_RADIO_CAPS_TRANSMIT_RETRIES) ? "tx-retries " : "",
344                 (missingCaps & OT_RADIO_CAPS_CSMA_BACKOFF) ? "CSMA-backoff " : "",
345                 (missingCaps & OT_RADIO_CAPS_TRANSMIT_SEC) ? "tx-security " : "",
346                 (missingCaps & OT_RADIO_CAPS_TRANSMIT_TIMING) ? "tx-timing " : "");
347 
348         DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
349     }
350 
351 exit:
352     return error;
353 }
354 
CheckRcpApiVersion(bool aSupportsRcpApiVersion,bool aSupportsRcpMinHostApiVersion)355 otError RadioSpinel::CheckRcpApiVersion(bool aSupportsRcpApiVersion, bool aSupportsRcpMinHostApiVersion)
356 {
357     otError error = OT_ERROR_NONE;
358 
359     static_assert(SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION <= SPINEL_RCP_API_VERSION,
360                   "MIN_HOST_SUPPORTED_RCP_API_VERSION must be smaller than or equal to RCP_API_VERSION");
361 
362     if (aSupportsRcpApiVersion)
363     {
364         // Make sure RCP is not too old and its version is within the
365         // range host supports.
366 
367         unsigned int rcpApiVersion;
368 
369         SuccessOrExit(error = Get(SPINEL_PROP_RCP_API_VERSION, SPINEL_DATATYPE_UINT_PACKED_S, &rcpApiVersion));
370 
371         if (rcpApiVersion < SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION)
372         {
373             LogCrit("RCP and host are using incompatible API versions");
374             LogCrit("RCP API Version %u is older than min required by host %u", rcpApiVersion,
375                     SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION);
376             DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
377         }
378     }
379 
380     if (aSupportsRcpMinHostApiVersion)
381     {
382         // Check with RCP about min host API version it can work with,
383         // and make sure on host side our version is within the supported
384         // range.
385 
386         unsigned int minHostRcpApiVersion;
387 
388         SuccessOrExit(
389             error = Get(SPINEL_PROP_RCP_MIN_HOST_API_VERSION, SPINEL_DATATYPE_UINT_PACKED_S, &minHostRcpApiVersion));
390 
391         if (SPINEL_RCP_API_VERSION < minHostRcpApiVersion)
392         {
393             LogCrit("RCP and host are using incompatible API versions");
394             LogCrit("RCP requires min host API version %u but host is older and at version %u", minHostRcpApiVersion,
395                     SPINEL_RCP_API_VERSION);
396             DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
397         }
398     }
399 
400 exit:
401     return error;
402 }
403 
Deinit(void)404 void RadioSpinel::Deinit(void)
405 {
406     if (mSpinelInterface != nullptr)
407     {
408         mSpinelInterface->Deinit();
409         mSpinelInterface = nullptr;
410     }
411 
412     // This allows implementing pseudo reset.
413     sIsReady = false;
414     new (this) RadioSpinel();
415 }
416 
HandleReceivedFrame(void * aContext)417 void RadioSpinel::HandleReceivedFrame(void *aContext) { static_cast<RadioSpinel *>(aContext)->HandleReceivedFrame(); }
418 
HandleReceivedFrame(void)419 void RadioSpinel::HandleReceivedFrame(void)
420 {
421     otError        error = OT_ERROR_NONE;
422     uint8_t        header;
423     spinel_ssize_t unpacked;
424 
425     LogSpinelFrame(mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetLength(), false);
426     unpacked = spinel_datatype_unpack(mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetLength(), "C", &header);
427 
428     // Accept spinel messages with the correct IID or broadcast IID.
429     spinel_iid_t iid = SPINEL_HEADER_GET_IID(header);
430 
431     if (!IsFrameForUs(iid))
432     {
433         mRxFrameBuffer.DiscardFrame();
434         ExitNow();
435     }
436 
437     VerifyOrExit(unpacked > 0 && (header & SPINEL_HEADER_FLAG) == SPINEL_HEADER_FLAG, error = OT_ERROR_PARSE);
438 
439     if (SPINEL_HEADER_GET_TID(header) == 0)
440     {
441         HandleNotification(mRxFrameBuffer);
442     }
443     else
444     {
445         HandleResponse(mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetLength());
446         mRxFrameBuffer.DiscardFrame();
447     }
448 
449 exit:
450     if (error != OT_ERROR_NONE)
451     {
452         mRxFrameBuffer.DiscardFrame();
453         LogWarn("Error handling hdlc frame: %s", otThreadErrorToString(error));
454     }
455 
456     UpdateParseErrorCount(error);
457 }
458 
HandleNotification(SpinelInterface::RxFrameBuffer & aFrameBuffer)459 void RadioSpinel::HandleNotification(SpinelInterface::RxFrameBuffer &aFrameBuffer)
460 {
461     spinel_prop_key_t key;
462     spinel_size_t     len = 0;
463     spinel_ssize_t    unpacked;
464     uint8_t          *data = nullptr;
465     uint32_t          cmd;
466     uint8_t           header;
467     otError           error           = OT_ERROR_NONE;
468     bool              shouldSaveFrame = false;
469 
470     unpacked = spinel_datatype_unpack(aFrameBuffer.GetFrame(), aFrameBuffer.GetLength(), "CiiD", &header, &cmd, &key,
471                                       &data, &len);
472     VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
473     VerifyOrExit(SPINEL_HEADER_GET_TID(header) == 0, error = OT_ERROR_PARSE);
474 
475     switch (cmd)
476     {
477     case SPINEL_CMD_PROP_VALUE_IS:
478         // Some spinel properties cannot be handled during `WaitResponse()`, we must cache these events.
479         // `mWaitingTid` is released immediately after received the response. And `mWaitingKey` is be set
480         // to `SPINEL_PROP_LAST_STATUS` at the end of `WaitResponse()`.
481 
482         if (!IsSafeToHandleNow(key))
483         {
484             ExitNow(shouldSaveFrame = true);
485         }
486 
487         HandleValueIs(key, data, static_cast<uint16_t>(len));
488         break;
489 
490     case SPINEL_CMD_PROP_VALUE_INSERTED:
491     case SPINEL_CMD_PROP_VALUE_REMOVED:
492         LogInfo("Ignored command %lu", ToUlong(cmd));
493         break;
494 
495     default:
496         ExitNow(error = OT_ERROR_PARSE);
497     }
498 
499 exit:
500     if (!shouldSaveFrame || aFrameBuffer.SaveFrame() != OT_ERROR_NONE)
501     {
502         aFrameBuffer.DiscardFrame();
503 
504         if (shouldSaveFrame)
505         {
506             LogCrit("RX Spinel buffer full, dropped incoming frame");
507         }
508     }
509 
510     UpdateParseErrorCount(error);
511     LogIfFail("Error processing notification", error);
512 }
513 
HandleNotification(const uint8_t * aFrame,uint16_t aLength)514 void RadioSpinel::HandleNotification(const uint8_t *aFrame, uint16_t aLength)
515 {
516     spinel_prop_key_t key;
517     spinel_size_t     len = 0;
518     spinel_ssize_t    unpacked;
519     uint8_t          *data = nullptr;
520     uint32_t          cmd;
521     uint8_t           header;
522     otError           error = OT_ERROR_NONE;
523 
524     unpacked = spinel_datatype_unpack(aFrame, aLength, "CiiD", &header, &cmd, &key, &data, &len);
525     VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
526     VerifyOrExit(SPINEL_HEADER_GET_TID(header) == 0, error = OT_ERROR_PARSE);
527     VerifyOrExit(cmd == SPINEL_CMD_PROP_VALUE_IS);
528     HandleValueIs(key, data, static_cast<uint16_t>(len));
529 
530 exit:
531     UpdateParseErrorCount(error);
532     LogIfFail("Error processing saved notification", error);
533 }
534 
HandleResponse(const uint8_t * aBuffer,uint16_t aLength)535 void RadioSpinel::HandleResponse(const uint8_t *aBuffer, uint16_t aLength)
536 {
537     spinel_prop_key_t key;
538     uint8_t          *data   = nullptr;
539     spinel_size_t     len    = 0;
540     uint8_t           header = 0;
541     uint32_t          cmd    = 0;
542     spinel_ssize_t    rval   = 0;
543     otError           error  = OT_ERROR_NONE;
544 
545     rval = spinel_datatype_unpack(aBuffer, aLength, "CiiD", &header, &cmd, &key, &data, &len);
546     VerifyOrExit(rval > 0 && cmd >= SPINEL_CMD_PROP_VALUE_IS && cmd <= SPINEL_CMD_PROP_VALUE_REMOVED,
547                  error = OT_ERROR_PARSE);
548 
549     if (mWaitingTid == SPINEL_HEADER_GET_TID(header))
550     {
551         HandleWaitingResponse(cmd, key, data, static_cast<uint16_t>(len));
552         FreeTid(mWaitingTid);
553         mWaitingTid = 0;
554     }
555     else if (mTxRadioTid == SPINEL_HEADER_GET_TID(header))
556     {
557         if (mState == kStateTransmitting)
558         {
559             HandleTransmitDone(cmd, key, data, static_cast<uint16_t>(len));
560         }
561 
562         FreeTid(mTxRadioTid);
563         mTxRadioTid = 0;
564     }
565     else
566     {
567         LogWarn("Unexpected Spinel transaction message: %u", SPINEL_HEADER_GET_TID(header));
568         error = OT_ERROR_DROP;
569     }
570 
571 exit:
572     UpdateParseErrorCount(error);
573     LogIfFail("Error processing response", error);
574 }
575 
HandleWaitingResponse(uint32_t aCommand,spinel_prop_key_t aKey,const uint8_t * aBuffer,uint16_t aLength)576 void RadioSpinel::HandleWaitingResponse(uint32_t          aCommand,
577                                         spinel_prop_key_t aKey,
578                                         const uint8_t    *aBuffer,
579                                         uint16_t          aLength)
580 {
581     if (aKey == SPINEL_PROP_LAST_STATUS)
582     {
583         spinel_status_t status;
584         spinel_ssize_t  unpacked = spinel_datatype_unpack(aBuffer, aLength, "i", &status);
585 
586         VerifyOrExit(unpacked > 0, mError = OT_ERROR_PARSE);
587         mError = SpinelStatusToOtError(status);
588     }
589 #if OPENTHREAD_CONFIG_DIAG_ENABLE
590     else if (aKey == SPINEL_PROP_NEST_STREAM_MFG)
591     {
592         spinel_ssize_t unpacked;
593 
594         mError = OT_ERROR_NONE;
595         VerifyOrExit(mDiagOutput != nullptr);
596         unpacked =
597             spinel_datatype_unpack_in_place(aBuffer, aLength, SPINEL_DATATYPE_UTF8_S, mDiagOutput, &mDiagOutputMaxLen);
598         VerifyOrExit(unpacked > 0, mError = OT_ERROR_PARSE);
599     }
600 #endif
601     else if (aKey == mWaitingKey)
602     {
603         if (mPropertyFormat)
604         {
605             if (static_cast<spinel_datatype_t>(mPropertyFormat[0]) == SPINEL_DATATYPE_VOID_C)
606             {
607                 // reserved SPINEL_DATATYPE_VOID_C indicate caller want to parse the spinel response itself
608                 ResponseHandler handler = va_arg(mPropertyArgs, ResponseHandler);
609 
610                 assert(handler != nullptr);
611                 mError = (this->*handler)(aBuffer, aLength);
612             }
613             else
614             {
615                 spinel_ssize_t unpacked =
616                     spinel_datatype_vunpack_in_place(aBuffer, aLength, mPropertyFormat, mPropertyArgs);
617 
618                 VerifyOrExit(unpacked > 0, mError = OT_ERROR_PARSE);
619                 mError = OT_ERROR_NONE;
620             }
621         }
622         else
623         {
624             if (aCommand == mExpectedCommand)
625             {
626                 mError = OT_ERROR_NONE;
627             }
628             else
629             {
630                 mError = OT_ERROR_DROP;
631             }
632         }
633     }
634     else
635     {
636         mError = OT_ERROR_DROP;
637     }
638 
639 exit:
640     UpdateParseErrorCount(mError);
641     LogIfFail("Error processing result", mError);
642 }
643 
HandleValueIs(spinel_prop_key_t aKey,const uint8_t * aBuffer,uint16_t aLength)644 void RadioSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength)
645 {
646     otError        error = OT_ERROR_NONE;
647     spinel_ssize_t unpacked;
648 
649     if (aKey == SPINEL_PROP_STREAM_RAW)
650     {
651         SuccessOrExit(error = ParseRadioFrame(mRxRadioFrame, aBuffer, aLength, unpacked));
652         RadioReceive();
653     }
654     else if (aKey == SPINEL_PROP_LAST_STATUS)
655     {
656         spinel_status_t status = SPINEL_STATUS_OK;
657 
658         unpacked = spinel_datatype_unpack(aBuffer, aLength, "i", &status);
659         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
660 
661         if (status >= SPINEL_STATUS_RESET__BEGIN && status <= SPINEL_STATUS_RESET__END)
662         {
663             if (IsEnabled())
664             {
665                 HandleRcpUnexpectedReset(status);
666                 ExitNow();
667             }
668 
669             LogInfo("RCP reset: %s", spinel_status_to_cstr(status));
670             sIsReady = true;
671         }
672         else if (status == SPINEL_STATUS_SWITCHOVER_DONE || status == SPINEL_STATUS_SWITCHOVER_FAILED)
673         {
674             if (mCallbacks.mSwitchoverDone != nullptr)
675             {
676                 mCallbacks.mSwitchoverDone(mInstance, status == SPINEL_STATUS_SWITCHOVER_DONE);
677             }
678         }
679         else
680         {
681             LogInfo("RCP last status: %s", spinel_status_to_cstr(status));
682         }
683     }
684     else if (aKey == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT)
685     {
686         uint8_t scanChannel;
687         int8_t  maxRssi;
688 
689         unpacked = spinel_datatype_unpack(aBuffer, aLength, "Cc", &scanChannel, &maxRssi);
690 
691         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
692 
693 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
694         mEnergyScanning = false;
695 #endif
696 
697         mCallbacks.mEnergyScanDone(mInstance, maxRssi);
698     }
699     else if (aKey == SPINEL_PROP_STREAM_DEBUG)
700     {
701         char         logStream[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE + 1];
702         unsigned int len = sizeof(logStream);
703 
704         unpacked = spinel_datatype_unpack_in_place(aBuffer, aLength, SPINEL_DATATYPE_DATA_S, logStream, &len);
705         assert(len < sizeof(logStream));
706         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
707         logStream[len] = '\0';
708         LogDebg("RCP => %s", logStream);
709     }
710     else if ((aKey == SPINEL_PROP_STREAM_LOG) && sSupportsLogStream)
711     {
712         const char *logString;
713         uint8_t     logLevel;
714 
715         unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UTF8_S, &logString);
716         VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
717         aBuffer += unpacked;
718         aLength -= unpacked;
719 
720         unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UINT8_S, &logLevel);
721         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
722 
723         switch (logLevel)
724         {
725         case SPINEL_NCP_LOG_LEVEL_EMERG:
726         case SPINEL_NCP_LOG_LEVEL_ALERT:
727         case SPINEL_NCP_LOG_LEVEL_CRIT:
728             LogCrit("RCP => %s", logString);
729             break;
730 
731         case SPINEL_NCP_LOG_LEVEL_ERR:
732         case SPINEL_NCP_LOG_LEVEL_WARN:
733             LogWarn("RCP => %s", logString);
734             break;
735 
736         case SPINEL_NCP_LOG_LEVEL_NOTICE:
737             LogNote("RCP => %s", logString);
738             break;
739 
740         case SPINEL_NCP_LOG_LEVEL_INFO:
741             LogInfo("RCP => %s", logString);
742             break;
743 
744         case SPINEL_NCP_LOG_LEVEL_DEBUG:
745         default:
746             LogDebg("RCP => %s", logString);
747             break;
748         }
749     }
750 #if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
751     else if (aKey >= SPINEL_PROP_VENDOR__BEGIN && aKey < SPINEL_PROP_VENDOR__END)
752     {
753         error = VendorHandleValueIs(aKey);
754     }
755 #endif
756 
757 exit:
758     UpdateParseErrorCount(error);
759     LogIfFail("Failed to handle ValueIs", error);
760 }
761 
ParseRadioFrame(otRadioFrame & aFrame,const uint8_t * aBuffer,uint16_t aLength,spinel_ssize_t & aUnpacked)762 otError RadioSpinel::ParseRadioFrame(otRadioFrame   &aFrame,
763                                      const uint8_t  *aBuffer,
764                                      uint16_t        aLength,
765                                      spinel_ssize_t &aUnpacked)
766 {
767     otError        error        = OT_ERROR_NONE;
768     uint16_t       flags        = 0;
769     int8_t         noiseFloor   = -128;
770     spinel_size_t  size         = OT_RADIO_FRAME_MAX_SIZE;
771     unsigned int   receiveError = 0;
772     spinel_ssize_t unpacked;
773 
774     VerifyOrExit(aLength > 0, aFrame.mLength = 0);
775 
776     unpacked = spinel_datatype_unpack_in_place(aBuffer, aLength,
777                                                SPINEL_DATATYPE_DATA_WLEN_S                          // Frame
778                                                    SPINEL_DATATYPE_INT8_S                           // RSSI
779                                                        SPINEL_DATATYPE_INT8_S                       // Noise Floor
780                                                            SPINEL_DATATYPE_UINT16_S                 // Flags
781                                                                SPINEL_DATATYPE_STRUCT_S(            // PHY-data
782                                                                    SPINEL_DATATYPE_UINT8_S          // 802.15.4 channel
783                                                                        SPINEL_DATATYPE_UINT8_S      // 802.15.4 LQI
784                                                                            SPINEL_DATATYPE_UINT64_S // Timestamp (us).
785                                                                    ) SPINEL_DATATYPE_STRUCT_S(      // Vendor-data
786                                                                    SPINEL_DATATYPE_UINT_PACKED_S    // Receive error
787                                                                    ),
788                                                aFrame.mPsdu, &size, &aFrame.mInfo.mRxInfo.mRssi, &noiseFloor, &flags,
789                                                &aFrame.mChannel, &aFrame.mInfo.mRxInfo.mLqi,
790                                                &aFrame.mInfo.mRxInfo.mTimestamp, &receiveError);
791 
792     VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
793     aUnpacked = unpacked;
794 
795     aBuffer += unpacked;
796     aLength -= static_cast<uint16_t>(unpacked);
797 
798     if (sRadioCaps & OT_RADIO_CAPS_TRANSMIT_SEC)
799     {
800         unpacked =
801             spinel_datatype_unpack_in_place(aBuffer, aLength,
802                                             SPINEL_DATATYPE_STRUCT_S(        // MAC-data
803                                                 SPINEL_DATATYPE_UINT8_S      // Security key index
804                                                     SPINEL_DATATYPE_UINT32_S // Security frame counter
805                                                 ),
806                                             &aFrame.mInfo.mRxInfo.mAckKeyId, &aFrame.mInfo.mRxInfo.mAckFrameCounter);
807 
808         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
809         aUnpacked += unpacked;
810 
811 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
812         if (flags & SPINEL_MD_FLAG_ACKED_SEC)
813         {
814             mMacFrameCounterSet = true;
815             mMacFrameCounter    = aFrame.mInfo.mRxInfo.mAckFrameCounter;
816         }
817 #endif
818     }
819 
820     if (receiveError == OT_ERROR_NONE)
821     {
822         aFrame.mLength = static_cast<uint8_t>(size);
823 
824         aFrame.mInfo.mRxInfo.mAckedWithFramePending = ((flags & SPINEL_MD_FLAG_ACKED_FP) != 0);
825         aFrame.mInfo.mRxInfo.mAckedWithSecEnhAck    = ((flags & SPINEL_MD_FLAG_ACKED_SEC) != 0);
826     }
827     else if (receiveError < OT_NUM_ERRORS)
828     {
829         error = static_cast<otError>(receiveError);
830     }
831     else
832     {
833         error = OT_ERROR_PARSE;
834     }
835 
836 exit:
837     UpdateParseErrorCount(error);
838     LogIfFail("Handle radio frame failed", error);
839     return error;
840 }
841 
ProcessFrameQueue(void)842 void RadioSpinel::ProcessFrameQueue(void)
843 {
844     uint8_t *frame = nullptr;
845     uint16_t length;
846 
847     while (mRxFrameBuffer.GetNextSavedFrame(frame, length) == OT_ERROR_NONE)
848     {
849         HandleNotification(frame, length);
850     }
851 
852     mRxFrameBuffer.ClearSavedFrames();
853 }
854 
RadioReceive(void)855 void RadioSpinel::RadioReceive(void)
856 {
857     if (!mIsPromiscuous)
858     {
859         switch (mState)
860         {
861         case kStateDisabled:
862         case kStateSleep:
863             ExitNow();
864 
865         case kStateReceive:
866         case kStateTransmitting:
867         case kStateTransmitDone:
868             break;
869         }
870     }
871 
872 #if OPENTHREAD_CONFIG_DIAG_ENABLE
873     if (otPlatDiagModeGet())
874     {
875         mCallbacks.mDiagReceiveDone(mInstance, &mRxRadioFrame, OT_ERROR_NONE);
876     }
877     else
878 #endif
879     {
880         mCallbacks.mReceiveDone(mInstance, &mRxRadioFrame, OT_ERROR_NONE);
881     }
882 exit:
883     return;
884 }
885 
TransmitDone(otRadioFrame * aFrame,otRadioFrame * aAckFrame,otError aError)886 void RadioSpinel::TransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError)
887 {
888 #if OPENTHREAD_CONFIG_DIAG_ENABLE
889     if (otPlatDiagModeGet())
890     {
891         mCallbacks.mDiagTransmitDone(mInstance, aFrame, aError);
892     }
893     else
894 #endif
895     {
896         mCallbacks.mTransmitDone(mInstance, aFrame, aAckFrame, aError);
897     }
898 }
899 
ProcessRadioStateMachine(void)900 void RadioSpinel::ProcessRadioStateMachine(void)
901 {
902     if (mState == kStateTransmitDone)
903     {
904         mState        = kStateReceive;
905         mTxRadioEndUs = UINT64_MAX;
906 
907         TransmitDone(mTransmitFrame, (mAckRadioFrame.mLength != 0) ? &mAckRadioFrame : nullptr, mTxError);
908     }
909     else if (mState == kStateTransmitting && otPlatTimeGet() >= mTxRadioEndUs)
910     {
911         // Frame has been successfully passed to radio, but no `TransmitDone` event received within kTxWaitUs.
912         LogWarn("radio tx timeout");
913         HandleRcpTimeout();
914     }
915 }
916 
Process(const void * aContext)917 void RadioSpinel::Process(const void *aContext)
918 {
919     if (mRxFrameBuffer.HasSavedFrame())
920     {
921         ProcessFrameQueue();
922         RecoverFromRcpFailure();
923     }
924 
925     mSpinelInterface->Process(aContext);
926     RecoverFromRcpFailure();
927 
928     if (mRxFrameBuffer.HasSavedFrame())
929     {
930         ProcessFrameQueue();
931         RecoverFromRcpFailure();
932     }
933 
934     ProcessRadioStateMachine();
935     RecoverFromRcpFailure();
936     CalcRcpTimeOffset();
937 }
938 
SetPromiscuous(bool aEnable)939 otError RadioSpinel::SetPromiscuous(bool aEnable)
940 {
941     otError error;
942 
943     uint8_t mode = (aEnable ? SPINEL_MAC_PROMISCUOUS_MODE_NETWORK : SPINEL_MAC_PROMISCUOUS_MODE_OFF);
944     SuccessOrExit(error = Set(SPINEL_PROP_MAC_PROMISCUOUS_MODE, SPINEL_DATATYPE_UINT8_S, mode));
945     mIsPromiscuous = aEnable;
946 
947 exit:
948     return error;
949 }
950 
SetRxOnWhenIdle(bool aEnable)951 otError RadioSpinel::SetRxOnWhenIdle(bool aEnable)
952 {
953     otError error = OT_ERROR_NONE;
954 
955     VerifyOrExit(mRxOnWhenIdle != aEnable);
956     SuccessOrExit(error = Set(SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE, SPINEL_DATATYPE_BOOL_S, aEnable));
957     mRxOnWhenIdle = aEnable;
958 
959 exit:
960     return error;
961 }
962 
SetShortAddress(uint16_t aAddress)963 otError RadioSpinel::SetShortAddress(uint16_t aAddress)
964 {
965     otError error = OT_ERROR_NONE;
966 
967     VerifyOrExit(mShortAddress != aAddress);
968     SuccessOrExit(error = Set(SPINEL_PROP_MAC_15_4_SADDR, SPINEL_DATATYPE_UINT16_S, aAddress));
969     mShortAddress = aAddress;
970 
971 exit:
972     return error;
973 }
974 
975 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
976 
ReadMacKey(const otMacKeyMaterial & aKeyMaterial,otMacKey & aKey)977 otError RadioSpinel::ReadMacKey(const otMacKeyMaterial &aKeyMaterial, otMacKey &aKey)
978 {
979     size_t  keySize;
980     otError error = otPlatCryptoExportKey(aKeyMaterial.mKeyMaterial.mKeyRef, aKey.m8, sizeof(aKey), &keySize);
981 
982     SuccessOrExit(error);
983     VerifyOrExit(keySize == sizeof(otMacKey), error = OT_ERROR_FAILED);
984 
985 exit:
986     return error;
987 }
988 
SetMacKey(uint8_t aKeyIdMode,uint8_t aKeyId,const otMacKeyMaterial * aPrevKey,const otMacKeyMaterial * aCurrKey,const otMacKeyMaterial * aNextKey)989 otError RadioSpinel::SetMacKey(uint8_t                 aKeyIdMode,
990                                uint8_t                 aKeyId,
991                                const otMacKeyMaterial *aPrevKey,
992                                const otMacKeyMaterial *aCurrKey,
993                                const otMacKeyMaterial *aNextKey)
994 {
995     otError  error;
996     otMacKey prevKey;
997     otMacKey currKey;
998     otMacKey nextKey;
999 
1000     SuccessOrExit(error = ReadMacKey(*aPrevKey, prevKey));
1001     SuccessOrExit(error = ReadMacKey(*aCurrKey, currKey));
1002     SuccessOrExit(error = ReadMacKey(*aNextKey, nextKey));
1003     error = SetMacKey(aKeyIdMode, aKeyId, prevKey, currKey, nextKey);
1004 
1005 exit:
1006     return error;
1007 }
1008 
1009 #else
1010 
SetMacKey(uint8_t aKeyIdMode,uint8_t aKeyId,const otMacKeyMaterial * aPrevKey,const otMacKeyMaterial * aCurrKey,const otMacKeyMaterial * aNextKey)1011 otError RadioSpinel::SetMacKey(uint8_t                 aKeyIdMode,
1012                                uint8_t                 aKeyId,
1013                                const otMacKeyMaterial *aPrevKey,
1014                                const otMacKeyMaterial *aCurrKey,
1015                                const otMacKeyMaterial *aNextKey)
1016 {
1017     return SetMacKey(aKeyIdMode, aKeyId, aPrevKey->mKeyMaterial.mKey, aCurrKey->mKeyMaterial.mKey,
1018                      aNextKey->mKeyMaterial.mKey);
1019 }
1020 
1021 #endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
1022 
SetMacKey(uint8_t aKeyIdMode,uint8_t aKeyId,const otMacKey & aPrevKey,const otMacKey & aCurrKey,const otMacKey & aNextKey)1023 otError RadioSpinel::SetMacKey(uint8_t         aKeyIdMode,
1024                                uint8_t         aKeyId,
1025                                const otMacKey &aPrevKey,
1026                                const otMacKey &aCurrKey,
1027                                const otMacKey &aNextKey)
1028 {
1029     otError error;
1030 
1031     SuccessOrExit(error = Set(SPINEL_PROP_RCP_MAC_KEY,
1032                               SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S
1033                                   SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_WLEN_S,
1034                               aKeyIdMode, aKeyId, aPrevKey.m8, sizeof(aPrevKey), aCurrKey.m8, sizeof(aCurrKey),
1035                               aNextKey.m8, sizeof(aNextKey)));
1036 
1037 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1038     mKeyIdMode = aKeyIdMode;
1039     mKeyId     = aKeyId;
1040 
1041     mPrevKey = aPrevKey;
1042     mCurrKey = aCurrKey;
1043     mNextKey = aNextKey;
1044 
1045     mMacKeySet = true;
1046 #endif
1047 
1048 exit:
1049     return error;
1050 }
1051 
SetMacFrameCounter(uint32_t aMacFrameCounter,bool aSetIfLarger)1052 otError RadioSpinel::SetMacFrameCounter(uint32_t aMacFrameCounter, bool aSetIfLarger)
1053 {
1054     otError error;
1055 
1056     SuccessOrExit(error = Set(SPINEL_PROP_RCP_MAC_FRAME_COUNTER, SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_BOOL_S,
1057                               aMacFrameCounter, aSetIfLarger));
1058 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1059     mMacFrameCounterSet = true;
1060     mMacFrameCounter    = aMacFrameCounter;
1061 #endif
1062 
1063 exit:
1064     return error;
1065 }
1066 
GetIeeeEui64(uint8_t * aIeeeEui64)1067 otError RadioSpinel::GetIeeeEui64(uint8_t *aIeeeEui64)
1068 {
1069     memcpy(aIeeeEui64, sIeeeEui64.m8, sizeof(sIeeeEui64.m8));
1070 
1071     return OT_ERROR_NONE;
1072 }
1073 
SetExtendedAddress(const otExtAddress & aExtAddress)1074 otError RadioSpinel::SetExtendedAddress(const otExtAddress &aExtAddress)
1075 {
1076     otError error;
1077 
1078     SuccessOrExit(error = Set(SPINEL_PROP_MAC_15_4_LADDR, SPINEL_DATATYPE_EUI64_S, aExtAddress.m8));
1079     mExtendedAddress = aExtAddress;
1080 
1081 exit:
1082     return error;
1083 }
1084 
SetPanId(uint16_t aPanId)1085 otError RadioSpinel::SetPanId(uint16_t aPanId)
1086 {
1087     otError error = OT_ERROR_NONE;
1088 
1089     VerifyOrExit(mPanId != aPanId);
1090     SuccessOrExit(error = Set(SPINEL_PROP_MAC_15_4_PANID, SPINEL_DATATYPE_UINT16_S, aPanId));
1091     mPanId = aPanId;
1092 
1093 exit:
1094     return error;
1095 }
1096 
EnableSrcMatch(bool aEnable)1097 otError RadioSpinel::EnableSrcMatch(bool aEnable)
1098 {
1099     return Set(SPINEL_PROP_MAC_SRC_MATCH_ENABLED, SPINEL_DATATYPE_BOOL_S, aEnable);
1100 }
1101 
AddSrcMatchShortEntry(uint16_t aShortAddress)1102 otError RadioSpinel::AddSrcMatchShortEntry(uint16_t aShortAddress)
1103 {
1104     otError error;
1105 
1106     SuccessOrExit(error = Insert(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, aShortAddress));
1107 
1108 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1109     assert(mSrcMatchShortEntryCount < OPENTHREAD_CONFIG_MLE_MAX_CHILDREN);
1110 
1111     for (int i = 0; i < mSrcMatchShortEntryCount; ++i)
1112     {
1113         if (mSrcMatchShortEntries[i] == aShortAddress)
1114         {
1115             ExitNow();
1116         }
1117     }
1118     mSrcMatchShortEntries[mSrcMatchShortEntryCount] = aShortAddress;
1119     ++mSrcMatchShortEntryCount;
1120 #endif
1121 
1122 exit:
1123     return error;
1124 }
1125 
AddSrcMatchExtEntry(const otExtAddress & aExtAddress)1126 otError RadioSpinel::AddSrcMatchExtEntry(const otExtAddress &aExtAddress)
1127 {
1128     otError error;
1129 
1130     SuccessOrExit(error =
1131                       Insert(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, aExtAddress.m8));
1132 
1133 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1134     assert(mSrcMatchExtEntryCount < OPENTHREAD_CONFIG_MLE_MAX_CHILDREN);
1135 
1136     for (int i = 0; i < mSrcMatchExtEntryCount; ++i)
1137     {
1138         if (memcmp(aExtAddress.m8, mSrcMatchExtEntries[i].m8, OT_EXT_ADDRESS_SIZE) == 0)
1139         {
1140             ExitNow();
1141         }
1142     }
1143     mSrcMatchExtEntries[mSrcMatchExtEntryCount] = aExtAddress;
1144     ++mSrcMatchExtEntryCount;
1145 #endif
1146 
1147 exit:
1148     return error;
1149 }
1150 
ClearSrcMatchShortEntry(uint16_t aShortAddress)1151 otError RadioSpinel::ClearSrcMatchShortEntry(uint16_t aShortAddress)
1152 {
1153     otError error;
1154 
1155     SuccessOrExit(error = Remove(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, aShortAddress));
1156 
1157 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1158     for (int i = 0; i < mSrcMatchShortEntryCount; ++i)
1159     {
1160         if (mSrcMatchShortEntries[i] == aShortAddress)
1161         {
1162             mSrcMatchShortEntries[i] = mSrcMatchShortEntries[mSrcMatchShortEntryCount - 1];
1163             --mSrcMatchShortEntryCount;
1164             break;
1165         }
1166     }
1167 #endif
1168 
1169 exit:
1170     return error;
1171 }
1172 
ClearSrcMatchExtEntry(const otExtAddress & aExtAddress)1173 otError RadioSpinel::ClearSrcMatchExtEntry(const otExtAddress &aExtAddress)
1174 {
1175     otError error;
1176 
1177     SuccessOrExit(error =
1178                       Remove(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, aExtAddress.m8));
1179 
1180 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1181     for (int i = 0; i < mSrcMatchExtEntryCount; ++i)
1182     {
1183         if (memcmp(mSrcMatchExtEntries[i].m8, aExtAddress.m8, OT_EXT_ADDRESS_SIZE) == 0)
1184         {
1185             mSrcMatchExtEntries[i] = mSrcMatchExtEntries[mSrcMatchExtEntryCount - 1];
1186             --mSrcMatchExtEntryCount;
1187             break;
1188         }
1189     }
1190 #endif
1191 
1192 exit:
1193     return error;
1194 }
1195 
ClearSrcMatchShortEntries(void)1196 otError RadioSpinel::ClearSrcMatchShortEntries(void)
1197 {
1198     otError error;
1199 
1200     SuccessOrExit(error = Set(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, nullptr));
1201 
1202 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1203     mSrcMatchShortEntryCount = 0;
1204 #endif
1205 
1206 exit:
1207     return error;
1208 }
1209 
ClearSrcMatchExtEntries(void)1210 otError RadioSpinel::ClearSrcMatchExtEntries(void)
1211 {
1212     otError error;
1213 
1214     SuccessOrExit(error = Set(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, nullptr));
1215 
1216 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1217     mSrcMatchExtEntryCount = 0;
1218 #endif
1219 
1220 exit:
1221     return error;
1222 }
1223 
GetTransmitPower(int8_t & aPower)1224 otError RadioSpinel::GetTransmitPower(int8_t &aPower)
1225 {
1226     otError error = Get(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, &aPower);
1227 
1228     LogIfFail("Get transmit power failed", error);
1229     return error;
1230 }
1231 
GetCcaEnergyDetectThreshold(int8_t & aThreshold)1232 otError RadioSpinel::GetCcaEnergyDetectThreshold(int8_t &aThreshold)
1233 {
1234     otError error = Get(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S, &aThreshold);
1235 
1236     LogIfFail("Get CCA ED threshold failed", error);
1237     return error;
1238 }
1239 
GetFemLnaGain(int8_t & aGain)1240 otError RadioSpinel::GetFemLnaGain(int8_t &aGain)
1241 {
1242     otError error = Get(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, &aGain);
1243 
1244     LogIfFail("Get FEM LNA gain failed", error);
1245     return error;
1246 }
1247 
GetRssi(void)1248 int8_t RadioSpinel::GetRssi(void)
1249 {
1250     int8_t  rssi  = OT_RADIO_RSSI_INVALID;
1251     otError error = Get(SPINEL_PROP_PHY_RSSI, SPINEL_DATATYPE_INT8_S, &rssi);
1252 
1253     LogIfFail("Get RSSI failed", error);
1254     return rssi;
1255 }
1256 
1257 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
SetCoexEnabled(bool aEnabled)1258 otError RadioSpinel::SetCoexEnabled(bool aEnabled)
1259 {
1260     otError error;
1261 
1262     SuccessOrExit(error = Set(SPINEL_PROP_RADIO_COEX_ENABLE, SPINEL_DATATYPE_BOOL_S, aEnabled));
1263 
1264 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1265     mCoexEnabled    = aEnabled;
1266     mCoexEnabledSet = true;
1267 #endif
1268 
1269 exit:
1270     return error;
1271 }
1272 
IsCoexEnabled(void)1273 bool RadioSpinel::IsCoexEnabled(void)
1274 {
1275     bool    enabled;
1276     otError error = Get(SPINEL_PROP_RADIO_COEX_ENABLE, SPINEL_DATATYPE_BOOL_S, &enabled);
1277 
1278     LogIfFail("Get Coex State failed", error);
1279     return enabled;
1280 }
1281 
GetCoexMetrics(otRadioCoexMetrics & aCoexMetrics)1282 otError RadioSpinel::GetCoexMetrics(otRadioCoexMetrics &aCoexMetrics)
1283 {
1284     otError error;
1285 
1286     error = Get(SPINEL_PROP_RADIO_COEX_METRICS,
1287                 SPINEL_DATATYPE_STRUCT_S(                                    // Tx Coex Metrics Structure
1288                     SPINEL_DATATYPE_UINT32_S                                 // NumTxRequest
1289                         SPINEL_DATATYPE_UINT32_S                             // NumTxGrantImmediate
1290                             SPINEL_DATATYPE_UINT32_S                         // NumTxGrantWait
1291                                 SPINEL_DATATYPE_UINT32_S                     // NumTxGrantWaitActivated
1292                                     SPINEL_DATATYPE_UINT32_S                 // NumTxGrantWaitTimeout
1293                                         SPINEL_DATATYPE_UINT32_S             // NumTxGrantDeactivatedDuringRequest
1294                                             SPINEL_DATATYPE_UINT32_S         // NumTxDelayedGrant
1295                                                 SPINEL_DATATYPE_UINT32_S     // AvgTxRequestToGrantTime
1296                     ) SPINEL_DATATYPE_STRUCT_S(                              // Rx Coex Metrics Structure
1297                     SPINEL_DATATYPE_UINT32_S                                 // NumRxRequest
1298                         SPINEL_DATATYPE_UINT32_S                             // NumRxGrantImmediate
1299                             SPINEL_DATATYPE_UINT32_S                         // NumRxGrantWait
1300                                 SPINEL_DATATYPE_UINT32_S                     // NumRxGrantWaitActivated
1301                                     SPINEL_DATATYPE_UINT32_S                 // NumRxGrantWaitTimeout
1302                                         SPINEL_DATATYPE_UINT32_S             // NumRxGrantDeactivatedDuringRequest
1303                                             SPINEL_DATATYPE_UINT32_S         // NumRxDelayedGrant
1304                                                 SPINEL_DATATYPE_UINT32_S     // AvgRxRequestToGrantTime
1305                                                     SPINEL_DATATYPE_UINT32_S // NumRxGrantNone
1306                     ) SPINEL_DATATYPE_BOOL_S                                 // Stopped
1307                     SPINEL_DATATYPE_UINT32_S,                                // NumGrantGlitch
1308                 &aCoexMetrics.mNumTxRequest, &aCoexMetrics.mNumTxGrantImmediate, &aCoexMetrics.mNumTxGrantWait,
1309                 &aCoexMetrics.mNumTxGrantWaitActivated, &aCoexMetrics.mNumTxGrantWaitTimeout,
1310                 &aCoexMetrics.mNumTxGrantDeactivatedDuringRequest, &aCoexMetrics.mNumTxDelayedGrant,
1311                 &aCoexMetrics.mAvgTxRequestToGrantTime, &aCoexMetrics.mNumRxRequest, &aCoexMetrics.mNumRxGrantImmediate,
1312                 &aCoexMetrics.mNumRxGrantWait, &aCoexMetrics.mNumRxGrantWaitActivated,
1313                 &aCoexMetrics.mNumRxGrantWaitTimeout, &aCoexMetrics.mNumRxGrantDeactivatedDuringRequest,
1314                 &aCoexMetrics.mNumRxDelayedGrant, &aCoexMetrics.mAvgRxRequestToGrantTime, &aCoexMetrics.mNumRxGrantNone,
1315                 &aCoexMetrics.mStopped, &aCoexMetrics.mNumGrantGlitch);
1316 
1317     LogIfFail("Get Coex Metrics failed", error);
1318     return error;
1319 }
1320 #endif
1321 
SetTransmitPower(int8_t aPower)1322 otError RadioSpinel::SetTransmitPower(int8_t aPower)
1323 {
1324     otError error;
1325 
1326     SuccessOrExit(error = Set(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, aPower));
1327 
1328 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1329     mTransmitPower    = aPower;
1330     mTransmitPowerSet = true;
1331 #endif
1332 
1333 exit:
1334     LogIfFail("Set transmit power failed", error);
1335     return error;
1336 }
1337 
SetCcaEnergyDetectThreshold(int8_t aThreshold)1338 otError RadioSpinel::SetCcaEnergyDetectThreshold(int8_t aThreshold)
1339 {
1340     otError error;
1341 
1342     SuccessOrExit(error = Set(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S, aThreshold));
1343 
1344 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1345     mCcaEnergyDetectThreshold    = aThreshold;
1346     mCcaEnergyDetectThresholdSet = true;
1347 #endif
1348 
1349 exit:
1350     LogIfFail("Set CCA ED threshold failed", error);
1351     return error;
1352 }
1353 
SetFemLnaGain(int8_t aGain)1354 otError RadioSpinel::SetFemLnaGain(int8_t aGain)
1355 {
1356     otError error;
1357 
1358     SuccessOrExit(error = Set(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, aGain));
1359 
1360 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1361     mFemLnaGain    = aGain;
1362     mFemLnaGainSet = true;
1363 #endif
1364 
1365 exit:
1366     LogIfFail("Set FEM LNA gain failed", error);
1367     return error;
1368 }
1369 
EnergyScan(uint8_t aScanChannel,uint16_t aScanDuration)1370 otError RadioSpinel::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration)
1371 {
1372     otError error;
1373 
1374     VerifyOrExit(sRadioCaps & OT_RADIO_CAPS_ENERGY_SCAN, error = OT_ERROR_NOT_CAPABLE);
1375 
1376 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1377     mScanChannel    = aScanChannel;
1378     mScanDuration   = aScanDuration;
1379     mEnergyScanning = true;
1380 #endif
1381 
1382     SuccessOrExit(error = Set(SPINEL_PROP_MAC_SCAN_MASK, SPINEL_DATATYPE_DATA_S, &aScanChannel, sizeof(uint8_t)));
1383     SuccessOrExit(error = Set(SPINEL_PROP_MAC_SCAN_PERIOD, SPINEL_DATATYPE_UINT16_S, aScanDuration));
1384     SuccessOrExit(error = Set(SPINEL_PROP_MAC_SCAN_STATE, SPINEL_DATATYPE_UINT8_S, SPINEL_SCAN_STATE_ENERGY));
1385 
1386     mChannel = aScanChannel;
1387 
1388 exit:
1389     return error;
1390 }
1391 
Get(spinel_prop_key_t aKey,const char * aFormat,...)1392 otError RadioSpinel::Get(spinel_prop_key_t aKey, const char *aFormat, ...)
1393 {
1394     otError error;
1395 
1396     assert(mWaitingTid == 0);
1397 
1398 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1399     do
1400     {
1401         RecoverFromRcpFailure();
1402 #endif
1403         va_start(mPropertyArgs, aFormat);
1404         error = RequestWithPropertyFormatV(aFormat, SPINEL_CMD_PROP_VALUE_GET, aKey, nullptr, mPropertyArgs);
1405         va_end(mPropertyArgs);
1406 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1407     } while (mRcpFailure != kRcpFailureNone);
1408 #endif
1409 
1410     return error;
1411 }
1412 
1413 // This is not a normal use case for VALUE_GET command and should be only used to get RCP timestamp with dummy payload
GetWithParam(spinel_prop_key_t aKey,const uint8_t * aParam,spinel_size_t aParamSize,const char * aFormat,...)1414 otError RadioSpinel::GetWithParam(spinel_prop_key_t aKey,
1415                                   const uint8_t    *aParam,
1416                                   spinel_size_t     aParamSize,
1417                                   const char       *aFormat,
1418                                   ...)
1419 {
1420     otError error;
1421 
1422     assert(mWaitingTid == 0);
1423 
1424 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1425     do
1426     {
1427         RecoverFromRcpFailure();
1428 #endif
1429         va_start(mPropertyArgs, aFormat);
1430         error = RequestWithPropertyFormat(aFormat, SPINEL_CMD_PROP_VALUE_GET, aKey, SPINEL_DATATYPE_DATA_S, aParam,
1431                                           aParamSize);
1432         va_end(mPropertyArgs);
1433 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1434     } while (mRcpFailure != kRcpFailureNone);
1435 #endif
1436 
1437     return error;
1438 }
1439 
Set(spinel_prop_key_t aKey,const char * aFormat,...)1440 otError RadioSpinel::Set(spinel_prop_key_t aKey, const char *aFormat, ...)
1441 {
1442     otError error;
1443 
1444     assert(mWaitingTid == 0);
1445 
1446 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1447     do
1448     {
1449         RecoverFromRcpFailure();
1450 #endif
1451         va_start(mPropertyArgs, aFormat);
1452         error = RequestWithExpectedCommandV(SPINEL_CMD_PROP_VALUE_IS, SPINEL_CMD_PROP_VALUE_SET, aKey, aFormat,
1453                                             mPropertyArgs);
1454         va_end(mPropertyArgs);
1455 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1456     } while (mRcpFailure != kRcpFailureNone);
1457 #endif
1458 
1459     return error;
1460 }
1461 
Insert(spinel_prop_key_t aKey,const char * aFormat,...)1462 otError RadioSpinel::Insert(spinel_prop_key_t aKey, const char *aFormat, ...)
1463 {
1464     otError error;
1465 
1466     assert(mWaitingTid == 0);
1467 
1468 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1469     do
1470     {
1471         RecoverFromRcpFailure();
1472 #endif
1473         va_start(mPropertyArgs, aFormat);
1474         error = RequestWithExpectedCommandV(SPINEL_CMD_PROP_VALUE_INSERTED, SPINEL_CMD_PROP_VALUE_INSERT, aKey, aFormat,
1475                                             mPropertyArgs);
1476         va_end(mPropertyArgs);
1477 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1478     } while (mRcpFailure != kRcpFailureNone);
1479 #endif
1480 
1481     return error;
1482 }
1483 
Remove(spinel_prop_key_t aKey,const char * aFormat,...)1484 otError RadioSpinel::Remove(spinel_prop_key_t aKey, const char *aFormat, ...)
1485 {
1486     otError error;
1487 
1488     assert(mWaitingTid == 0);
1489 
1490 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1491     do
1492     {
1493         RecoverFromRcpFailure();
1494 #endif
1495         va_start(mPropertyArgs, aFormat);
1496         error = RequestWithExpectedCommandV(SPINEL_CMD_PROP_VALUE_REMOVED, SPINEL_CMD_PROP_VALUE_REMOVE, aKey, aFormat,
1497                                             mPropertyArgs);
1498         va_end(mPropertyArgs);
1499 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1500     } while (mRcpFailure != kRcpFailureNone);
1501 #endif
1502 
1503     return error;
1504 }
1505 
WaitResponse(bool aHandleRcpTimeout)1506 otError RadioSpinel::WaitResponse(bool aHandleRcpTimeout)
1507 {
1508     uint64_t end = otPlatTimeGet() + kMaxWaitTime * kUsPerMs;
1509 
1510     LogDebg("Wait response: tid=%u key=%lu", mWaitingTid, ToUlong(mWaitingKey));
1511 
1512     do
1513     {
1514         uint64_t now;
1515 
1516         now = otPlatTimeGet();
1517         if ((end <= now) || (mSpinelInterface->WaitForFrame(end - now) != OT_ERROR_NONE))
1518         {
1519             LogWarn("Wait for response timeout");
1520             if (aHandleRcpTimeout)
1521             {
1522                 HandleRcpTimeout();
1523             }
1524             ExitNow(mError = OT_ERROR_RESPONSE_TIMEOUT);
1525         }
1526     } while (mWaitingTid || !sIsReady);
1527 
1528     LogIfFail("Error waiting response", mError);
1529     // This indicates end of waiting response.
1530     mWaitingKey = SPINEL_PROP_LAST_STATUS;
1531 
1532 exit:
1533     return mError;
1534 }
1535 
GetNextTid(void)1536 spinel_tid_t RadioSpinel::GetNextTid(void)
1537 {
1538     spinel_tid_t tid = mCmdNextTid;
1539 
1540     while (((1 << tid) & mCmdTidsInUse) != 0)
1541     {
1542         tid = SPINEL_GET_NEXT_TID(tid);
1543 
1544         if (tid == mCmdNextTid)
1545         {
1546             // We looped back to `mCmdNextTid` indicating that all
1547             // TIDs are in-use.
1548 
1549             ExitNow(tid = 0);
1550         }
1551     }
1552 
1553     mCmdTidsInUse |= (1 << tid);
1554     mCmdNextTid = SPINEL_GET_NEXT_TID(tid);
1555 
1556 exit:
1557     return tid;
1558 }
1559 
SendReset(uint8_t aResetType)1560 otError RadioSpinel::SendReset(uint8_t aResetType)
1561 {
1562     otError        error = OT_ERROR_NONE;
1563     uint8_t        buffer[kMaxSpinelFrame];
1564     spinel_ssize_t packed;
1565 
1566     if ((aResetType == SPINEL_RESET_BOOTLOADER) && !sSupportsResetToBootloader)
1567     {
1568         ExitNow(error = OT_ERROR_NOT_CAPABLE);
1569     }
1570 
1571     // Pack the header, command and key
1572     packed = spinel_datatype_pack(buffer, sizeof(buffer), SPINEL_DATATYPE_COMMAND_S SPINEL_DATATYPE_UINT8_S,
1573                                   SPINEL_HEADER_FLAG | SPINEL_HEADER_IID(mIid), SPINEL_CMD_RESET, aResetType);
1574 
1575     VerifyOrExit(packed > 0 && static_cast<size_t>(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
1576 
1577     SuccessOrExit(error = mSpinelInterface->SendFrame(buffer, static_cast<uint16_t>(packed)));
1578     LogSpinelFrame(buffer, static_cast<uint16_t>(packed), true);
1579 
1580 exit:
1581     return error;
1582 }
1583 
SendCommand(uint32_t aCommand,spinel_prop_key_t aKey,spinel_tid_t tid,const char * aFormat,va_list args)1584 otError RadioSpinel::SendCommand(uint32_t          aCommand,
1585                                  spinel_prop_key_t aKey,
1586                                  spinel_tid_t      tid,
1587                                  const char       *aFormat,
1588                                  va_list           args)
1589 {
1590     otError        error = OT_ERROR_NONE;
1591     uint8_t        buffer[kMaxSpinelFrame];
1592     spinel_ssize_t packed;
1593     uint16_t       offset;
1594 
1595     // Pack the header, command and key
1596     packed = spinel_datatype_pack(buffer, sizeof(buffer), "Cii", SPINEL_HEADER_FLAG | SPINEL_HEADER_IID(mIid) | tid,
1597                                   aCommand, aKey);
1598 
1599     VerifyOrExit(packed > 0 && static_cast<size_t>(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
1600 
1601     offset = static_cast<uint16_t>(packed);
1602 
1603     // Pack the data (if any)
1604     if (aFormat)
1605     {
1606         packed = spinel_datatype_vpack(buffer + offset, sizeof(buffer) - offset, aFormat, args);
1607         VerifyOrExit(packed > 0 && static_cast<size_t>(packed + offset) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
1608 
1609         offset += static_cast<uint16_t>(packed);
1610     }
1611 
1612     SuccessOrExit(error = mSpinelInterface->SendFrame(buffer, offset));
1613     LogSpinelFrame(buffer, offset, true);
1614 
1615 exit:
1616     return error;
1617 }
1618 
RequestV(uint32_t command,spinel_prop_key_t aKey,const char * aFormat,va_list aArgs)1619 otError RadioSpinel::RequestV(uint32_t command, spinel_prop_key_t aKey, const char *aFormat, va_list aArgs)
1620 {
1621     otError      error = OT_ERROR_NONE;
1622     spinel_tid_t tid   = GetNextTid();
1623 
1624     VerifyOrExit(tid > 0, error = OT_ERROR_BUSY);
1625 
1626     error = SendCommand(command, aKey, tid, aFormat, aArgs);
1627     SuccessOrExit(error);
1628 
1629     if (aKey == SPINEL_PROP_STREAM_RAW)
1630     {
1631         // not allowed to send another frame before the last frame is done.
1632         assert(mTxRadioTid == 0);
1633         VerifyOrExit(mTxRadioTid == 0, error = OT_ERROR_BUSY);
1634         mTxRadioTid = tid;
1635     }
1636     else
1637     {
1638         mWaitingKey = aKey;
1639         mWaitingTid = tid;
1640         error       = WaitResponse();
1641     }
1642 
1643 exit:
1644     return error;
1645 }
1646 
Request(uint32_t aCommand,spinel_prop_key_t aKey,const char * aFormat,...)1647 otError RadioSpinel::Request(uint32_t aCommand, spinel_prop_key_t aKey, const char *aFormat, ...)
1648 {
1649     va_list args;
1650     va_start(args, aFormat);
1651     otError status = RequestV(aCommand, aKey, aFormat, args);
1652     va_end(args);
1653     return status;
1654 }
1655 
RequestWithPropertyFormat(const char * aPropertyFormat,uint32_t aCommand,spinel_prop_key_t aKey,const char * aFormat,...)1656 otError RadioSpinel::RequestWithPropertyFormat(const char       *aPropertyFormat,
1657                                                uint32_t          aCommand,
1658                                                spinel_prop_key_t aKey,
1659                                                const char       *aFormat,
1660                                                ...)
1661 {
1662     otError error;
1663     va_list args;
1664 
1665     va_start(args, aFormat);
1666     error = RequestWithPropertyFormatV(aPropertyFormat, aCommand, aKey, aFormat, args);
1667     va_end(args);
1668 
1669     return error;
1670 }
1671 
RequestWithPropertyFormatV(const char * aPropertyFormat,uint32_t aCommand,spinel_prop_key_t aKey,const char * aFormat,va_list aArgs)1672 otError RadioSpinel::RequestWithPropertyFormatV(const char       *aPropertyFormat,
1673                                                 uint32_t          aCommand,
1674                                                 spinel_prop_key_t aKey,
1675                                                 const char       *aFormat,
1676                                                 va_list           aArgs)
1677 {
1678     otError error;
1679 
1680     mPropertyFormat = aPropertyFormat;
1681     error           = RequestV(aCommand, aKey, aFormat, aArgs);
1682     mPropertyFormat = nullptr;
1683 
1684     return error;
1685 }
1686 
RequestWithExpectedCommandV(uint32_t aExpectedCommand,uint32_t aCommand,spinel_prop_key_t aKey,const char * aFormat,va_list aArgs)1687 otError RadioSpinel::RequestWithExpectedCommandV(uint32_t          aExpectedCommand,
1688                                                  uint32_t          aCommand,
1689                                                  spinel_prop_key_t aKey,
1690                                                  const char       *aFormat,
1691                                                  va_list           aArgs)
1692 {
1693     otError error;
1694 
1695     mExpectedCommand = aExpectedCommand;
1696     error            = RequestV(aCommand, aKey, aFormat, aArgs);
1697     mExpectedCommand = SPINEL_CMD_NOOP;
1698 
1699     return error;
1700 }
1701 
HandleTransmitDone(uint32_t aCommand,spinel_prop_key_t aKey,const uint8_t * aBuffer,uint16_t aLength)1702 void RadioSpinel::HandleTransmitDone(uint32_t          aCommand,
1703                                      spinel_prop_key_t aKey,
1704                                      const uint8_t    *aBuffer,
1705                                      uint16_t          aLength)
1706 {
1707     otError         error         = OT_ERROR_NONE;
1708     spinel_status_t status        = SPINEL_STATUS_OK;
1709     bool            framePending  = false;
1710     bool            headerUpdated = false;
1711     spinel_ssize_t  unpacked;
1712 
1713     VerifyOrExit(aCommand == SPINEL_CMD_PROP_VALUE_IS && aKey == SPINEL_PROP_LAST_STATUS, error = OT_ERROR_FAILED);
1714 
1715     unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status);
1716     VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
1717 
1718     aBuffer += unpacked;
1719     aLength -= static_cast<uint16_t>(unpacked);
1720 
1721     unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_BOOL_S, &framePending);
1722     VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
1723 
1724     aBuffer += unpacked;
1725     aLength -= static_cast<uint16_t>(unpacked);
1726 
1727     unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_BOOL_S, &headerUpdated);
1728     VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
1729 
1730     aBuffer += unpacked;
1731     aLength -= static_cast<uint16_t>(unpacked);
1732 
1733     if (status == SPINEL_STATUS_OK)
1734     {
1735         SuccessOrExit(error = ParseRadioFrame(mAckRadioFrame, aBuffer, aLength, unpacked));
1736         aBuffer += unpacked;
1737         aLength -= static_cast<uint16_t>(unpacked);
1738     }
1739     else
1740     {
1741         error = SpinelStatusToOtError(status);
1742     }
1743 
1744     static_cast<Mac::TxFrame *>(mTransmitFrame)->SetIsHeaderUpdated(headerUpdated);
1745 
1746     if ((sRadioCaps & OT_RADIO_CAPS_TRANSMIT_SEC) && headerUpdated &&
1747         static_cast<Mac::TxFrame *>(mTransmitFrame)->GetSecurityEnabled())
1748     {
1749         uint8_t  keyId;
1750         uint32_t frameCounter;
1751 
1752         // Replace transmit frame security key index and frame counter with the one filled by RCP
1753         unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT32_S, &keyId,
1754                                           &frameCounter);
1755         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
1756         static_cast<Mac::TxFrame *>(mTransmitFrame)->SetKeyId(keyId);
1757         static_cast<Mac::TxFrame *>(mTransmitFrame)->SetFrameCounter(frameCounter);
1758 
1759 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1760         mMacFrameCounterSet = true;
1761         mMacFrameCounter    = frameCounter;
1762 #endif
1763     }
1764 
1765 exit:
1766     mState   = kStateTransmitDone;
1767     mTxError = error;
1768     UpdateParseErrorCount(error);
1769     LogIfFail("Handle transmit done failed", error);
1770 }
1771 
Transmit(otRadioFrame & aFrame)1772 otError RadioSpinel::Transmit(otRadioFrame &aFrame)
1773 {
1774     otError error = OT_ERROR_INVALID_STATE;
1775 
1776     VerifyOrExit(mState == kStateReceive || (mState == kStateSleep && (sRadioCaps & OT_RADIO_CAPS_SLEEP_TO_TX)));
1777 
1778     mTransmitFrame = &aFrame;
1779 
1780     // `otPlatRadioTxStarted()` is triggered immediately for now, which may be earlier than real started time.
1781     mCallbacks.mTxStarted(mInstance, mTransmitFrame);
1782 
1783     error = Request(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_STREAM_RAW,
1784                     SPINEL_DATATYPE_DATA_WLEN_S                                      // Frame data
1785                         SPINEL_DATATYPE_UINT8_S                                      // Channel
1786                             SPINEL_DATATYPE_UINT8_S                                  // MaxCsmaBackoffs
1787                                 SPINEL_DATATYPE_UINT8_S                              // MaxFrameRetries
1788                                     SPINEL_DATATYPE_BOOL_S                           // CsmaCaEnabled
1789                                         SPINEL_DATATYPE_BOOL_S                       // IsHeaderUpdated
1790                                             SPINEL_DATATYPE_BOOL_S                   // IsARetx
1791                                                 SPINEL_DATATYPE_BOOL_S               // IsSecurityProcessed
1792                                                     SPINEL_DATATYPE_UINT32_S         // TxDelay
1793                                                         SPINEL_DATATYPE_UINT32_S     // TxDelayBaseTime
1794                                                             SPINEL_DATATYPE_UINT8_S, // RxChannelAfterTxDone
1795                     mTransmitFrame->mPsdu, mTransmitFrame->mLength, mTransmitFrame->mChannel,
1796                     mTransmitFrame->mInfo.mTxInfo.mMaxCsmaBackoffs, mTransmitFrame->mInfo.mTxInfo.mMaxFrameRetries,
1797                     mTransmitFrame->mInfo.mTxInfo.mCsmaCaEnabled, mTransmitFrame->mInfo.mTxInfo.mIsHeaderUpdated,
1798                     mTransmitFrame->mInfo.mTxInfo.mIsARetx, mTransmitFrame->mInfo.mTxInfo.mIsSecurityProcessed,
1799                     mTransmitFrame->mInfo.mTxInfo.mTxDelay, mTransmitFrame->mInfo.mTxInfo.mTxDelayBaseTime,
1800                     mTransmitFrame->mInfo.mTxInfo.mRxChannelAfterTxDone);
1801 
1802     if (error == OT_ERROR_NONE)
1803     {
1804         // Waiting for `TransmitDone` event.
1805         mState        = kStateTransmitting;
1806         mTxRadioEndUs = otPlatTimeGet() + kTxWaitUs;
1807         mChannel      = mTransmitFrame->mChannel;
1808     }
1809 
1810 exit:
1811     return error;
1812 }
1813 
Receive(uint8_t aChannel)1814 otError RadioSpinel::Receive(uint8_t aChannel)
1815 {
1816     otError error = OT_ERROR_NONE;
1817 
1818     VerifyOrExit(mState != kStateDisabled, error = OT_ERROR_INVALID_STATE);
1819 
1820     if (mChannel != aChannel)
1821     {
1822         error = Set(SPINEL_PROP_PHY_CHAN, SPINEL_DATATYPE_UINT8_S, aChannel);
1823         SuccessOrExit(error);
1824         mChannel = aChannel;
1825     }
1826 
1827     if (mState == kStateSleep)
1828     {
1829         error = Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true);
1830         SuccessOrExit(error);
1831     }
1832 
1833     if (mTxRadioTid != 0)
1834     {
1835         FreeTid(mTxRadioTid);
1836         mTxRadioTid = 0;
1837     }
1838 
1839     mState = kStateReceive;
1840 
1841 exit:
1842     return error;
1843 }
1844 
Sleep(void)1845 otError RadioSpinel::Sleep(void)
1846 {
1847     otError error = OT_ERROR_NONE;
1848 
1849     switch (mState)
1850     {
1851     case kStateReceive:
1852         error = Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, false);
1853         SuccessOrExit(error);
1854 
1855         mState = kStateSleep;
1856         break;
1857 
1858     case kStateSleep:
1859         break;
1860 
1861     default:
1862         error = OT_ERROR_INVALID_STATE;
1863         break;
1864     }
1865 
1866 exit:
1867     return error;
1868 }
1869 
Enable(otInstance * aInstance)1870 otError RadioSpinel::Enable(otInstance *aInstance)
1871 {
1872     otError error = OT_ERROR_NONE;
1873 
1874     VerifyOrExit(!IsEnabled());
1875 
1876     mInstance = aInstance;
1877 
1878     SuccessOrExit(error = Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
1879     SuccessOrExit(error = Set(SPINEL_PROP_MAC_15_4_PANID, SPINEL_DATATYPE_UINT16_S, mPanId));
1880     SuccessOrExit(error = Set(SPINEL_PROP_MAC_15_4_SADDR, SPINEL_DATATYPE_UINT16_S, mShortAddress));
1881     SuccessOrExit(error = Get(SPINEL_PROP_PHY_RX_SENSITIVITY, SPINEL_DATATYPE_INT8_S, &mRxSensitivity));
1882 
1883     mState = kStateSleep;
1884 
1885 exit:
1886     if (error != OT_ERROR_NONE)
1887     {
1888         LogWarn("RadioSpinel enable: %s", otThreadErrorToString(error));
1889         error = OT_ERROR_FAILED;
1890     }
1891 
1892     return error;
1893 }
1894 
Disable(void)1895 otError RadioSpinel::Disable(void)
1896 {
1897     otError error = OT_ERROR_NONE;
1898 
1899     VerifyOrExit(IsEnabled());
1900     VerifyOrExit(mState == kStateSleep, error = OT_ERROR_INVALID_STATE);
1901 
1902     SuccessOrDie(Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, false));
1903     mState    = kStateDisabled;
1904     mInstance = nullptr;
1905 
1906 exit:
1907     return error;
1908 }
1909 
1910 #if OPENTHREAD_CONFIG_DIAG_ENABLE
PlatDiagProcess(const char * aString,char * aOutput,size_t aOutputMaxLen)1911 otError RadioSpinel::PlatDiagProcess(const char *aString, char *aOutput, size_t aOutputMaxLen)
1912 {
1913     otError error;
1914 
1915     mDiagOutput       = aOutput;
1916     mDiagOutputMaxLen = aOutputMaxLen;
1917 
1918     error = Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString);
1919 
1920     mDiagOutput       = nullptr;
1921     mDiagOutputMaxLen = 0;
1922 
1923     return error;
1924 }
1925 #endif
1926 
GetRadioChannelMask(bool aPreferred)1927 uint32_t RadioSpinel::GetRadioChannelMask(bool aPreferred)
1928 {
1929     uint8_t        maskBuffer[kChannelMaskBufferSize];
1930     otError        error       = OT_ERROR_NONE;
1931     uint32_t       channelMask = 0;
1932     const uint8_t *maskData    = maskBuffer;
1933     spinel_size_t  maskLength  = sizeof(maskBuffer);
1934 
1935     SuccessOrDie(Get(aPreferred ? SPINEL_PROP_PHY_CHAN_PREFERRED : SPINEL_PROP_PHY_CHAN_SUPPORTED,
1936                      SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength));
1937 
1938     while (maskLength > 0)
1939     {
1940         uint8_t        channel;
1941         spinel_ssize_t unpacked;
1942 
1943         unpacked = spinel_datatype_unpack(maskData, maskLength, SPINEL_DATATYPE_UINT8_S, &channel);
1944         VerifyOrExit(unpacked > 0, error = OT_ERROR_FAILED);
1945         VerifyOrExit(channel < kChannelMaskBufferSize, error = OT_ERROR_PARSE);
1946         channelMask |= (1UL << channel);
1947 
1948         maskData += unpacked;
1949         maskLength -= static_cast<spinel_size_t>(unpacked);
1950     }
1951 
1952     channelMask &= mMaxPowerTable.GetSupportedChannelMask();
1953 
1954 exit:
1955     UpdateParseErrorCount(error);
1956     LogIfFail("Get radio channel mask failed", error);
1957     return channelMask;
1958 }
1959 
GetState(void) const1960 otRadioState RadioSpinel::GetState(void) const
1961 {
1962     static const otRadioState sOtRadioStateMap[] = {
1963         OT_RADIO_STATE_DISABLED, OT_RADIO_STATE_SLEEP,    OT_RADIO_STATE_RECEIVE,
1964         OT_RADIO_STATE_TRANSMIT, OT_RADIO_STATE_TRANSMIT,
1965     };
1966 
1967     return sOtRadioStateMap[mState];
1968 }
1969 
CalcRcpTimeOffset(void)1970 void RadioSpinel::CalcRcpTimeOffset(void)
1971 {
1972 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
1973     otError        error = OT_ERROR_NONE;
1974     uint64_t       localTxTimestamp;
1975     uint64_t       localRxTimestamp;
1976     uint64_t       remoteTimestamp = 0;
1977     uint8_t        buffer[sizeof(remoteTimestamp)];
1978     spinel_ssize_t packed;
1979 
1980     /*
1981      * Use a modified Network Time Protocol(NTP) to calculate the time offset
1982      * Assume the time offset is D so that local can calculate remote time with,
1983      *         T' = T + D
1984      * Where T is the local time and T' is the remote time.
1985      * The time offset is calculated using timestamp measured at local and remote.
1986      *
1987      *              T0  P    P T2
1988      *  local time --+----+----+--->
1989      *                \   |   ^
1990      *              get\  |  /is
1991      *                  v | /
1992      * remote time -------+--------->
1993      *                    T1'
1994      *
1995      * Based on the assumptions,
1996      * 1. If the propagation time(P) from local to remote and from remote to local are same.
1997      * 2. Both the host and RCP can accurately measure the time they send or receive a message.
1998      * The degree to which these assumptions hold true determines the accuracy of the offset.
1999      * Then,
2000      *         T1' = T0 + P + D and T1' = T2 - P + D
2001      * Time offset can be calculated with,
2002      *         D = T1' - ((T0 + T2)/ 2)
2003      */
2004 
2005     VerifyOrExit(!mIsTimeSynced || (otPlatTimeGet() >= GetNextRadioTimeRecalcStart()));
2006 
2007     LogDebg("Trying to get RCP time offset");
2008 
2009     packed = spinel_datatype_pack(buffer, sizeof(buffer), SPINEL_DATATYPE_UINT64_S, remoteTimestamp);
2010     VerifyOrExit(packed > 0 && static_cast<size_t>(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
2011 
2012     localTxTimestamp = otPlatTimeGet();
2013 
2014     // Dummy timestamp payload to make request length same as response
2015     error = GetWithParam(SPINEL_PROP_RCP_TIMESTAMP, buffer, static_cast<spinel_size_t>(packed),
2016                          SPINEL_DATATYPE_UINT64_S, &remoteTimestamp);
2017 
2018     localRxTimestamp = otPlatTimeGet();
2019 
2020     VerifyOrExit(error == OT_ERROR_NONE, mRadioTimeRecalcStart = localRxTimestamp);
2021 
2022     mRadioTimeOffset      = (remoteTimestamp - ((localRxTimestamp / 2) + (localTxTimestamp / 2)));
2023     mIsTimeSynced         = true;
2024     mRadioTimeRecalcStart = localRxTimestamp + OPENTHREAD_SPINEL_CONFIG_RCP_TIME_SYNC_INTERVAL;
2025 
2026 exit:
2027     LogIfFail("Error calculating RCP time offset: %s", error);
2028 #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
2029 }
2030 
GetNow(void)2031 uint64_t RadioSpinel::GetNow(void) { return (mIsTimeSynced) ? (otPlatTimeGet() + mRadioTimeOffset) : UINT64_MAX; }
2032 
GetBusSpeed(void) const2033 uint32_t RadioSpinel::GetBusSpeed(void) const { return mSpinelInterface->GetBusSpeed(); }
2034 
HandleRcpUnexpectedReset(spinel_status_t aStatus)2035 void RadioSpinel::HandleRcpUnexpectedReset(spinel_status_t aStatus)
2036 {
2037     OT_UNUSED_VARIABLE(aStatus);
2038 
2039     mRadioSpinelMetrics.mRcpUnexpectedResetCount++;
2040     LogCrit("Unexpected RCP reset: %s", spinel_status_to_cstr(aStatus));
2041 
2042 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
2043     mRcpFailure = kRcpFailureUnexpectedReset;
2044 #elif OPENTHREAD_SPINEL_CONFIG_ABORT_ON_UNEXPECTED_RCP_RESET_ENABLE
2045     abort();
2046 #else
2047     DieNow(OT_EXIT_RADIO_SPINEL_RESET);
2048 #endif
2049 }
2050 
HandleRcpTimeout(void)2051 void RadioSpinel::HandleRcpTimeout(void)
2052 {
2053     mRadioSpinelMetrics.mRcpTimeoutCount++;
2054 
2055 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
2056     mRcpFailure = kRcpFailureTimeout;
2057 #else
2058     if (!sIsReady)
2059     {
2060         LogCrit("Failed to communicate with RCP - no response from RCP during initialization");
2061         LogCrit("This is not a bug and typically due a config error (wrong URL parameters) or bad RCP image:");
2062         LogCrit("- Make sure RCP is running the correct firmware");
2063         LogCrit("- Double check the config parameters passed as `RadioURL` input");
2064     }
2065 
2066     DieNow(OT_EXIT_RADIO_SPINEL_NO_RESPONSE);
2067 #endif
2068 }
2069 
RecoverFromRcpFailure(void)2070 void RadioSpinel::RecoverFromRcpFailure(void)
2071 {
2072 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
2073     constexpr int16_t kMaxFailureCount = OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT;
2074     State             recoveringState  = mState;
2075     bool              skipReset        = false;
2076 
2077     if (mRcpFailure == kRcpFailureNone)
2078     {
2079         ExitNow();
2080     }
2081 
2082 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
2083     skipReset = (mRcpFailure == kRcpFailureUnexpectedReset);
2084 #endif
2085 
2086     mRcpFailure = kRcpFailureNone;
2087 
2088     LogWarn("RCP failure detected");
2089 
2090     ++mRadioSpinelMetrics.mRcpRestorationCount;
2091     ++mRcpFailureCount;
2092     if (mRcpFailureCount > kMaxFailureCount)
2093     {
2094         LogCrit("Too many rcp failures, exiting");
2095         DieNow(OT_EXIT_FAILURE);
2096     }
2097 
2098     LogWarn("Trying to recover (%d/%d)", mRcpFailureCount, kMaxFailureCount);
2099 
2100     mState = kStateDisabled;
2101     mRxFrameBuffer.Clear();
2102     mCmdTidsInUse = 0;
2103     mCmdNextTid   = 1;
2104     mTxRadioTid   = 0;
2105     mWaitingTid   = 0;
2106     mError        = OT_ERROR_NONE;
2107     mIsTimeSynced = false;
2108 
2109     if (skipReset)
2110     {
2111         sIsReady = true;
2112     }
2113     else
2114     {
2115         ResetRcp(mResetRadioOnStartup);
2116     }
2117 
2118     SuccessOrDie(Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
2119     mState = kStateSleep;
2120 
2121     RestoreProperties();
2122 
2123     switch (recoveringState)
2124     {
2125     case kStateDisabled:
2126         mState = kStateDisabled;
2127         break;
2128     case kStateSleep:
2129         break;
2130     case kStateReceive:
2131         SuccessOrDie(Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
2132         mState = kStateReceive;
2133         break;
2134     case kStateTransmitting:
2135     case kStateTransmitDone:
2136         SuccessOrDie(Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
2137         mTxError = OT_ERROR_ABORT;
2138         mState   = kStateTransmitDone;
2139         break;
2140     }
2141 
2142     if (mEnergyScanning)
2143     {
2144         SuccessOrDie(EnergyScan(mScanChannel, mScanDuration));
2145     }
2146 
2147     --mRcpFailureCount;
2148     LogNote("RCP recovery is done");
2149 
2150 exit:
2151     return;
2152 #endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
2153 }
2154 
2155 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
RestoreProperties(void)2156 void RadioSpinel::RestoreProperties(void)
2157 {
2158     SuccessOrDie(Set(SPINEL_PROP_MAC_15_4_PANID, SPINEL_DATATYPE_UINT16_S, mPanId));
2159     SuccessOrDie(Set(SPINEL_PROP_MAC_15_4_SADDR, SPINEL_DATATYPE_UINT16_S, mShortAddress));
2160     SuccessOrDie(Set(SPINEL_PROP_MAC_15_4_LADDR, SPINEL_DATATYPE_EUI64_S, mExtendedAddress.m8));
2161     SuccessOrDie(Set(SPINEL_PROP_PHY_CHAN, SPINEL_DATATYPE_UINT8_S, mChannel));
2162 
2163     if (mMacKeySet)
2164     {
2165         SuccessOrDie(Set(SPINEL_PROP_RCP_MAC_KEY,
2166                          SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S
2167                              SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_WLEN_S,
2168                          mKeyIdMode, mKeyId, mPrevKey.m8, sizeof(otMacKey), mCurrKey.m8, sizeof(otMacKey), mNextKey.m8,
2169                          sizeof(otMacKey)));
2170     }
2171 
2172     if (mMacFrameCounterSet)
2173     {
2174         // There is a chance that radio/RCP has used some counters after `mMacFrameCounter` (for enh ack) and they
2175         // are in queue to be sent to host (not yet processed by host RadioSpinel). Here we add some guard jump
2176         // when we restore the frame counter.
2177         // Consider the worst case: the radio/RCP continuously receives the shortest data frame and replies with the
2178         // shortest enhanced ACK. The radio/RCP consumes at most 992 frame counters during the timeout time.
2179         // The frame counter guard is set to 1000 which should ensure that the restored frame counter is unused.
2180         //
2181         // DataFrame: 6(PhyHeader) + 2(Fcf) + 1(Seq) + 6(AddrInfo) + 6(SecHeader) + 1(Payload) + 4(Mic) + 2(Fcs) = 28
2182         // AckFrame : 6(PhyHeader) + 2(Fcf) + 1(Seq) + 6(AddrInfo) + 6(SecHeader) + 2(Ie) + 4(Mic) + 2(Fcs) = 29
2183         // CounterGuard: 2000ms(Timeout) / [(28bytes(Data) + 29bytes(Ack)) * 32us/byte + 192us(Ifs)] = 992
2184         static constexpr uint16_t kFrameCounterGuard = 1000;
2185 
2186         SuccessOrDie(
2187             Set(SPINEL_PROP_RCP_MAC_FRAME_COUNTER, SPINEL_DATATYPE_UINT32_S, mMacFrameCounter + kFrameCounterGuard));
2188     }
2189 
2190     for (int i = 0; i < mSrcMatchShortEntryCount; ++i)
2191     {
2192         SuccessOrDie(
2193             Insert(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, mSrcMatchShortEntries[i]));
2194     }
2195 
2196     for (int i = 0; i < mSrcMatchExtEntryCount; ++i)
2197     {
2198         SuccessOrDie(
2199             Insert(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, mSrcMatchExtEntries[i].m8));
2200     }
2201 
2202     if (mCcaEnergyDetectThresholdSet)
2203     {
2204         SuccessOrDie(Set(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S, mCcaEnergyDetectThreshold));
2205     }
2206 
2207     if (mTransmitPowerSet)
2208     {
2209         SuccessOrDie(Set(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, mTransmitPower));
2210     }
2211 
2212     if (mCoexEnabledSet)
2213     {
2214         SuccessOrDie(Set(SPINEL_PROP_RADIO_COEX_ENABLE, SPINEL_DATATYPE_BOOL_S, mCoexEnabled));
2215     }
2216 
2217     if (mFemLnaGainSet)
2218     {
2219         SuccessOrDie(Set(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, mFemLnaGain));
2220     }
2221 
2222 #if OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
2223     for (uint8_t channel = Radio::kChannelMin; channel <= Radio::kChannelMax; channel++)
2224     {
2225         int8_t power = mMaxPowerTable.GetTransmitPower(channel);
2226 
2227         if (power != OT_RADIO_POWER_INVALID)
2228         {
2229             // Some old RCPs doesn't support max transmit power
2230             otError error = SetChannelMaxTransmitPower(channel, power);
2231 
2232             if (error != OT_ERROR_NONE && error != OT_ERROR_NOT_FOUND)
2233             {
2234                 DieNow(OT_EXIT_FAILURE);
2235             }
2236         }
2237     }
2238 #endif // OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
2239 
2240     if ((sRadioCaps & OT_RADIO_CAPS_RX_ON_WHEN_IDLE) != 0)
2241     {
2242         SuccessOrDie(Set(SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE, SPINEL_DATATYPE_BOOL_S, mRxOnWhenIdle));
2243     }
2244 
2245     CalcRcpTimeOffset();
2246 }
2247 #endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
2248 
GetMultipanActiveInterface(spinel_iid_t * aIid)2249 otError RadioSpinel::GetMultipanActiveInterface(spinel_iid_t *aIid)
2250 {
2251     otError error = Get(SPINEL_PROP_MULTIPAN_ACTIVE_INTERFACE, SPINEL_DATATYPE_UINT8_S, aIid);
2252     LogIfFail("Get GetMultipanActiveInterface failed", error);
2253     return error;
2254 }
2255 
SetMultipanActiveInterface(spinel_iid_t aIid,bool aCompletePending)2256 otError RadioSpinel::SetMultipanActiveInterface(spinel_iid_t aIid, bool aCompletePending)
2257 {
2258     otError error;
2259     uint8_t value;
2260 
2261     VerifyOrExit(aIid == (aIid & SPINEL_MULTIPAN_INTERFACE_ID_MASK), error = OT_ERROR_INVALID_ARGS);
2262 
2263     value = static_cast<uint8_t>(aIid);
2264     if (aCompletePending)
2265     {
2266         value |= (1 << SPINEL_MULTIPAN_INTERFACE_SOFT_SWITCH_SHIFT);
2267     }
2268 
2269     error = Set(SPINEL_PROP_MULTIPAN_ACTIVE_INTERFACE, SPINEL_DATATYPE_UINT8_S, value);
2270 
2271 exit:
2272     return error;
2273 }
2274 
SetChannelMaxTransmitPower(uint8_t aChannel,int8_t aMaxPower)2275 otError RadioSpinel::SetChannelMaxTransmitPower(uint8_t aChannel, int8_t aMaxPower)
2276 {
2277     otError error = OT_ERROR_NONE;
2278     VerifyOrExit(aChannel >= Radio::kChannelMin && aChannel <= Radio::kChannelMax, error = OT_ERROR_INVALID_ARGS);
2279     mMaxPowerTable.SetTransmitPower(aChannel, aMaxPower);
2280     error = Set(SPINEL_PROP_PHY_CHAN_MAX_POWER, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S, aChannel, aMaxPower);
2281 
2282 exit:
2283     return error;
2284 }
2285 
SetRadioRegion(uint16_t aRegionCode)2286 otError RadioSpinel::SetRadioRegion(uint16_t aRegionCode)
2287 {
2288     otError error;
2289 
2290     error = Set(SPINEL_PROP_PHY_REGION_CODE, SPINEL_DATATYPE_UINT16_S, aRegionCode);
2291 
2292     if (error == OT_ERROR_NONE)
2293     {
2294         LogNote("Set region code \"%c%c\" successfully", static_cast<char>(aRegionCode >> 8),
2295                 static_cast<char>(aRegionCode));
2296     }
2297     else
2298     {
2299         LogWarn("Failed to set region code \"%c%c\": %s", static_cast<char>(aRegionCode >> 8),
2300                 static_cast<char>(aRegionCode), otThreadErrorToString(error));
2301     }
2302 
2303     return error;
2304 }
2305 
GetRadioRegion(uint16_t * aRegionCode)2306 otError RadioSpinel::GetRadioRegion(uint16_t *aRegionCode)
2307 {
2308     otError error = OT_ERROR_NONE;
2309 
2310     VerifyOrExit(aRegionCode != nullptr, error = OT_ERROR_INVALID_ARGS);
2311     error = Get(SPINEL_PROP_PHY_REGION_CODE, SPINEL_DATATYPE_UINT16_S, aRegionCode);
2312 
2313 exit:
2314     return error;
2315 }
2316 
2317 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
ConfigureEnhAckProbing(otLinkMetrics aLinkMetrics,const otShortAddress & aShortAddress,const otExtAddress & aExtAddress)2318 otError RadioSpinel::ConfigureEnhAckProbing(otLinkMetrics         aLinkMetrics,
2319                                             const otShortAddress &aShortAddress,
2320                                             const otExtAddress   &aExtAddress)
2321 {
2322     otError error = OT_ERROR_NONE;
2323     uint8_t flags = 0;
2324 
2325     if (aLinkMetrics.mPduCount)
2326     {
2327         flags |= SPINEL_THREAD_LINK_METRIC_PDU_COUNT;
2328     }
2329 
2330     if (aLinkMetrics.mLqi)
2331     {
2332         flags |= SPINEL_THREAD_LINK_METRIC_LQI;
2333     }
2334 
2335     if (aLinkMetrics.mLinkMargin)
2336     {
2337         flags |= SPINEL_THREAD_LINK_METRIC_LINK_MARGIN;
2338     }
2339 
2340     if (aLinkMetrics.mRssi)
2341     {
2342         flags |= SPINEL_THREAD_LINK_METRIC_RSSI;
2343     }
2344 
2345     error =
2346         Set(SPINEL_PROP_RCP_ENH_ACK_PROBING, SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S,
2347             aShortAddress, aExtAddress.m8, flags);
2348 
2349     return error;
2350 }
2351 #endif
2352 
2353 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
GetCslAccuracy(void)2354 uint8_t RadioSpinel::GetCslAccuracy(void)
2355 {
2356     uint8_t accuracy = UINT8_MAX;
2357     otError error    = Get(SPINEL_PROP_RCP_CSL_ACCURACY, SPINEL_DATATYPE_UINT8_S, &accuracy);
2358 
2359     LogIfFail("Get CSL Accuracy failed", error);
2360     return accuracy;
2361 }
2362 #endif
2363 
2364 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
GetCslUncertainty(void)2365 uint8_t RadioSpinel::GetCslUncertainty(void)
2366 {
2367     uint8_t uncertainty = UINT8_MAX;
2368     otError error       = Get(SPINEL_PROP_RCP_CSL_UNCERTAINTY, SPINEL_DATATYPE_UINT8_S, &uncertainty);
2369 
2370     LogIfFail("Get CSL Uncertainty failed", error);
2371     return uncertainty;
2372 }
2373 #endif
2374 
2375 #if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
AddCalibratedPower(uint8_t aChannel,int16_t aActualPower,const uint8_t * aRawPowerSetting,uint16_t aRawPowerSettingLength)2376 otError RadioSpinel::AddCalibratedPower(uint8_t        aChannel,
2377                                         int16_t        aActualPower,
2378                                         const uint8_t *aRawPowerSetting,
2379                                         uint16_t       aRawPowerSettingLength)
2380 {
2381     otError error;
2382 
2383     assert(aRawPowerSetting != nullptr);
2384     SuccessOrExit(error = Insert(SPINEL_PROP_PHY_CALIBRATED_POWER,
2385                                  SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_DATA_WLEN_S, aChannel,
2386                                  aActualPower, aRawPowerSetting, aRawPowerSettingLength));
2387 
2388 exit:
2389     return error;
2390 }
2391 
ClearCalibratedPowers(void)2392 otError RadioSpinel::ClearCalibratedPowers(void) { return Set(SPINEL_PROP_PHY_CALIBRATED_POWER, nullptr); }
2393 
SetChannelTargetPower(uint8_t aChannel,int16_t aTargetPower)2394 otError RadioSpinel::SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower)
2395 {
2396     otError error = OT_ERROR_NONE;
2397     VerifyOrExit(aChannel >= Radio::kChannelMin && aChannel <= Radio::kChannelMax, error = OT_ERROR_INVALID_ARGS);
2398     error =
2399         Set(SPINEL_PROP_PHY_CHAN_TARGET_POWER, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, aChannel, aTargetPower);
2400 
2401 exit:
2402     return error;
2403 }
2404 #endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
2405 
Snprintf(char * aDest,uint32_t aSize,const char * aFormat,...)2406 uint32_t RadioSpinel::Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...)
2407 {
2408     int     len;
2409     va_list args;
2410 
2411     va_start(args, aFormat);
2412     len = vsnprintf(aDest, static_cast<size_t>(aSize), aFormat, args);
2413     va_end(args);
2414 
2415     return (len < 0) ? 0 : Min(static_cast<uint32_t>(len), aSize - 1);
2416 }
2417 
LogSpinelFrame(const uint8_t * aFrame,uint16_t aLength,bool aTx)2418 void RadioSpinel::LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx)
2419 {
2420     otError           error                               = OT_ERROR_NONE;
2421     char              buf[OPENTHREAD_CONFIG_LOG_MAX_SIZE] = {0};
2422     spinel_ssize_t    unpacked;
2423     uint8_t           header;
2424     uint32_t          cmd;
2425     spinel_prop_key_t key;
2426     uint8_t          *data;
2427     spinel_size_t     len;
2428     const char       *prefix = nullptr;
2429     char             *start  = buf;
2430     char             *end    = buf + sizeof(buf);
2431 
2432     VerifyOrExit(otLoggingGetLevel() >= OT_LOG_LEVEL_DEBG);
2433 
2434     prefix   = aTx ? "Sent spinel frame" : "Received spinel frame";
2435     unpacked = spinel_datatype_unpack(aFrame, aLength, "CiiD", &header, &cmd, &key, &data, &len);
2436     VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2437 
2438     start += Snprintf(start, static_cast<uint32_t>(end - start), "%s, flg:0x%x, iid:%d, tid:%u, cmd:%s", prefix,
2439                       SPINEL_HEADER_GET_FLAG(header), SPINEL_HEADER_GET_IID(header), SPINEL_HEADER_GET_TID(header),
2440                       spinel_command_to_cstr(cmd));
2441     VerifyOrExit(cmd != SPINEL_CMD_RESET);
2442 
2443     start += Snprintf(start, static_cast<uint32_t>(end - start), ", key:%s", spinel_prop_key_to_cstr(key));
2444     VerifyOrExit(cmd != SPINEL_CMD_PROP_VALUE_GET);
2445 
2446     switch (key)
2447     {
2448     case SPINEL_PROP_LAST_STATUS:
2449     {
2450         spinel_status_t status;
2451 
2452         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &status);
2453         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2454         start += Snprintf(start, static_cast<uint32_t>(end - start), ", status:%s", spinel_status_to_cstr(status));
2455     }
2456     break;
2457 
2458     case SPINEL_PROP_MAC_RAW_STREAM_ENABLED:
2459     case SPINEL_PROP_MAC_SRC_MATCH_ENABLED:
2460     case SPINEL_PROP_PHY_ENABLED:
2461     case SPINEL_PROP_RADIO_COEX_ENABLE:
2462     {
2463         bool enabled;
2464 
2465         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_BOOL_S, &enabled);
2466         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2467         start += Snprintf(start, static_cast<uint32_t>(end - start), ", enabled:%u", enabled);
2468     }
2469     break;
2470 
2471     case SPINEL_PROP_PHY_CCA_THRESHOLD:
2472     case SPINEL_PROP_PHY_FEM_LNA_GAIN:
2473     case SPINEL_PROP_PHY_RX_SENSITIVITY:
2474     case SPINEL_PROP_PHY_RSSI:
2475     case SPINEL_PROP_PHY_TX_POWER:
2476     {
2477         const char *name = nullptr;
2478         int8_t      value;
2479 
2480         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_INT8_S, &value);
2481         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2482 
2483         switch (key)
2484         {
2485         case SPINEL_PROP_PHY_TX_POWER:
2486             name = "power";
2487             break;
2488         case SPINEL_PROP_PHY_CCA_THRESHOLD:
2489             name = "threshold";
2490             break;
2491         case SPINEL_PROP_PHY_FEM_LNA_GAIN:
2492             name = "gain";
2493             break;
2494         case SPINEL_PROP_PHY_RX_SENSITIVITY:
2495             name = "sensitivity";
2496             break;
2497         case SPINEL_PROP_PHY_RSSI:
2498             name = "rssi";
2499             break;
2500         }
2501 
2502         start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%d", name, value);
2503     }
2504     break;
2505 
2506     case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
2507     case SPINEL_PROP_MAC_SCAN_STATE:
2508     case SPINEL_PROP_PHY_CHAN:
2509     case SPINEL_PROP_RCP_CSL_ACCURACY:
2510     case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
2511     {
2512         const char *name = nullptr;
2513         uint8_t     value;
2514 
2515         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &value);
2516         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2517 
2518         switch (key)
2519         {
2520         case SPINEL_PROP_MAC_SCAN_STATE:
2521             name = "state";
2522             break;
2523         case SPINEL_PROP_RCP_CSL_ACCURACY:
2524             name = "accuracy";
2525             break;
2526         case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
2527             name = "uncertainty";
2528             break;
2529         case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
2530             name = "mode";
2531             break;
2532         case SPINEL_PROP_PHY_CHAN:
2533             name = "channel";
2534             break;
2535         }
2536 
2537         start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
2538     }
2539     break;
2540 
2541     case SPINEL_PROP_MAC_15_4_PANID:
2542     case SPINEL_PROP_MAC_15_4_SADDR:
2543     case SPINEL_PROP_MAC_SCAN_PERIOD:
2544     case SPINEL_PROP_PHY_REGION_CODE:
2545     {
2546         const char *name = nullptr;
2547         uint16_t    value;
2548 
2549         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &value);
2550         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2551 
2552         switch (key)
2553         {
2554         case SPINEL_PROP_MAC_SCAN_PERIOD:
2555             name = "period";
2556             break;
2557         case SPINEL_PROP_PHY_REGION_CODE:
2558             name = "region";
2559             break;
2560         case SPINEL_PROP_MAC_15_4_SADDR:
2561             name = "saddr";
2562             break;
2563         case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
2564             name = "saddr";
2565             break;
2566         case SPINEL_PROP_MAC_15_4_PANID:
2567             name = "panid";
2568             break;
2569         }
2570 
2571         start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:0x%04x", name, value);
2572     }
2573     break;
2574 
2575     case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
2576     {
2577         uint16_t saddr;
2578 
2579         start += Snprintf(start, static_cast<uint32_t>(end - start), ", saddr:");
2580 
2581         if (len < sizeof(saddr))
2582         {
2583             start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
2584         }
2585         else
2586         {
2587             while (len >= sizeof(saddr))
2588             {
2589                 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &saddr);
2590                 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2591                 data += unpacked;
2592                 len -= static_cast<spinel_size_t>(unpacked);
2593                 start += Snprintf(start, static_cast<uint32_t>(end - start), "0x%04x ", saddr);
2594             }
2595         }
2596     }
2597     break;
2598 
2599     case SPINEL_PROP_RCP_MAC_FRAME_COUNTER:
2600     case SPINEL_PROP_RCP_TIMESTAMP:
2601     {
2602         const char *name;
2603         uint32_t    value;
2604 
2605         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT32_S, &value);
2606         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2607 
2608         name = (key == SPINEL_PROP_RCP_TIMESTAMP) ? "timestamp" : "counter";
2609         start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
2610     }
2611     break;
2612 
2613     case SPINEL_PROP_RADIO_CAPS:
2614     case SPINEL_PROP_RCP_API_VERSION:
2615     case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
2616     {
2617         const char  *name;
2618         unsigned int value;
2619 
2620         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &value);
2621         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2622 
2623         switch (key)
2624         {
2625         case SPINEL_PROP_RADIO_CAPS:
2626             name = "caps";
2627             break;
2628         case SPINEL_PROP_RCP_API_VERSION:
2629             name = "version";
2630             break;
2631         case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
2632             name = "min-host-version";
2633             break;
2634         default:
2635             name = "";
2636             break;
2637         }
2638 
2639         start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
2640     }
2641     break;
2642 
2643     case SPINEL_PROP_MAC_ENERGY_SCAN_RESULT:
2644     case SPINEL_PROP_PHY_CHAN_MAX_POWER:
2645     {
2646         const char *name;
2647         uint8_t     channel;
2648         int8_t      value;
2649 
2650         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S, &channel, &value);
2651         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2652 
2653         name = (key == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT) ? "rssi" : "power";
2654         start += Snprintf(start, static_cast<uint32_t>(end - start), ", channel:%u, %s:%d", channel, name, value);
2655     }
2656     break;
2657 
2658     case SPINEL_PROP_CAPS:
2659     {
2660         unsigned int capability;
2661 
2662         start += Snprintf(start, static_cast<uint32_t>(end - start), ", caps:");
2663 
2664         while (len > 0)
2665         {
2666             unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &capability);
2667             VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2668             data += unpacked;
2669             len -= static_cast<spinel_size_t>(unpacked);
2670             start += Snprintf(start, static_cast<uint32_t>(end - start), "%s ", spinel_capability_to_cstr(capability));
2671         }
2672     }
2673     break;
2674 
2675     case SPINEL_PROP_PROTOCOL_VERSION:
2676     {
2677         unsigned int major;
2678         unsigned int minor;
2679 
2680         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S,
2681                                           &major, &minor);
2682         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2683         start += Snprintf(start, static_cast<uint32_t>(end - start), ", major:%u, minor:%u", major, minor);
2684     }
2685     break;
2686 
2687     case SPINEL_PROP_PHY_CHAN_PREFERRED:
2688     case SPINEL_PROP_PHY_CHAN_SUPPORTED:
2689     {
2690         uint8_t        maskBuffer[kChannelMaskBufferSize];
2691         uint32_t       channelMask = 0;
2692         const uint8_t *maskData    = maskBuffer;
2693         spinel_size_t  maskLength  = sizeof(maskBuffer);
2694 
2695         unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength);
2696         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2697 
2698         while (maskLength > 0)
2699         {
2700             uint8_t channel;
2701 
2702             unpacked = spinel_datatype_unpack(maskData, maskLength, SPINEL_DATATYPE_UINT8_S, &channel);
2703             VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2704             VerifyOrExit(channel < kChannelMaskBufferSize, error = OT_ERROR_PARSE);
2705             channelMask |= (1UL << channel);
2706 
2707             maskData += unpacked;
2708             maskLength -= static_cast<spinel_size_t>(unpacked);
2709         }
2710 
2711         start += Snprintf(start, static_cast<uint32_t>(end - start), ", channelMask:0x%08x", channelMask);
2712     }
2713     break;
2714 
2715     case SPINEL_PROP_NCP_VERSION:
2716     {
2717         const char *version;
2718 
2719         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &version);
2720         VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
2721         start += Snprintf(start, static_cast<uint32_t>(end - start), ", version:%s", version);
2722     }
2723     break;
2724 
2725     case SPINEL_PROP_STREAM_RAW:
2726     {
2727         otRadioFrame frame;
2728 
2729         if (cmd == SPINEL_CMD_PROP_VALUE_IS)
2730         {
2731             uint16_t     flags;
2732             int8_t       noiseFloor;
2733             unsigned int receiveError;
2734 
2735             unpacked = spinel_datatype_unpack(data, len,
2736                                               SPINEL_DATATYPE_DATA_WLEN_S                          // Frame
2737                                                   SPINEL_DATATYPE_INT8_S                           // RSSI
2738                                                       SPINEL_DATATYPE_INT8_S                       // Noise Floor
2739                                                           SPINEL_DATATYPE_UINT16_S                 // Flags
2740                                                               SPINEL_DATATYPE_STRUCT_S(            // PHY-data
2741                                                                   SPINEL_DATATYPE_UINT8_S          // 802.15.4 channel
2742                                                                       SPINEL_DATATYPE_UINT8_S      // 802.15.4 LQI
2743                                                                           SPINEL_DATATYPE_UINT64_S // Timestamp (us).
2744                                                                   ) SPINEL_DATATYPE_STRUCT_S(      // Vendor-data
2745                                                                   SPINEL_DATATYPE_UINT_PACKED_S    // Receive error
2746                                                                   ),
2747                                               &frame.mPsdu, &frame.mLength, &frame.mInfo.mRxInfo.mRssi, &noiseFloor,
2748                                               &flags, &frame.mChannel, &frame.mInfo.mRxInfo.mLqi,
2749                                               &frame.mInfo.mRxInfo.mTimestamp, &receiveError);
2750             VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2751             start += Snprintf(start, static_cast<uint32_t>(end - start), ", len:%u, rssi:%d ...", frame.mLength,
2752                               frame.mInfo.mRxInfo.mRssi);
2753             OT_UNUSED_VARIABLE(start); // Avoid static analysis error
2754             LogDebg("%s", buf);
2755 
2756             start = buf;
2757             start += Snprintf(start, static_cast<uint32_t>(end - start),
2758                               "... noise:%d, flags:0x%04x, channel:%u, lqi:%u, timestamp:%lu, rxerr:%u", noiseFloor,
2759                               flags, frame.mChannel, frame.mInfo.mRxInfo.mLqi,
2760                               static_cast<unsigned long>(frame.mInfo.mRxInfo.mTimestamp), receiveError);
2761         }
2762         else if (cmd == SPINEL_CMD_PROP_VALUE_SET)
2763         {
2764             bool csmaCaEnabled;
2765             bool isHeaderUpdated;
2766             bool isARetx;
2767             bool skipAes;
2768 
2769             unpacked = spinel_datatype_unpack(
2770                 data, len,
2771                 SPINEL_DATATYPE_DATA_WLEN_S                                   // Frame data
2772                     SPINEL_DATATYPE_UINT8_S                                   // Channel
2773                         SPINEL_DATATYPE_UINT8_S                               // MaxCsmaBackoffs
2774                             SPINEL_DATATYPE_UINT8_S                           // MaxFrameRetries
2775                                 SPINEL_DATATYPE_BOOL_S                        // CsmaCaEnabled
2776                                     SPINEL_DATATYPE_BOOL_S                    // IsHeaderUpdated
2777                                         SPINEL_DATATYPE_BOOL_S                // IsARetx
2778                                             SPINEL_DATATYPE_BOOL_S            // SkipAes
2779                                                 SPINEL_DATATYPE_UINT32_S      // TxDelay
2780                                                     SPINEL_DATATYPE_UINT32_S, // TxDelayBaseTime
2781                 &frame.mPsdu, &frame.mLength, &frame.mChannel, &frame.mInfo.mTxInfo.mMaxCsmaBackoffs,
2782                 &frame.mInfo.mTxInfo.mMaxFrameRetries, &csmaCaEnabled, &isHeaderUpdated, &isARetx, &skipAes,
2783                 &frame.mInfo.mTxInfo.mTxDelay, &frame.mInfo.mTxInfo.mTxDelayBaseTime);
2784 
2785             VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2786             start += Snprintf(start, static_cast<uint32_t>(end - start),
2787                               ", len:%u, channel:%u, maxbackoffs:%u, maxretries:%u ...", frame.mLength, frame.mChannel,
2788                               frame.mInfo.mTxInfo.mMaxCsmaBackoffs, frame.mInfo.mTxInfo.mMaxFrameRetries);
2789             OT_UNUSED_VARIABLE(start); // Avoid static analysis error
2790             LogDebg("%s", buf);
2791 
2792             start = buf;
2793             start += Snprintf(start, static_cast<uint32_t>(end - start),
2794                               "... csmaCaEnabled:%u, isHeaderUpdated:%u, isARetx:%u, skipAes:%u"
2795                               ", txDelay:%u, txDelayBase:%u",
2796                               csmaCaEnabled, isHeaderUpdated, isARetx, skipAes, frame.mInfo.mTxInfo.mTxDelay,
2797                               frame.mInfo.mTxInfo.mTxDelayBaseTime);
2798         }
2799     }
2800     break;
2801 
2802     case SPINEL_PROP_STREAM_DEBUG:
2803     {
2804         char          debugString[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE + 1];
2805         spinel_size_t stringLength = sizeof(debugString);
2806 
2807         unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, debugString, &stringLength);
2808         assert(stringLength < sizeof(debugString));
2809         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2810         debugString[stringLength] = '\0';
2811         start += Snprintf(start, static_cast<uint32_t>(end - start), ", debug:%s", debugString);
2812     }
2813     break;
2814 
2815     case SPINEL_PROP_STREAM_LOG:
2816     {
2817         const char *logString;
2818         uint8_t     logLevel;
2819 
2820         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &logString);
2821         VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
2822         data += unpacked;
2823         len -= static_cast<spinel_size_t>(unpacked);
2824 
2825         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &logLevel);
2826         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2827         start += Snprintf(start, static_cast<uint32_t>(end - start), ", level:%u, log:%s", logLevel, logString);
2828     }
2829     break;
2830 
2831     case SPINEL_PROP_NEST_STREAM_MFG:
2832     {
2833         const char *output;
2834         size_t      outputLen;
2835 
2836         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &output, &outputLen);
2837         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2838         start += Snprintf(start, static_cast<uint32_t>(end - start), ", diag:%s", output);
2839     }
2840     break;
2841 
2842     case SPINEL_PROP_RCP_MAC_KEY:
2843     {
2844         uint8_t      keyIdMode;
2845         uint8_t      keyId;
2846         otMacKey     prevKey;
2847         unsigned int prevKeyLen = sizeof(otMacKey);
2848         otMacKey     currKey;
2849         unsigned int currKeyLen = sizeof(otMacKey);
2850         otMacKey     nextKey;
2851         unsigned int nextKeyLen = sizeof(otMacKey);
2852 
2853         unpacked = spinel_datatype_unpack(data, len,
2854                                           SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S
2855                                               SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_WLEN_S,
2856                                           &keyIdMode, &keyId, prevKey.m8, &prevKeyLen, currKey.m8, &currKeyLen,
2857                                           nextKey.m8, &nextKeyLen);
2858         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2859         start += Snprintf(start, static_cast<uint32_t>(end - start),
2860                           ", keyIdMode:%u, keyId:%u, prevKey:***, currKey:***, nextKey:***", keyIdMode, keyId);
2861     }
2862     break;
2863 
2864     case SPINEL_PROP_HWADDR:
2865     case SPINEL_PROP_MAC_15_4_LADDR:
2866     {
2867         const char *name                    = nullptr;
2868         uint8_t     m8[OT_EXT_ADDRESS_SIZE] = {0};
2869 
2870         unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, &m8[0]);
2871         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2872 
2873         name = (key == SPINEL_PROP_HWADDR) ? "eui64" : "laddr";
2874         start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%02x%02x%02x%02x%02x%02x%02x%02x", name,
2875                           m8[0], m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
2876     }
2877     break;
2878 
2879     case SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES:
2880     {
2881         uint8_t m8[OT_EXT_ADDRESS_SIZE];
2882 
2883         start += Snprintf(start, static_cast<uint32_t>(end - start), ", extaddr:");
2884 
2885         if (len < sizeof(m8))
2886         {
2887             start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
2888         }
2889         else
2890         {
2891             while (len >= sizeof(m8))
2892             {
2893                 unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, m8);
2894                 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2895                 data += unpacked;
2896                 len -= static_cast<spinel_size_t>(unpacked);
2897                 start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x%02x%02x%02x%02x%02x%02x%02x ", m8[0],
2898                                   m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
2899             }
2900         }
2901     }
2902     break;
2903 
2904     case SPINEL_PROP_RADIO_COEX_METRICS:
2905     {
2906         otRadioCoexMetrics metrics;
2907         unpacked = spinel_datatype_unpack(
2908             data, len,
2909             SPINEL_DATATYPE_STRUCT_S(                                    // Tx Coex Metrics Structure
2910                 SPINEL_DATATYPE_UINT32_S                                 // NumTxRequest
2911                     SPINEL_DATATYPE_UINT32_S                             // NumTxGrantImmediate
2912                         SPINEL_DATATYPE_UINT32_S                         // NumTxGrantWait
2913                             SPINEL_DATATYPE_UINT32_S                     // NumTxGrantWaitActivated
2914                                 SPINEL_DATATYPE_UINT32_S                 // NumTxGrantWaitTimeout
2915                                     SPINEL_DATATYPE_UINT32_S             // NumTxGrantDeactivatedDuringRequest
2916                                         SPINEL_DATATYPE_UINT32_S         // NumTxDelayedGrant
2917                                             SPINEL_DATATYPE_UINT32_S     // AvgTxRequestToGrantTime
2918                 ) SPINEL_DATATYPE_STRUCT_S(                              // Rx Coex Metrics Structure
2919                 SPINEL_DATATYPE_UINT32_S                                 // NumRxRequest
2920                     SPINEL_DATATYPE_UINT32_S                             // NumRxGrantImmediate
2921                         SPINEL_DATATYPE_UINT32_S                         // NumRxGrantWait
2922                             SPINEL_DATATYPE_UINT32_S                     // NumRxGrantWaitActivated
2923                                 SPINEL_DATATYPE_UINT32_S                 // NumRxGrantWaitTimeout
2924                                     SPINEL_DATATYPE_UINT32_S             // NumRxGrantDeactivatedDuringRequest
2925                                         SPINEL_DATATYPE_UINT32_S         // NumRxDelayedGrant
2926                                             SPINEL_DATATYPE_UINT32_S     // AvgRxRequestToGrantTime
2927                                                 SPINEL_DATATYPE_UINT32_S // NumRxGrantNone
2928                 ) SPINEL_DATATYPE_BOOL_S                                 // Stopped
2929                 SPINEL_DATATYPE_UINT32_S,                                // NumGrantGlitch
2930             &metrics.mNumTxRequest, &metrics.mNumTxGrantImmediate, &metrics.mNumTxGrantWait,
2931             &metrics.mNumTxGrantWaitActivated, &metrics.mNumTxGrantWaitTimeout,
2932             &metrics.mNumTxGrantDeactivatedDuringRequest, &metrics.mNumTxDelayedGrant,
2933             &metrics.mAvgTxRequestToGrantTime, &metrics.mNumRxRequest, &metrics.mNumRxGrantImmediate,
2934             &metrics.mNumRxGrantWait, &metrics.mNumRxGrantWaitActivated, &metrics.mNumRxGrantWaitTimeout,
2935             &metrics.mNumRxGrantDeactivatedDuringRequest, &metrics.mNumRxDelayedGrant,
2936             &metrics.mAvgRxRequestToGrantTime, &metrics.mNumRxGrantNone, &metrics.mStopped, &metrics.mNumGrantGlitch);
2937 
2938         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2939 
2940         LogDebg("%s ...", buf);
2941         LogDebg(" txRequest:%lu", ToUlong(metrics.mNumTxRequest));
2942         LogDebg(" txGrantImmediate:%lu", ToUlong(metrics.mNumTxGrantImmediate));
2943         LogDebg(" txGrantWait:%lu", ToUlong(metrics.mNumTxGrantWait));
2944         LogDebg(" txGrantWaitActivated:%lu", ToUlong(metrics.mNumTxGrantWaitActivated));
2945         LogDebg(" txGrantWaitTimeout:%lu", ToUlong(metrics.mNumTxGrantWaitTimeout));
2946         LogDebg(" txGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumTxGrantDeactivatedDuringRequest));
2947         LogDebg(" txDelayedGrant:%lu", ToUlong(metrics.mNumTxDelayedGrant));
2948         LogDebg(" avgTxRequestToGrantTime:%lu", ToUlong(metrics.mAvgTxRequestToGrantTime));
2949         LogDebg(" rxRequest:%lu", ToUlong(metrics.mNumRxRequest));
2950         LogDebg(" rxGrantImmediate:%lu", ToUlong(metrics.mNumRxGrantImmediate));
2951         LogDebg(" rxGrantWait:%lu", ToUlong(metrics.mNumRxGrantWait));
2952         LogDebg(" rxGrantWaitActivated:%lu", ToUlong(metrics.mNumRxGrantWaitActivated));
2953         LogDebg(" rxGrantWaitTimeout:%lu", ToUlong(metrics.mNumRxGrantWaitTimeout));
2954         LogDebg(" rxGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumRxGrantDeactivatedDuringRequest));
2955         LogDebg(" rxDelayedGrant:%lu", ToUlong(metrics.mNumRxDelayedGrant));
2956         LogDebg(" avgRxRequestToGrantTime:%lu", ToUlong(metrics.mAvgRxRequestToGrantTime));
2957         LogDebg(" rxGrantNone:%lu", ToUlong(metrics.mNumRxGrantNone));
2958         LogDebg(" stopped:%u", metrics.mStopped);
2959 
2960         start = buf;
2961         start += Snprintf(start, static_cast<uint32_t>(end - start), " grantGlitch:%u", metrics.mNumGrantGlitch);
2962     }
2963     break;
2964 
2965     case SPINEL_PROP_MAC_SCAN_MASK:
2966     {
2967         constexpr uint8_t kNumChannels = 16;
2968         uint8_t           channels[kNumChannels];
2969         spinel_size_t     size;
2970 
2971         unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_DATA_S, channels, &size);
2972         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2973         start += Snprintf(start, static_cast<uint32_t>(end - start), ", channels:");
2974 
2975         for (spinel_size_t i = 0; i < size; i++)
2976         {
2977             start += Snprintf(start, static_cast<uint32_t>(end - start), "%u ", channels[i]);
2978         }
2979     }
2980     break;
2981 
2982     case SPINEL_PROP_RCP_ENH_ACK_PROBING:
2983     {
2984         uint16_t saddr;
2985         uint8_t  m8[OT_EXT_ADDRESS_SIZE];
2986         uint8_t  flags;
2987 
2988         unpacked = spinel_datatype_unpack(
2989             data, len, SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S, &saddr, m8, &flags);
2990 
2991         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2992         start += Snprintf(start, static_cast<uint32_t>(end - start),
2993                           ", saddr:%04x, extaddr:%02x%02x%02x%02x%02x%02x%02x%02x, flags:0x%02x", saddr, m8[0], m8[1],
2994                           m8[2], m8[3], m8[4], m8[5], m8[6], m8[7], flags);
2995     }
2996     break;
2997 
2998     case SPINEL_PROP_PHY_CALIBRATED_POWER:
2999     {
3000         if (cmd == SPINEL_CMD_PROP_VALUE_INSERT)
3001         {
3002             uint8_t      channel;
3003             int16_t      actualPower;
3004             uint8_t     *rawPowerSetting;
3005             unsigned int rawPowerSettingLength;
3006 
3007             unpacked = spinel_datatype_unpack(
3008                 data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_DATA_WLEN_S, &channel,
3009                 &actualPower, &rawPowerSetting, &rawPowerSettingLength);
3010             VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
3011 
3012             start += Snprintf(start, static_cast<uint32_t>(end - start),
3013                               ", ch:%u, actualPower:%d, rawPowerSetting:", channel, actualPower);
3014             for (unsigned int i = 0; i < rawPowerSettingLength; i++)
3015             {
3016                 start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x", rawPowerSetting[i]);
3017             }
3018         }
3019     }
3020     break;
3021 
3022     case SPINEL_PROP_PHY_CHAN_TARGET_POWER:
3023     {
3024         uint8_t channel;
3025         int16_t targetPower;
3026 
3027         unpacked =
3028             spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, &channel, &targetPower);
3029         VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
3030         start += Snprintf(start, static_cast<uint32_t>(end - start), ", ch:%u, targetPower:%d", channel, targetPower);
3031     }
3032     break;
3033     }
3034 
3035 exit:
3036     OT_UNUSED_VARIABLE(start); // Avoid static analysis error
3037     if (error == OT_ERROR_NONE)
3038     {
3039         LogDebg("%s", buf);
3040     }
3041     else if (prefix != nullptr)
3042     {
3043         LogDebg("%s, failed to parse spinel frame !", prefix);
3044     }
3045 }
3046 
SpinelStatusToOtError(spinel_status_t aStatus)3047 otError RadioSpinel::SpinelStatusToOtError(spinel_status_t aStatus)
3048 {
3049     otError ret;
3050 
3051     switch (aStatus)
3052     {
3053     case SPINEL_STATUS_OK:
3054         ret = OT_ERROR_NONE;
3055         break;
3056 
3057     case SPINEL_STATUS_FAILURE:
3058         ret = OT_ERROR_FAILED;
3059         break;
3060 
3061     case SPINEL_STATUS_DROPPED:
3062         ret = OT_ERROR_DROP;
3063         break;
3064 
3065     case SPINEL_STATUS_NOMEM:
3066         ret = OT_ERROR_NO_BUFS;
3067         break;
3068 
3069     case SPINEL_STATUS_BUSY:
3070         ret = OT_ERROR_BUSY;
3071         break;
3072 
3073     case SPINEL_STATUS_PARSE_ERROR:
3074         ret = OT_ERROR_PARSE;
3075         break;
3076 
3077     case SPINEL_STATUS_INVALID_ARGUMENT:
3078         ret = OT_ERROR_INVALID_ARGS;
3079         break;
3080 
3081     case SPINEL_STATUS_UNIMPLEMENTED:
3082         ret = OT_ERROR_NOT_IMPLEMENTED;
3083         break;
3084 
3085     case SPINEL_STATUS_INVALID_STATE:
3086         ret = OT_ERROR_INVALID_STATE;
3087         break;
3088 
3089     case SPINEL_STATUS_NO_ACK:
3090         ret = OT_ERROR_NO_ACK;
3091         break;
3092 
3093     case SPINEL_STATUS_CCA_FAILURE:
3094         ret = OT_ERROR_CHANNEL_ACCESS_FAILURE;
3095         break;
3096 
3097     case SPINEL_STATUS_ALREADY:
3098         ret = OT_ERROR_ALREADY;
3099         break;
3100 
3101     case SPINEL_STATUS_PROP_NOT_FOUND:
3102         ret = OT_ERROR_NOT_IMPLEMENTED;
3103         break;
3104 
3105     case SPINEL_STATUS_ITEM_NOT_FOUND:
3106         ret = OT_ERROR_NOT_FOUND;
3107         break;
3108 
3109     default:
3110         if (aStatus >= SPINEL_STATUS_STACK_NATIVE__BEGIN && aStatus <= SPINEL_STATUS_STACK_NATIVE__END)
3111         {
3112             ret = static_cast<otError>(aStatus - SPINEL_STATUS_STACK_NATIVE__BEGIN);
3113         }
3114         else
3115         {
3116             ret = OT_ERROR_FAILED;
3117         }
3118         break;
3119     }
3120 
3121     return ret;
3122 }
3123 
LogIfFail(const char * aText,otError aError)3124 void RadioSpinel::LogIfFail(const char *aText, otError aError)
3125 {
3126     OT_UNUSED_VARIABLE(aText);
3127 
3128     if (aError != OT_ERROR_NONE && aError != OT_ERROR_NO_ACK)
3129     {
3130         LogWarn("%s: %s", aText, otThreadErrorToString(aError));
3131     }
3132 }
3133 
3134 static const char kModuleName[] = "RadioSpinel";
3135 
LogCrit(const char * aFormat,...)3136 void RadioSpinel::LogCrit(const char *aFormat, ...)
3137 {
3138     va_list args;
3139 
3140     va_start(args, aFormat);
3141     otLogPlatArgs(OT_LOG_LEVEL_CRIT, kModuleName, aFormat, args);
3142     va_end(args);
3143 }
3144 
LogWarn(const char * aFormat,...)3145 void RadioSpinel::LogWarn(const char *aFormat, ...)
3146 {
3147     va_list args;
3148 
3149     va_start(args, aFormat);
3150     otLogPlatArgs(OT_LOG_LEVEL_WARN, kModuleName, aFormat, args);
3151     va_end(args);
3152 }
3153 
LogNote(const char * aFormat,...)3154 void RadioSpinel::LogNote(const char *aFormat, ...)
3155 {
3156     va_list args;
3157 
3158     va_start(args, aFormat);
3159     otLogPlatArgs(OT_LOG_LEVEL_NOTE, kModuleName, aFormat, args);
3160     va_end(args);
3161 }
3162 
LogInfo(const char * aFormat,...)3163 void RadioSpinel::LogInfo(const char *aFormat, ...)
3164 {
3165     va_list args;
3166 
3167     va_start(args, aFormat);
3168     otLogPlatArgs(OT_LOG_LEVEL_INFO, kModuleName, aFormat, args);
3169     va_end(args);
3170 }
3171 
LogDebg(const char * aFormat,...)3172 void RadioSpinel::LogDebg(const char *aFormat, ...)
3173 {
3174     va_list args;
3175 
3176     va_start(args, aFormat);
3177     otLogPlatArgs(OT_LOG_LEVEL_DEBG, kModuleName, aFormat, args);
3178     va_end(args);
3179 }
3180 
3181 } // namespace Spinel
3182 } // namespace ot
3183