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