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