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