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