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