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