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