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