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