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