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