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