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