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