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