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