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
68 #ifndef TX_WAIT_US
69 #define TX_WAIT_US (5 * US_PER_S)
70 #endif
71
72 using ot::Spinel::Decoder;
73
74 namespace ot {
75 namespace Spinel {
76
SpinelStatusToOtError(spinel_status_t aError)77 static inline otError SpinelStatusToOtError(spinel_status_t aError)
78 {
79 otError ret;
80
81 switch (aError)
82 {
83 case SPINEL_STATUS_OK:
84 ret = OT_ERROR_NONE;
85 break;
86
87 case SPINEL_STATUS_FAILURE:
88 ret = OT_ERROR_FAILED;
89 break;
90
91 case SPINEL_STATUS_DROPPED:
92 ret = OT_ERROR_DROP;
93 break;
94
95 case SPINEL_STATUS_NOMEM:
96 ret = OT_ERROR_NO_BUFS;
97 break;
98
99 case SPINEL_STATUS_BUSY:
100 ret = OT_ERROR_BUSY;
101 break;
102
103 case SPINEL_STATUS_PARSE_ERROR:
104 ret = OT_ERROR_PARSE;
105 break;
106
107 case SPINEL_STATUS_INVALID_ARGUMENT:
108 ret = OT_ERROR_INVALID_ARGS;
109 break;
110
111 case SPINEL_STATUS_UNIMPLEMENTED:
112 ret = OT_ERROR_NOT_IMPLEMENTED;
113 break;
114
115 case SPINEL_STATUS_INVALID_STATE:
116 ret = OT_ERROR_INVALID_STATE;
117 break;
118
119 case SPINEL_STATUS_NO_ACK:
120 ret = OT_ERROR_NO_ACK;
121 break;
122
123 case SPINEL_STATUS_CCA_FAILURE:
124 ret = OT_ERROR_CHANNEL_ACCESS_FAILURE;
125 break;
126
127 case SPINEL_STATUS_ALREADY:
128 ret = OT_ERROR_ALREADY;
129 break;
130
131 case SPINEL_STATUS_PROP_NOT_FOUND:
132 ret = OT_ERROR_NOT_IMPLEMENTED;
133 break;
134
135 case SPINEL_STATUS_ITEM_NOT_FOUND:
136 ret = OT_ERROR_NOT_FOUND;
137 break;
138
139 default:
140 if (aError >= SPINEL_STATUS_STACK_NATIVE__BEGIN && aError <= SPINEL_STATUS_STACK_NATIVE__END)
141 {
142 ret = static_cast<otError>(aError - SPINEL_STATUS_STACK_NATIVE__BEGIN);
143 }
144 else
145 {
146 ret = OT_ERROR_FAILED;
147 }
148 break;
149 }
150
151 return ret;
152 }
153
LogIfFail(const char * aText,otError aError)154 static inline void LogIfFail(const char *aText, otError aError)
155 {
156 OT_UNUSED_VARIABLE(aText);
157 OT_UNUSED_VARIABLE(aError);
158
159 if (aError != OT_ERROR_NONE && aError != OT_ERROR_NO_ACK)
160 {
161 otLogWarnPlat("%s: %s", aText, otThreadErrorToString(aError));
162 }
163 }
164
165 template <typename InterfaceType, typename ProcessContextType>
HandleReceivedFrame(void * aContext)166 void RadioSpinel<InterfaceType, ProcessContextType>::HandleReceivedFrame(void *aContext)
167 {
168 static_cast<RadioSpinel *>(aContext)->HandleReceivedFrame();
169 }
170
171 template <typename InterfaceType, typename ProcessContextType>
RadioSpinel(void)172 RadioSpinel<InterfaceType, ProcessContextType>::RadioSpinel(void)
173 : mInstance(nullptr)
174 , mRxFrameBuffer()
175 , mSpinelInterface(HandleReceivedFrame, this, mRxFrameBuffer)
176 , mCmdTidsInUse(0)
177 , mCmdNextTid(1)
178 , mTxRadioTid(0)
179 , mWaitingTid(0)
180 , mWaitingKey(SPINEL_PROP_LAST_STATUS)
181 , mPropertyFormat(nullptr)
182 , mExpectedCommand(0)
183 , mError(OT_ERROR_NONE)
184 , mTransmitFrame(nullptr)
185 , mShortAddress(0)
186 , mPanId(0xffff)
187 , mRadioCaps(0)
188 , mChannel(0)
189 , mRxSensitivity(0)
190 , mState(kStateDisabled)
191 , mIsPromiscuous(false)
192 , mIsReady(false)
193 , mSupportsLogStream(false)
194 , mIsTimeSynced(false)
195 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
196 , mRcpFailureCount(0)
197 , mSrcMatchShortEntryCount(0)
198 , mSrcMatchExtEntryCount(0)
199 , mMacKeySet(false)
200 , mCcaEnergyDetectThresholdSet(false)
201 , mTransmitPowerSet(false)
202 , mCoexEnabledSet(false)
203 , mFemLnaGainSet(false)
204 , mRcpFailed(false)
205 , mEnergyScanning(false)
206 #endif
207 #if OPENTHREAD_CONFIG_DIAG_ENABLE
208 , mDiagMode(false)
209 , mDiagOutput(nullptr)
210 , mDiagOutputMaxLen(0)
211 #endif
212 , mTxRadioEndUs(UINT64_MAX)
213 , mRadioTimeRecalcStart(UINT64_MAX)
214 , mRadioTimeOffset(UINT64_MAX)
215 {
216 mVersion[0] = '\0';
217 memset(&mRadioSpinelMetrics, 0, sizeof(mRadioSpinelMetrics));
218 }
219
220 template <typename InterfaceType, typename ProcessContextType>
Init(bool aResetRadio,bool aRestoreDatasetFromNcp,bool aSkipRcpCompatibilityCheck)221 void RadioSpinel<InterfaceType, ProcessContextType>::Init(bool aResetRadio,
222 bool aRestoreDatasetFromNcp,
223 bool aSkipRcpCompatibilityCheck)
224 {
225 otError error = OT_ERROR_NONE;
226 bool supportsRcpApiVersion;
227 bool supportsRcpMinHostApiVersion;
228
229 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
230 mResetRadioOnStartup = aResetRadio;
231 #endif
232
233 ResetRcp(aResetRadio);
234 SuccessOrExit(error = CheckSpinelVersion());
235 SuccessOrExit(error = Get(SPINEL_PROP_NCP_VERSION, SPINEL_DATATYPE_UTF8_S, mVersion, sizeof(mVersion)));
236 SuccessOrExit(error = Get(SPINEL_PROP_HWADDR, SPINEL_DATATYPE_EUI64_S, mIeeeEui64.m8));
237
238 if (!IsRcp(supportsRcpApiVersion, supportsRcpMinHostApiVersion))
239 {
240 uint8_t exitCode = OT_EXIT_RADIO_SPINEL_INCOMPATIBLE;
241
242 if (aRestoreDatasetFromNcp)
243 {
244 #if !OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
245 exitCode = (RestoreDatasetFromNcp() == OT_ERROR_NONE) ? OT_EXIT_SUCCESS : OT_EXIT_FAILURE;
246 #endif
247 }
248
249 DieNow(exitCode);
250 }
251
252 if (!aSkipRcpCompatibilityCheck)
253 {
254 SuccessOrDie(CheckRcpApiVersion(supportsRcpApiVersion, supportsRcpMinHostApiVersion));
255 SuccessOrDie(CheckRadioCapabilities());
256 }
257
258 mRxRadioFrame.mPsdu = mRxPsdu;
259 mTxRadioFrame.mPsdu = mTxPsdu;
260 mAckRadioFrame.mPsdu = mAckPsdu;
261
262 exit:
263 SuccessOrDie(error);
264 }
265
266 template <typename InterfaceType, typename ProcessContextType>
ResetRcp(bool aResetRadio)267 void RadioSpinel<InterfaceType, ProcessContextType>::ResetRcp(bool aResetRadio)
268 {
269 bool hardwareReset;
270 bool resetDone = false;
271
272 mIsReady = false;
273 mWaitingKey = SPINEL_PROP_LAST_STATUS;
274
275 if (aResetRadio && (SendReset(SPINEL_RESET_STACK) == OT_ERROR_NONE) && (WaitResponse(false) == OT_ERROR_NONE))
276 {
277 otLogInfoPlat("Software reset RCP successfully");
278 ExitNow(resetDone = true);
279 }
280
281 hardwareReset = (mSpinelInterface.HardwareReset() == OT_ERROR_NONE);
282 SuccessOrExit(WaitResponse(false));
283
284 resetDone = true;
285
286 if (hardwareReset)
287 {
288 otLogInfoPlat("Hardware reset RCP successfully");
289 }
290 else
291 {
292 otLogInfoPlat("RCP self reset successfully");
293 }
294
295 exit:
296 if (!resetDone)
297 {
298 otLogCritPlat("Failed to reset RCP!");
299 DieNow(OT_EXIT_FAILURE);
300 }
301 }
302
303 template <typename InterfaceType, typename ProcessContextType>
CheckSpinelVersion(void)304 otError RadioSpinel<InterfaceType, ProcessContextType>::CheckSpinelVersion(void)
305 {
306 otError error = OT_ERROR_NONE;
307 unsigned int versionMajor;
308 unsigned int versionMinor;
309
310 SuccessOrExit(error =
311 Get(SPINEL_PROP_PROTOCOL_VERSION, (SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S),
312 &versionMajor, &versionMinor));
313
314 if ((versionMajor != SPINEL_PROTOCOL_VERSION_THREAD_MAJOR) ||
315 (versionMinor != SPINEL_PROTOCOL_VERSION_THREAD_MINOR))
316 {
317 otLogCritPlat("Spinel version mismatch - Posix:%d.%d, RCP:%d.%d", SPINEL_PROTOCOL_VERSION_THREAD_MAJOR,
318 SPINEL_PROTOCOL_VERSION_THREAD_MINOR, versionMajor, versionMinor);
319 DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
320 }
321
322 exit:
323 return error;
324 }
325
326 template <typename InterfaceType, typename ProcessContextType>
IsRcp(bool & aSupportsRcpApiVersion,bool & aSupportsRcpMinHostApiVersion)327 bool RadioSpinel<InterfaceType, ProcessContextType>::IsRcp(bool &aSupportsRcpApiVersion,
328 bool &aSupportsRcpMinHostApiVersion)
329 {
330 uint8_t capsBuffer[kCapsBufferSize];
331 const uint8_t *capsData = capsBuffer;
332 spinel_size_t capsLength = sizeof(capsBuffer);
333 bool supportsRawRadio = false;
334 bool isRcp = false;
335
336 aSupportsRcpApiVersion = false;
337 aSupportsRcpMinHostApiVersion = false;
338
339 SuccessOrDie(Get(SPINEL_PROP_CAPS, SPINEL_DATATYPE_DATA_S, capsBuffer, &capsLength));
340
341 while (capsLength > 0)
342 {
343 unsigned int capability;
344 spinel_ssize_t unpacked;
345
346 unpacked = spinel_datatype_unpack(capsData, capsLength, SPINEL_DATATYPE_UINT_PACKED_S, &capability);
347 VerifyOrDie(unpacked > 0, OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
348
349 if (capability == SPINEL_CAP_MAC_RAW)
350 {
351 supportsRawRadio = true;
352 }
353
354 if (capability == SPINEL_CAP_CONFIG_RADIO)
355 {
356 isRcp = true;
357 }
358
359 if (capability == SPINEL_CAP_OPENTHREAD_LOG_METADATA)
360 {
361 mSupportsLogStream = true;
362 }
363
364 if (capability == SPINEL_CAP_RCP_API_VERSION)
365 {
366 aSupportsRcpApiVersion = true;
367 }
368
369 if (capability == SPINEL_PROP_RCP_MIN_HOST_API_VERSION)
370 {
371 aSupportsRcpMinHostApiVersion = true;
372 }
373
374 capsData += unpacked;
375 capsLength -= static_cast<spinel_size_t>(unpacked);
376 }
377
378 if (!supportsRawRadio && isRcp)
379 {
380 otLogCritPlat("RCP capability list does not include support for radio/raw mode");
381 DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
382 }
383
384 return isRcp;
385 }
386
387 template <typename InterfaceType, typename ProcessContextType>
CheckRadioCapabilities(void)388 otError RadioSpinel<InterfaceType, ProcessContextType>::CheckRadioCapabilities(void)
389 {
390 const otRadioCaps kRequiredRadioCaps =
391 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
392 OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING |
393 #endif
394 OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_TRANSMIT_RETRIES | OT_RADIO_CAPS_CSMA_BACKOFF;
395
396 otError error = OT_ERROR_NONE;
397 unsigned int radioCaps;
398
399 SuccessOrExit(error = Get(SPINEL_PROP_RADIO_CAPS, SPINEL_DATATYPE_UINT_PACKED_S, &radioCaps));
400 mRadioCaps = static_cast<otRadioCaps>(radioCaps);
401
402 if ((mRadioCaps & kRequiredRadioCaps) != kRequiredRadioCaps)
403 {
404 otRadioCaps missingCaps = (mRadioCaps & kRequiredRadioCaps) ^ kRequiredRadioCaps;
405
406 // missingCaps may be an unused variable when otLogCritPlat is blank
407 // avoid compiler warning in that case
408 OT_UNUSED_VARIABLE(missingCaps);
409
410 otLogCritPlat("RCP is missing required capabilities: %s%s%s%s%s",
411 (missingCaps & OT_RADIO_CAPS_ACK_TIMEOUT) ? "ack-timeout " : "",
412 (missingCaps & OT_RADIO_CAPS_TRANSMIT_RETRIES) ? "tx-retries " : "",
413 (missingCaps & OT_RADIO_CAPS_CSMA_BACKOFF) ? "CSMA-backoff " : "",
414 (missingCaps & OT_RADIO_CAPS_TRANSMIT_SEC) ? "tx-security " : "",
415 (missingCaps & OT_RADIO_CAPS_TRANSMIT_TIMING) ? "tx-timing " : "");
416
417 DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
418 }
419
420 exit:
421 return error;
422 }
423
424 template <typename InterfaceType, typename ProcessContextType>
CheckRcpApiVersion(bool aSupportsRcpApiVersion,bool aSupportsRcpMinHostApiVersion)425 otError RadioSpinel<InterfaceType, ProcessContextType>::CheckRcpApiVersion(bool aSupportsRcpApiVersion,
426 bool aSupportsRcpMinHostApiVersion)
427 {
428 otError error = OT_ERROR_NONE;
429
430 static_assert(SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION <= SPINEL_RCP_API_VERSION,
431 "MIN_HOST_SUPPORTED_RCP_API_VERSION must be smaller than or equal to RCP_API_VERSION");
432
433 if (aSupportsRcpApiVersion)
434 {
435 // Make sure RCP is not too old and its version is within the
436 // range host supports.
437
438 unsigned int rcpApiVersion;
439
440 SuccessOrExit(error = Get(SPINEL_PROP_RCP_API_VERSION, SPINEL_DATATYPE_UINT_PACKED_S, &rcpApiVersion));
441
442 if (rcpApiVersion < SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION)
443 {
444 otLogCritPlat("RCP and host are using incompatible API versions");
445 otLogCritPlat("RCP API Version %u is older than min required by host %u", rcpApiVersion,
446 SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION);
447 DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
448 }
449 }
450
451 if (aSupportsRcpMinHostApiVersion)
452 {
453 // Check with RCP about min host API version it can work with,
454 // and make sure on host side our version is within the supported
455 // range.
456
457 unsigned int minHostRcpApiVersion;
458
459 SuccessOrExit(
460 error = Get(SPINEL_PROP_RCP_MIN_HOST_API_VERSION, SPINEL_DATATYPE_UINT_PACKED_S, &minHostRcpApiVersion));
461
462 if (SPINEL_RCP_API_VERSION < minHostRcpApiVersion)
463 {
464 otLogCritPlat("RCP and host are using incompatible API versions");
465 otLogCritPlat("RCP requires min host API version %u but host is older and at version %u",
466 minHostRcpApiVersion, SPINEL_RCP_API_VERSION);
467 DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
468 }
469 }
470
471 exit:
472 return error;
473 }
474
475 #if !OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
476 template <typename InterfaceType, typename ProcessContextType>
RestoreDatasetFromNcp(void)477 otError RadioSpinel<InterfaceType, ProcessContextType>::RestoreDatasetFromNcp(void)
478 {
479 otError error = OT_ERROR_NONE;
480
481 Instance::Get().template Get<SettingsDriver>().Init(nullptr, 0);
482
483 otLogInfoPlat("Trying to get saved dataset from NCP");
484 SuccessOrExit(
485 error = Get(SPINEL_PROP_THREAD_ACTIVE_DATASET, SPINEL_DATATYPE_VOID_S, &RadioSpinel::ThreadDatasetHandler));
486 SuccessOrExit(
487 error = Get(SPINEL_PROP_THREAD_PENDING_DATASET, SPINEL_DATATYPE_VOID_S, &RadioSpinel::ThreadDatasetHandler));
488
489 exit:
490 Instance::Get().template Get<SettingsDriver>().Deinit();
491 return error;
492 }
493 #endif
494
495 template <typename InterfaceType, typename ProcessContextType>
Deinit(void)496 void RadioSpinel<InterfaceType, ProcessContextType>::Deinit(void)
497 {
498 mSpinelInterface.Deinit();
499 // This allows implementing pseudo reset.
500 new (this) RadioSpinel();
501 }
502
503 template <typename InterfaceType, typename ProcessContextType>
HandleReceivedFrame(void)504 void RadioSpinel<InterfaceType, ProcessContextType>::HandleReceivedFrame(void)
505 {
506 otError error = OT_ERROR_NONE;
507 uint8_t header;
508 spinel_ssize_t unpacked;
509
510 LogSpinelFrame(mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetLength(), false);
511 unpacked = spinel_datatype_unpack(mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetLength(), "C", &header);
512
513 VerifyOrExit(unpacked > 0 && (header & SPINEL_HEADER_FLAG) == SPINEL_HEADER_FLAG &&
514 SPINEL_HEADER_GET_IID(header) == 0,
515 error = OT_ERROR_PARSE);
516
517 if (SPINEL_HEADER_GET_TID(header) == 0)
518 {
519 HandleNotification(mRxFrameBuffer);
520 }
521 else
522 {
523 HandleResponse(mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetLength());
524 mRxFrameBuffer.DiscardFrame();
525 }
526
527 exit:
528 if (error != OT_ERROR_NONE)
529 {
530 mRxFrameBuffer.DiscardFrame();
531 otLogWarnPlat("Error handling hdlc frame: %s", otThreadErrorToString(error));
532 }
533
534 UpdateParseErrorCount(error);
535 }
536
537 template <typename InterfaceType, typename ProcessContextType>
HandleNotification(SpinelInterface::RxFrameBuffer & aFrameBuffer)538 void RadioSpinel<InterfaceType, ProcessContextType>::HandleNotification(SpinelInterface::RxFrameBuffer &aFrameBuffer)
539 {
540 spinel_prop_key_t key;
541 spinel_size_t len = 0;
542 spinel_ssize_t unpacked;
543 uint8_t *data = nullptr;
544 uint32_t cmd;
545 uint8_t header;
546 otError error = OT_ERROR_NONE;
547 bool shouldSaveFrame = false;
548
549 unpacked = spinel_datatype_unpack(aFrameBuffer.GetFrame(), aFrameBuffer.GetLength(), "CiiD", &header, &cmd, &key,
550 &data, &len);
551 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
552 VerifyOrExit(SPINEL_HEADER_GET_TID(header) == 0, error = OT_ERROR_PARSE);
553
554 switch (cmd)
555 {
556 case SPINEL_CMD_PROP_VALUE_IS:
557 // Some spinel properties cannot be handled during `WaitResponse()`, we must cache these events.
558 // `mWaitingTid` is released immediately after received the response. And `mWaitingKey` is be set
559 // to `SPINEL_PROP_LAST_STATUS` at the end of `WaitResponse()`.
560
561 if (!IsSafeToHandleNow(key))
562 {
563 ExitNow(shouldSaveFrame = true);
564 }
565
566 HandleValueIs(key, data, static_cast<uint16_t>(len));
567 break;
568
569 case SPINEL_CMD_PROP_VALUE_INSERTED:
570 case SPINEL_CMD_PROP_VALUE_REMOVED:
571 otLogInfoPlat("Ignored command %lu", ToUlong(cmd));
572 break;
573
574 default:
575 ExitNow(error = OT_ERROR_PARSE);
576 }
577
578 exit:
579 if (shouldSaveFrame)
580 {
581 aFrameBuffer.SaveFrame();
582 }
583 else
584 {
585 aFrameBuffer.DiscardFrame();
586 }
587
588 UpdateParseErrorCount(error);
589 LogIfFail("Error processing notification", error);
590 }
591
592 template <typename InterfaceType, typename ProcessContextType>
HandleNotification(const uint8_t * aFrame,uint16_t aLength)593 void RadioSpinel<InterfaceType, ProcessContextType>::HandleNotification(const uint8_t *aFrame, uint16_t aLength)
594 {
595 spinel_prop_key_t key;
596 spinel_size_t len = 0;
597 spinel_ssize_t unpacked;
598 uint8_t *data = nullptr;
599 uint32_t cmd;
600 uint8_t header;
601 otError error = OT_ERROR_NONE;
602
603 unpacked = spinel_datatype_unpack(aFrame, aLength, "CiiD", &header, &cmd, &key, &data, &len);
604 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
605 VerifyOrExit(SPINEL_HEADER_GET_TID(header) == 0, error = OT_ERROR_PARSE);
606 VerifyOrExit(cmd == SPINEL_CMD_PROP_VALUE_IS);
607 HandleValueIs(key, data, static_cast<uint16_t>(len));
608
609 exit:
610 UpdateParseErrorCount(error);
611 LogIfFail("Error processing saved notification", error);
612 }
613
614 template <typename InterfaceType, typename ProcessContextType>
HandleResponse(const uint8_t * aBuffer,uint16_t aLength)615 void RadioSpinel<InterfaceType, ProcessContextType>::HandleResponse(const uint8_t *aBuffer, uint16_t aLength)
616 {
617 spinel_prop_key_t key;
618 uint8_t *data = nullptr;
619 spinel_size_t len = 0;
620 uint8_t header = 0;
621 uint32_t cmd = 0;
622 spinel_ssize_t rval = 0;
623 otError error = OT_ERROR_NONE;
624
625 rval = spinel_datatype_unpack(aBuffer, aLength, "CiiD", &header, &cmd, &key, &data, &len);
626 VerifyOrExit(rval > 0 && cmd >= SPINEL_CMD_PROP_VALUE_IS && cmd <= SPINEL_CMD_PROP_VALUE_REMOVED,
627 error = OT_ERROR_PARSE);
628
629 if (mWaitingTid == SPINEL_HEADER_GET_TID(header))
630 {
631 HandleWaitingResponse(cmd, key, data, static_cast<uint16_t>(len));
632 FreeTid(mWaitingTid);
633 mWaitingTid = 0;
634 }
635 else if (mTxRadioTid == SPINEL_HEADER_GET_TID(header))
636 {
637 if (mState == kStateTransmitting)
638 {
639 HandleTransmitDone(cmd, key, data, static_cast<uint16_t>(len));
640 }
641
642 FreeTid(mTxRadioTid);
643 mTxRadioTid = 0;
644 }
645 else
646 {
647 otLogWarnPlat("Unexpected Spinel transaction message: %u", SPINEL_HEADER_GET_TID(header));
648 error = OT_ERROR_DROP;
649 }
650
651 exit:
652 UpdateParseErrorCount(error);
653 LogIfFail("Error processing response", error);
654 }
655
656 #if !OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
657 template <typename InterfaceType, typename ProcessContextType>
ThreadDatasetHandler(const uint8_t * aBuffer,uint16_t aLength)658 otError RadioSpinel<InterfaceType, ProcessContextType>::ThreadDatasetHandler(const uint8_t *aBuffer, uint16_t aLength)
659 {
660 otError error = OT_ERROR_NONE;
661 otOperationalDataset opDataset;
662 bool isActive = ((mWaitingKey == SPINEL_PROP_THREAD_ACTIVE_DATASET) ? true : false);
663 Spinel::Decoder decoder;
664 MeshCoP::Dataset dataset;
665
666 memset(&opDataset, 0, sizeof(otOperationalDataset));
667 decoder.Init(aBuffer, aLength);
668
669 while (!decoder.IsAllReadInStruct())
670 {
671 unsigned int propKey;
672
673 SuccessOrExit(error = decoder.OpenStruct());
674 SuccessOrExit(error = decoder.ReadUintPacked(propKey));
675
676 switch (static_cast<spinel_prop_key_t>(propKey))
677 {
678 case SPINEL_PROP_NET_NETWORK_KEY:
679 {
680 const uint8_t *key;
681 uint16_t len;
682
683 SuccessOrExit(error = decoder.ReadData(key, len));
684 VerifyOrExit(len == OT_NETWORK_KEY_SIZE, error = OT_ERROR_INVALID_ARGS);
685 memcpy(opDataset.mNetworkKey.m8, key, len);
686 opDataset.mComponents.mIsNetworkKeyPresent = true;
687 break;
688 }
689
690 case SPINEL_PROP_NET_NETWORK_NAME:
691 {
692 const char *name;
693 size_t len;
694
695 SuccessOrExit(error = decoder.ReadUtf8(name));
696 len = StringLength(name, OT_NETWORK_NAME_MAX_SIZE);
697 memcpy(opDataset.mNetworkName.m8, name, len);
698 opDataset.mNetworkName.m8[len] = '\0';
699 opDataset.mComponents.mIsNetworkNamePresent = true;
700 break;
701 }
702
703 case SPINEL_PROP_NET_XPANID:
704 {
705 const uint8_t *xpanid;
706 uint16_t len;
707
708 SuccessOrExit(error = decoder.ReadData(xpanid, len));
709 VerifyOrExit(len == OT_EXT_PAN_ID_SIZE, error = OT_ERROR_INVALID_ARGS);
710 memcpy(opDataset.mExtendedPanId.m8, xpanid, len);
711 opDataset.mComponents.mIsExtendedPanIdPresent = true;
712 break;
713 }
714
715 case SPINEL_PROP_IPV6_ML_PREFIX:
716 {
717 const otIp6Address *addr;
718 uint8_t prefixLen;
719
720 SuccessOrExit(error = decoder.ReadIp6Address(addr));
721 SuccessOrExit(error = decoder.ReadUint8(prefixLen));
722 VerifyOrExit(prefixLen == OT_IP6_PREFIX_BITSIZE, error = OT_ERROR_INVALID_ARGS);
723 memcpy(opDataset.mMeshLocalPrefix.m8, addr, OT_MESH_LOCAL_PREFIX_SIZE);
724 opDataset.mComponents.mIsMeshLocalPrefixPresent = true;
725 break;
726 }
727
728 case SPINEL_PROP_DATASET_DELAY_TIMER:
729 {
730 SuccessOrExit(error = decoder.ReadUint32(opDataset.mDelay));
731 opDataset.mComponents.mIsDelayPresent = true;
732 break;
733 }
734
735 case SPINEL_PROP_MAC_15_4_PANID:
736 {
737 SuccessOrExit(error = decoder.ReadUint16(opDataset.mPanId));
738 opDataset.mComponents.mIsPanIdPresent = true;
739 break;
740 }
741
742 case SPINEL_PROP_PHY_CHAN:
743 {
744 uint8_t channel;
745
746 SuccessOrExit(error = decoder.ReadUint8(channel));
747 opDataset.mChannel = channel;
748 opDataset.mComponents.mIsChannelPresent = true;
749 break;
750 }
751
752 case SPINEL_PROP_NET_PSKC:
753 {
754 const uint8_t *psk;
755 uint16_t len;
756
757 SuccessOrExit(error = decoder.ReadData(psk, len));
758 VerifyOrExit(len == OT_PSKC_MAX_SIZE, error = OT_ERROR_INVALID_ARGS);
759 memcpy(opDataset.mPskc.m8, psk, OT_PSKC_MAX_SIZE);
760 opDataset.mComponents.mIsPskcPresent = true;
761 break;
762 }
763
764 case SPINEL_PROP_DATASET_SECURITY_POLICY:
765 {
766 uint8_t flags[2];
767 uint8_t flagsLength = 1;
768
769 SuccessOrExit(error = decoder.ReadUint16(opDataset.mSecurityPolicy.mRotationTime));
770 SuccessOrExit(error = decoder.ReadUint8(flags[0]));
771 if (otThreadGetVersion() >= OT_THREAD_VERSION_1_2 && decoder.GetRemainingLengthInStruct() > 0)
772 {
773 SuccessOrExit(error = decoder.ReadUint8(flags[1]));
774 ++flagsLength;
775 }
776 static_cast<SecurityPolicy &>(opDataset.mSecurityPolicy).SetFlags(flags, flagsLength);
777 opDataset.mComponents.mIsSecurityPolicyPresent = true;
778 break;
779 }
780
781 case SPINEL_PROP_PHY_CHAN_SUPPORTED:
782 {
783 uint8_t channel;
784
785 opDataset.mChannelMask = 0;
786
787 while (!decoder.IsAllReadInStruct())
788 {
789 SuccessOrExit(error = decoder.ReadUint8(channel));
790 VerifyOrExit(channel <= 31, error = OT_ERROR_INVALID_ARGS);
791 opDataset.mChannelMask |= (1UL << channel);
792 }
793 opDataset.mComponents.mIsChannelMaskPresent = true;
794 break;
795 }
796
797 default:
798 break;
799 }
800
801 SuccessOrExit(error = decoder.CloseStruct());
802 }
803
804 /*
805 * Initially set Active Timestamp to 0. This is to allow the node to join the network
806 * yet retrieve the full Active Dataset from a neighboring device if one exists.
807 */
808 memset(&opDataset.mActiveTimestamp, 0, sizeof(opDataset.mActiveTimestamp));
809 opDataset.mComponents.mIsActiveTimestampPresent = true;
810
811 SuccessOrExit(error = dataset.SetFrom(static_cast<MeshCoP::Dataset::Info &>(opDataset)));
812 SuccessOrExit(error = Instance::Get().template Get<SettingsDriver>().Set(
813 isActive ? SettingsBase::kKeyActiveDataset : SettingsBase::kKeyPendingDataset, dataset.GetBytes(),
814 dataset.GetSize()));
815
816 exit:
817 return error;
818 }
819 #endif // #if !OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
820
821 template <typename InterfaceType, typename ProcessContextType>
HandleWaitingResponse(uint32_t aCommand,spinel_prop_key_t aKey,const uint8_t * aBuffer,uint16_t aLength)822 void RadioSpinel<InterfaceType, ProcessContextType>::HandleWaitingResponse(uint32_t aCommand,
823 spinel_prop_key_t aKey,
824 const uint8_t *aBuffer,
825 uint16_t aLength)
826 {
827 if (aKey == SPINEL_PROP_LAST_STATUS)
828 {
829 spinel_status_t status;
830 spinel_ssize_t unpacked = spinel_datatype_unpack(aBuffer, aLength, "i", &status);
831
832 VerifyOrExit(unpacked > 0, mError = OT_ERROR_PARSE);
833 mError = SpinelStatusToOtError(status);
834 }
835 #if OPENTHREAD_CONFIG_DIAG_ENABLE
836 else if (aKey == SPINEL_PROP_NEST_STREAM_MFG)
837 {
838 spinel_ssize_t unpacked;
839
840 mError = OT_ERROR_NONE;
841 VerifyOrExit(mDiagOutput != nullptr);
842 unpacked =
843 spinel_datatype_unpack_in_place(aBuffer, aLength, SPINEL_DATATYPE_UTF8_S, mDiagOutput, &mDiagOutputMaxLen);
844 VerifyOrExit(unpacked > 0, mError = OT_ERROR_PARSE);
845 }
846 #endif
847 else if (aKey == mWaitingKey)
848 {
849 if (mPropertyFormat)
850 {
851 if (static_cast<spinel_datatype_t>(mPropertyFormat[0]) == SPINEL_DATATYPE_VOID_C)
852 {
853 // reserved SPINEL_DATATYPE_VOID_C indicate caller want to parse the spinel response itself
854 ResponseHandler handler = va_arg(mPropertyArgs, ResponseHandler);
855
856 assert(handler != nullptr);
857 mError = (this->*handler)(aBuffer, aLength);
858 }
859 else
860 {
861 spinel_ssize_t unpacked =
862 spinel_datatype_vunpack_in_place(aBuffer, aLength, mPropertyFormat, mPropertyArgs);
863
864 VerifyOrExit(unpacked > 0, mError = OT_ERROR_PARSE);
865 mError = OT_ERROR_NONE;
866 }
867 }
868 else
869 {
870 if (aCommand == mExpectedCommand)
871 {
872 mError = OT_ERROR_NONE;
873 }
874 else
875 {
876 mError = OT_ERROR_DROP;
877 }
878 }
879 }
880 else
881 {
882 mError = OT_ERROR_DROP;
883 }
884
885 exit:
886 UpdateParseErrorCount(mError);
887 LogIfFail("Error processing result", mError);
888 }
889
890 template <typename InterfaceType, typename ProcessContextType>
HandleValueIs(spinel_prop_key_t aKey,const uint8_t * aBuffer,uint16_t aLength)891 void RadioSpinel<InterfaceType, ProcessContextType>::HandleValueIs(spinel_prop_key_t aKey,
892 const uint8_t *aBuffer,
893 uint16_t aLength)
894 {
895 otError error = OT_ERROR_NONE;
896 spinel_ssize_t unpacked;
897
898 if (aKey == SPINEL_PROP_STREAM_RAW)
899 {
900 SuccessOrExit(error = ParseRadioFrame(mRxRadioFrame, aBuffer, aLength, unpacked));
901 RadioReceive();
902 }
903 else if (aKey == SPINEL_PROP_LAST_STATUS)
904 {
905 spinel_status_t status = SPINEL_STATUS_OK;
906
907 unpacked = spinel_datatype_unpack(aBuffer, aLength, "i", &status);
908 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
909
910 if (status >= SPINEL_STATUS_RESET__BEGIN && status <= SPINEL_STATUS_RESET__END)
911 {
912 if (IsEnabled())
913 {
914 HandleRcpUnexpectedReset(status);
915 ExitNow();
916 }
917
918 otLogInfoPlat("RCP reset: %s", spinel_status_to_cstr(status));
919 mIsReady = true;
920 }
921 else
922 {
923 otLogInfoPlat("RCP last status: %s", spinel_status_to_cstr(status));
924 }
925 }
926 else if (aKey == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT)
927 {
928 uint8_t scanChannel;
929 int8_t maxRssi;
930
931 unpacked = spinel_datatype_unpack(aBuffer, aLength, "Cc", &scanChannel, &maxRssi);
932
933 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
934
935 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
936 mEnergyScanning = false;
937 #endif
938
939 otPlatRadioEnergyScanDone(mInstance, maxRssi);
940 }
941 else if (aKey == SPINEL_PROP_STREAM_DEBUG)
942 {
943 char logStream[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE + 1];
944 unsigned int len = sizeof(logStream);
945
946 unpacked = spinel_datatype_unpack_in_place(aBuffer, aLength, SPINEL_DATATYPE_DATA_S, logStream, &len);
947 assert(len < sizeof(logStream));
948 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
949 logStream[len] = '\0';
950 otLogDebgPlat("RCP => %s", logStream);
951 }
952 else if ((aKey == SPINEL_PROP_STREAM_LOG) && mSupportsLogStream)
953 {
954 const char *logString;
955 uint8_t logLevel;
956
957 unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UTF8_S, &logString);
958 VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
959 aBuffer += unpacked;
960 aLength -= unpacked;
961
962 unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UINT8_S, &logLevel);
963 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
964
965 switch (logLevel)
966 {
967 case SPINEL_NCP_LOG_LEVEL_EMERG:
968 case SPINEL_NCP_LOG_LEVEL_ALERT:
969 case SPINEL_NCP_LOG_LEVEL_CRIT:
970 otLogCritPlat("RCP => %s", logString);
971 break;
972
973 case SPINEL_NCP_LOG_LEVEL_ERR:
974 case SPINEL_NCP_LOG_LEVEL_WARN:
975 otLogWarnPlat("RCP => %s", logString);
976 break;
977
978 case SPINEL_NCP_LOG_LEVEL_NOTICE:
979 otLogNotePlat("RCP => %s", logString);
980 break;
981
982 case SPINEL_NCP_LOG_LEVEL_INFO:
983 otLogInfoPlat("RCP => %s", logString);
984 break;
985
986 case SPINEL_NCP_LOG_LEVEL_DEBUG:
987 default:
988 otLogDebgPlat("RCP => %s", logString);
989 break;
990 }
991 }
992
993 exit:
994 UpdateParseErrorCount(error);
995 LogIfFail("Failed to handle ValueIs", error);
996 }
997
998 template <typename InterfaceType, typename ProcessContextType>
ParseRadioFrame(otRadioFrame & aFrame,const uint8_t * aBuffer,uint16_t aLength,spinel_ssize_t & aUnpacked)999 otError RadioSpinel<InterfaceType, ProcessContextType>::ParseRadioFrame(otRadioFrame &aFrame,
1000 const uint8_t *aBuffer,
1001 uint16_t aLength,
1002 spinel_ssize_t &aUnpacked)
1003 {
1004 otError error = OT_ERROR_NONE;
1005 uint16_t flags = 0;
1006 int8_t noiseFloor = -128;
1007 spinel_size_t size = OT_RADIO_FRAME_MAX_SIZE;
1008 unsigned int receiveError = 0;
1009 spinel_ssize_t unpacked;
1010
1011 VerifyOrExit(aLength > 0, aFrame.mLength = 0);
1012
1013 unpacked = spinel_datatype_unpack_in_place(aBuffer, aLength,
1014 SPINEL_DATATYPE_DATA_WLEN_S // Frame
1015 SPINEL_DATATYPE_INT8_S // RSSI
1016 SPINEL_DATATYPE_INT8_S // Noise Floor
1017 SPINEL_DATATYPE_UINT16_S // Flags
1018 SPINEL_DATATYPE_STRUCT_S( // PHY-data
1019 SPINEL_DATATYPE_UINT8_S // 802.15.4 channel
1020 SPINEL_DATATYPE_UINT8_S // 802.15.4 LQI
1021 SPINEL_DATATYPE_UINT64_S // Timestamp (us).
1022 ) SPINEL_DATATYPE_STRUCT_S( // Vendor-data
1023 SPINEL_DATATYPE_UINT_PACKED_S // Receive error
1024 ),
1025 aFrame.mPsdu, &size, &aFrame.mInfo.mRxInfo.mRssi, &noiseFloor, &flags,
1026 &aFrame.mChannel, &aFrame.mInfo.mRxInfo.mLqi,
1027 &aFrame.mInfo.mRxInfo.mTimestamp, &receiveError);
1028
1029 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
1030 aUnpacked = unpacked;
1031
1032 aBuffer += unpacked;
1033 aLength -= static_cast<uint16_t>(unpacked);
1034
1035 if (mRadioCaps & OT_RADIO_CAPS_TRANSMIT_SEC)
1036 {
1037 unpacked =
1038 spinel_datatype_unpack_in_place(aBuffer, aLength,
1039 SPINEL_DATATYPE_STRUCT_S( // MAC-data
1040 SPINEL_DATATYPE_UINT8_S // Security key index
1041 SPINEL_DATATYPE_UINT32_S // Security frame counter
1042 ),
1043 &aFrame.mInfo.mRxInfo.mAckKeyId, &aFrame.mInfo.mRxInfo.mAckFrameCounter);
1044
1045 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
1046 aUnpacked += unpacked;
1047 }
1048
1049 if (receiveError == OT_ERROR_NONE)
1050 {
1051 aFrame.mLength = static_cast<uint8_t>(size);
1052
1053 aFrame.mInfo.mRxInfo.mAckedWithFramePending = ((flags & SPINEL_MD_FLAG_ACKED_FP) != 0);
1054 aFrame.mInfo.mRxInfo.mAckedWithSecEnhAck = ((flags & SPINEL_MD_FLAG_ACKED_SEC) != 0);
1055 }
1056 else if (receiveError < OT_NUM_ERRORS)
1057 {
1058 error = static_cast<otError>(receiveError);
1059 }
1060 else
1061 {
1062 error = OT_ERROR_PARSE;
1063 }
1064
1065 exit:
1066 UpdateParseErrorCount(error);
1067 LogIfFail("Handle radio frame failed", error);
1068 return error;
1069 }
1070
1071 template <typename InterfaceType, typename ProcessContextType>
ProcessFrameQueue(void)1072 void RadioSpinel<InterfaceType, ProcessContextType>::ProcessFrameQueue(void)
1073 {
1074 uint8_t *frame = nullptr;
1075 uint16_t length;
1076
1077 while (mRxFrameBuffer.GetNextSavedFrame(frame, length) == OT_ERROR_NONE)
1078 {
1079 HandleNotification(frame, length);
1080 }
1081
1082 mRxFrameBuffer.ClearSavedFrames();
1083 }
1084
1085 template <typename InterfaceType, typename ProcessContextType>
RadioReceive(void)1086 void RadioSpinel<InterfaceType, ProcessContextType>::RadioReceive(void)
1087 {
1088 if (!mIsPromiscuous)
1089 {
1090 switch (mState)
1091 {
1092 case kStateDisabled:
1093 case kStateSleep:
1094 ExitNow();
1095
1096 case kStateReceive:
1097 case kStateTransmitting:
1098 case kStateTransmitDone:
1099 break;
1100 }
1101 }
1102
1103 #if OPENTHREAD_CONFIG_DIAG_ENABLE
1104 if (otPlatDiagModeGet())
1105 {
1106 otPlatDiagRadioReceiveDone(mInstance, &mRxRadioFrame, OT_ERROR_NONE);
1107 }
1108 else
1109 #endif
1110 {
1111 otPlatRadioReceiveDone(mInstance, &mRxRadioFrame, OT_ERROR_NONE);
1112 }
1113
1114 exit:
1115 return;
1116 }
1117
1118 template <typename InterfaceType, typename ProcessContextType>
TransmitDone(otRadioFrame * aFrame,otRadioFrame * aAckFrame,otError aError)1119 void RadioSpinel<InterfaceType, ProcessContextType>::TransmitDone(otRadioFrame *aFrame,
1120 otRadioFrame *aAckFrame,
1121 otError aError)
1122 {
1123 #if OPENTHREAD_CONFIG_DIAG_ENABLE
1124 if (otPlatDiagModeGet())
1125 {
1126 otPlatDiagRadioTransmitDone(mInstance, aFrame, aError);
1127 }
1128 else
1129 #endif
1130 {
1131 otPlatRadioTxDone(mInstance, aFrame, aAckFrame, aError);
1132 }
1133 }
1134
1135 template <typename InterfaceType, typename ProcessContextType>
ProcessRadioStateMachine(void)1136 void RadioSpinel<InterfaceType, ProcessContextType>::ProcessRadioStateMachine(void)
1137 {
1138 if (mState == kStateTransmitDone)
1139 {
1140 mState = kStateReceive;
1141 mTxRadioEndUs = UINT64_MAX;
1142
1143 TransmitDone(mTransmitFrame, (mAckRadioFrame.mLength != 0) ? &mAckRadioFrame : nullptr, mTxError);
1144 }
1145 else if (mState == kStateTransmitting && otPlatTimeGet() >= mTxRadioEndUs)
1146 {
1147 // Frame has been successfully passed to radio, but no `TransmitDone` event received within TX_WAIT_US.
1148 otLogWarnPlat("radio tx timeout");
1149 HandleRcpTimeout();
1150 }
1151 }
1152
1153 template <typename InterfaceType, typename ProcessContextType>
Process(const ProcessContextType & aContext)1154 void RadioSpinel<InterfaceType, ProcessContextType>::Process(const ProcessContextType &aContext)
1155 {
1156 if (mRxFrameBuffer.HasSavedFrame())
1157 {
1158 ProcessFrameQueue();
1159 RecoverFromRcpFailure();
1160 }
1161
1162 GetSpinelInterface().Process(aContext);
1163 RecoverFromRcpFailure();
1164
1165 if (mRxFrameBuffer.HasSavedFrame())
1166 {
1167 ProcessFrameQueue();
1168 RecoverFromRcpFailure();
1169 }
1170
1171 ProcessRadioStateMachine();
1172 RecoverFromRcpFailure();
1173 CalcRcpTimeOffset();
1174 }
1175
1176 template <typename InterfaceType, typename ProcessContextType>
SetPromiscuous(bool aEnable)1177 otError RadioSpinel<InterfaceType, ProcessContextType>::SetPromiscuous(bool aEnable)
1178 {
1179 otError error;
1180
1181 uint8_t mode = (aEnable ? SPINEL_MAC_PROMISCUOUS_MODE_NETWORK : SPINEL_MAC_PROMISCUOUS_MODE_OFF);
1182 SuccessOrExit(error = Set(SPINEL_PROP_MAC_PROMISCUOUS_MODE, SPINEL_DATATYPE_UINT8_S, mode));
1183 mIsPromiscuous = aEnable;
1184
1185 exit:
1186 return error;
1187 }
1188
1189 template <typename InterfaceType, typename ProcessContextType>
SetShortAddress(uint16_t aAddress)1190 otError RadioSpinel<InterfaceType, ProcessContextType>::SetShortAddress(uint16_t aAddress)
1191 {
1192 otError error = OT_ERROR_NONE;
1193
1194 VerifyOrExit(mShortAddress != aAddress);
1195 SuccessOrExit(error = Set(SPINEL_PROP_MAC_15_4_SADDR, SPINEL_DATATYPE_UINT16_S, aAddress));
1196 mShortAddress = aAddress;
1197
1198 exit:
1199 return error;
1200 }
1201
1202 template <typename InterfaceType, typename ProcessContextType>
SetMacKey(uint8_t aKeyIdMode,uint8_t aKeyId,const otMacKeyMaterial * aPrevKey,const otMacKeyMaterial * aCurrKey,const otMacKeyMaterial * aNextKey)1203 otError RadioSpinel<InterfaceType, ProcessContextType>::SetMacKey(uint8_t aKeyIdMode,
1204 uint8_t aKeyId,
1205 const otMacKeyMaterial *aPrevKey,
1206 const otMacKeyMaterial *aCurrKey,
1207 const otMacKeyMaterial *aNextKey)
1208 {
1209 otError error;
1210 size_t aKeySize;
1211
1212 VerifyOrExit((aPrevKey != nullptr) && (aCurrKey != nullptr) && (aNextKey != nullptr), error = kErrorInvalidArgs);
1213
1214 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
1215 SuccessOrExit(error = otPlatCryptoExportKey(aPrevKey->mKeyMaterial.mKeyRef, aPrevKey->mKeyMaterial.mKey.m8,
1216 sizeof(aPrevKey->mKeyMaterial.mKey.m8), &aKeySize));
1217 SuccessOrExit(error = otPlatCryptoExportKey(aCurrKey->mKeyMaterial.mKeyRef, aCurrKey->mKeyMaterial.mKey.m8,
1218 sizeof(aCurrKey->mKeyMaterial.mKey.m8), &aKeySize));
1219 SuccessOrExit(error = otPlatCryptoExportKey(aNextKey->mKeyMaterial.mKeyRef, aNextKey->mKeyMaterial.mKey.m8,
1220 sizeof(aNextKey->mKeyMaterial.mKey.m8), &aKeySize));
1221 #else
1222 OT_UNUSED_VARIABLE(aKeySize);
1223 #endif
1224
1225 SuccessOrExit(error = Set(SPINEL_PROP_RCP_MAC_KEY,
1226 SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S
1227 SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_WLEN_S,
1228 aKeyIdMode, aKeyId, aPrevKey->mKeyMaterial.mKey.m8, sizeof(otMacKey),
1229 aCurrKey->mKeyMaterial.mKey.m8, sizeof(otMacKey), aNextKey->mKeyMaterial.mKey.m8,
1230 sizeof(otMacKey)));
1231
1232 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1233 mKeyIdMode = aKeyIdMode;
1234 mKeyId = aKeyId;
1235 memcpy(mPrevKey.m8, aPrevKey->mKeyMaterial.mKey.m8, OT_MAC_KEY_SIZE);
1236 memcpy(mCurrKey.m8, aCurrKey->mKeyMaterial.mKey.m8, OT_MAC_KEY_SIZE);
1237 memcpy(mNextKey.m8, aNextKey->mKeyMaterial.mKey.m8, OT_MAC_KEY_SIZE);
1238 mMacKeySet = true;
1239 #endif
1240
1241 exit:
1242 return error;
1243 }
1244
1245 template <typename InterfaceType, typename ProcessContextType>
SetMacFrameCounter(uint32_t aMacFrameCounter,bool aSetIfLarger)1246 otError RadioSpinel<InterfaceType, ProcessContextType>::SetMacFrameCounter(uint32_t aMacFrameCounter, bool aSetIfLarger)
1247 {
1248 otError error;
1249
1250 SuccessOrExit(error = Set(SPINEL_PROP_RCP_MAC_FRAME_COUNTER, SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_BOOL_S,
1251 aMacFrameCounter, aSetIfLarger));
1252
1253 exit:
1254 return error;
1255 }
1256
1257 template <typename InterfaceType, typename ProcessContextType>
GetIeeeEui64(uint8_t * aIeeeEui64)1258 otError RadioSpinel<InterfaceType, ProcessContextType>::GetIeeeEui64(uint8_t *aIeeeEui64)
1259 {
1260 memcpy(aIeeeEui64, mIeeeEui64.m8, sizeof(mIeeeEui64.m8));
1261
1262 return OT_ERROR_NONE;
1263 }
1264
1265 template <typename InterfaceType, typename ProcessContextType>
SetExtendedAddress(const otExtAddress & aExtAddress)1266 otError RadioSpinel<InterfaceType, ProcessContextType>::SetExtendedAddress(const otExtAddress &aExtAddress)
1267 {
1268 otError error;
1269
1270 SuccessOrExit(error = Set(SPINEL_PROP_MAC_15_4_LADDR, SPINEL_DATATYPE_EUI64_S, aExtAddress.m8));
1271 mExtendedAddress = aExtAddress;
1272
1273 exit:
1274 return error;
1275 }
1276
1277 template <typename InterfaceType, typename ProcessContextType>
SetPanId(uint16_t aPanId)1278 otError RadioSpinel<InterfaceType, ProcessContextType>::SetPanId(uint16_t aPanId)
1279 {
1280 otError error = OT_ERROR_NONE;
1281
1282 VerifyOrExit(mPanId != aPanId);
1283 SuccessOrExit(error = Set(SPINEL_PROP_MAC_15_4_PANID, SPINEL_DATATYPE_UINT16_S, aPanId));
1284 mPanId = aPanId;
1285
1286 exit:
1287 return error;
1288 }
1289
1290 template <typename InterfaceType, typename ProcessContextType>
EnableSrcMatch(bool aEnable)1291 otError RadioSpinel<InterfaceType, ProcessContextType>::EnableSrcMatch(bool aEnable)
1292 {
1293 return Set(SPINEL_PROP_MAC_SRC_MATCH_ENABLED, SPINEL_DATATYPE_BOOL_S, aEnable);
1294 }
1295
1296 template <typename InterfaceType, typename ProcessContextType>
AddSrcMatchShortEntry(uint16_t aShortAddress)1297 otError RadioSpinel<InterfaceType, ProcessContextType>::AddSrcMatchShortEntry(uint16_t aShortAddress)
1298 {
1299 otError error;
1300
1301 SuccessOrExit(error = Insert(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, aShortAddress));
1302
1303 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1304 assert(mSrcMatchShortEntryCount < OPENTHREAD_CONFIG_MLE_MAX_CHILDREN);
1305
1306 for (int i = 0; i < mSrcMatchShortEntryCount; ++i)
1307 {
1308 if (mSrcMatchShortEntries[i] == aShortAddress)
1309 {
1310 ExitNow();
1311 }
1312 }
1313 mSrcMatchShortEntries[mSrcMatchShortEntryCount] = aShortAddress;
1314 ++mSrcMatchShortEntryCount;
1315 #endif
1316
1317 exit:
1318 return error;
1319 }
1320
1321 template <typename InterfaceType, typename ProcessContextType>
AddSrcMatchExtEntry(const otExtAddress & aExtAddress)1322 otError RadioSpinel<InterfaceType, ProcessContextType>::AddSrcMatchExtEntry(const otExtAddress &aExtAddress)
1323 {
1324 otError error;
1325
1326 SuccessOrExit(error =
1327 Insert(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 assert(mSrcMatchExtEntryCount < OPENTHREAD_CONFIG_MLE_MAX_CHILDREN);
1331
1332 for (int i = 0; i < mSrcMatchExtEntryCount; ++i)
1333 {
1334 if (memcmp(aExtAddress.m8, mSrcMatchExtEntries[i].m8, OT_EXT_ADDRESS_SIZE) == 0)
1335 {
1336 ExitNow();
1337 }
1338 }
1339 mSrcMatchExtEntries[mSrcMatchExtEntryCount] = aExtAddress;
1340 ++mSrcMatchExtEntryCount;
1341 #endif
1342
1343 exit:
1344 return error;
1345 }
1346
1347 template <typename InterfaceType, typename ProcessContextType>
ClearSrcMatchShortEntry(uint16_t aShortAddress)1348 otError RadioSpinel<InterfaceType, ProcessContextType>::ClearSrcMatchShortEntry(uint16_t aShortAddress)
1349 {
1350 otError error;
1351
1352 SuccessOrExit(error = Remove(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, aShortAddress));
1353
1354 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1355 for (int i = 0; i < mSrcMatchShortEntryCount; ++i)
1356 {
1357 if (mSrcMatchShortEntries[i] == aShortAddress)
1358 {
1359 mSrcMatchShortEntries[i] = mSrcMatchShortEntries[mSrcMatchShortEntryCount - 1];
1360 --mSrcMatchShortEntryCount;
1361 break;
1362 }
1363 }
1364 #endif
1365
1366 exit:
1367 return error;
1368 }
1369
1370 template <typename InterfaceType, typename ProcessContextType>
ClearSrcMatchExtEntry(const otExtAddress & aExtAddress)1371 otError RadioSpinel<InterfaceType, ProcessContextType>::ClearSrcMatchExtEntry(const otExtAddress &aExtAddress)
1372 {
1373 otError error;
1374
1375 SuccessOrExit(error =
1376 Remove(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, aExtAddress.m8));
1377
1378 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1379 for (int i = 0; i < mSrcMatchExtEntryCount; ++i)
1380 {
1381 if (memcmp(mSrcMatchExtEntries[i].m8, aExtAddress.m8, OT_EXT_ADDRESS_SIZE) == 0)
1382 {
1383 mSrcMatchExtEntries[i] = mSrcMatchExtEntries[mSrcMatchExtEntryCount - 1];
1384 --mSrcMatchExtEntryCount;
1385 break;
1386 }
1387 }
1388 #endif
1389
1390 exit:
1391 return error;
1392 }
1393
1394 template <typename InterfaceType, typename ProcessContextType>
ClearSrcMatchShortEntries(void)1395 otError RadioSpinel<InterfaceType, ProcessContextType>::ClearSrcMatchShortEntries(void)
1396 {
1397 otError error;
1398
1399 SuccessOrExit(error = Set(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, nullptr));
1400
1401 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1402 mSrcMatchShortEntryCount = 0;
1403 #endif
1404
1405 exit:
1406 return error;
1407 }
1408
1409 template <typename InterfaceType, typename ProcessContextType>
ClearSrcMatchExtEntries(void)1410 otError RadioSpinel<InterfaceType, ProcessContextType>::ClearSrcMatchExtEntries(void)
1411 {
1412 otError error;
1413
1414 SuccessOrExit(error = Set(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, nullptr));
1415
1416 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1417 mSrcMatchExtEntryCount = 0;
1418 #endif
1419
1420 exit:
1421 return error;
1422 }
1423
1424 template <typename InterfaceType, typename ProcessContextType>
GetTransmitPower(int8_t & aPower)1425 otError RadioSpinel<InterfaceType, ProcessContextType>::GetTransmitPower(int8_t &aPower)
1426 {
1427 otError error = Get(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, &aPower);
1428
1429 LogIfFail("Get transmit power failed", error);
1430 return error;
1431 }
1432
1433 template <typename InterfaceType, typename ProcessContextType>
GetCcaEnergyDetectThreshold(int8_t & aThreshold)1434 otError RadioSpinel<InterfaceType, ProcessContextType>::GetCcaEnergyDetectThreshold(int8_t &aThreshold)
1435 {
1436 otError error = Get(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S, &aThreshold);
1437
1438 LogIfFail("Get CCA ED threshold failed", error);
1439 return error;
1440 }
1441
1442 template <typename InterfaceType, typename ProcessContextType>
GetFemLnaGain(int8_t & aGain)1443 otError RadioSpinel<InterfaceType, ProcessContextType>::GetFemLnaGain(int8_t &aGain)
1444 {
1445 otError error = Get(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, &aGain);
1446
1447 LogIfFail("Get FEM LNA gain failed", error);
1448 return error;
1449 }
1450
1451 template <typename InterfaceType, typename ProcessContextType>
GetRssi(void)1452 int8_t RadioSpinel<InterfaceType, ProcessContextType>::GetRssi(void)
1453 {
1454 int8_t rssi = OT_RADIO_RSSI_INVALID;
1455 otError error = Get(SPINEL_PROP_PHY_RSSI, SPINEL_DATATYPE_INT8_S, &rssi);
1456
1457 LogIfFail("Get RSSI failed", error);
1458 return rssi;
1459 }
1460
1461 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
1462 template <typename InterfaceType, typename ProcessContextType>
SetCoexEnabled(bool aEnabled)1463 otError RadioSpinel<InterfaceType, ProcessContextType>::SetCoexEnabled(bool aEnabled)
1464 {
1465 otError error;
1466
1467 SuccessOrExit(error = Set(SPINEL_PROP_RADIO_COEX_ENABLE, SPINEL_DATATYPE_BOOL_S, aEnabled));
1468
1469 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1470 mCoexEnabled = aEnabled;
1471 mCoexEnabledSet = true;
1472 #endif
1473
1474 exit:
1475 return error;
1476 }
1477
1478 template <typename InterfaceType, typename ProcessContextType>
IsCoexEnabled(void)1479 bool RadioSpinel<InterfaceType, ProcessContextType>::IsCoexEnabled(void)
1480 {
1481 bool enabled;
1482 otError error = Get(SPINEL_PROP_RADIO_COEX_ENABLE, SPINEL_DATATYPE_BOOL_S, &enabled);
1483
1484 LogIfFail("Get Coex State failed", error);
1485 return enabled;
1486 }
1487
1488 template <typename InterfaceType, typename ProcessContextType>
GetCoexMetrics(otRadioCoexMetrics & aCoexMetrics)1489 otError RadioSpinel<InterfaceType, ProcessContextType>::GetCoexMetrics(otRadioCoexMetrics &aCoexMetrics)
1490 {
1491 otError error;
1492
1493 error = Get(SPINEL_PROP_RADIO_COEX_METRICS,
1494 SPINEL_DATATYPE_STRUCT_S( // Tx Coex Metrics Structure
1495 SPINEL_DATATYPE_UINT32_S // NumTxRequest
1496 SPINEL_DATATYPE_UINT32_S // NumTxGrantImmediate
1497 SPINEL_DATATYPE_UINT32_S // NumTxGrantWait
1498 SPINEL_DATATYPE_UINT32_S // NumTxGrantWaitActivated
1499 SPINEL_DATATYPE_UINT32_S // NumTxGrantWaitTimeout
1500 SPINEL_DATATYPE_UINT32_S // NumTxGrantDeactivatedDuringRequest
1501 SPINEL_DATATYPE_UINT32_S // NumTxDelayedGrant
1502 SPINEL_DATATYPE_UINT32_S // AvgTxRequestToGrantTime
1503 ) SPINEL_DATATYPE_STRUCT_S( // Rx Coex Metrics Structure
1504 SPINEL_DATATYPE_UINT32_S // NumRxRequest
1505 SPINEL_DATATYPE_UINT32_S // NumRxGrantImmediate
1506 SPINEL_DATATYPE_UINT32_S // NumRxGrantWait
1507 SPINEL_DATATYPE_UINT32_S // NumRxGrantWaitActivated
1508 SPINEL_DATATYPE_UINT32_S // NumRxGrantWaitTimeout
1509 SPINEL_DATATYPE_UINT32_S // NumRxGrantDeactivatedDuringRequest
1510 SPINEL_DATATYPE_UINT32_S // NumRxDelayedGrant
1511 SPINEL_DATATYPE_UINT32_S // AvgRxRequestToGrantTime
1512 SPINEL_DATATYPE_UINT32_S // NumRxGrantNone
1513 ) SPINEL_DATATYPE_BOOL_S // Stopped
1514 SPINEL_DATATYPE_UINT32_S, // NumGrantGlitch
1515 &aCoexMetrics.mNumTxRequest, &aCoexMetrics.mNumTxGrantImmediate, &aCoexMetrics.mNumTxGrantWait,
1516 &aCoexMetrics.mNumTxGrantWaitActivated, &aCoexMetrics.mNumTxGrantWaitTimeout,
1517 &aCoexMetrics.mNumTxGrantDeactivatedDuringRequest, &aCoexMetrics.mNumTxDelayedGrant,
1518 &aCoexMetrics.mAvgTxRequestToGrantTime, &aCoexMetrics.mNumRxRequest, &aCoexMetrics.mNumRxGrantImmediate,
1519 &aCoexMetrics.mNumRxGrantWait, &aCoexMetrics.mNumRxGrantWaitActivated,
1520 &aCoexMetrics.mNumRxGrantWaitTimeout, &aCoexMetrics.mNumRxGrantDeactivatedDuringRequest,
1521 &aCoexMetrics.mNumRxDelayedGrant, &aCoexMetrics.mAvgRxRequestToGrantTime, &aCoexMetrics.mNumRxGrantNone,
1522 &aCoexMetrics.mStopped, &aCoexMetrics.mNumGrantGlitch);
1523
1524 LogIfFail("Get Coex Metrics failed", error);
1525 return error;
1526 }
1527 #endif
1528
1529 template <typename InterfaceType, typename ProcessContextType>
SetTransmitPower(int8_t aPower)1530 otError RadioSpinel<InterfaceType, ProcessContextType>::SetTransmitPower(int8_t aPower)
1531 {
1532 otError error;
1533
1534 SuccessOrExit(error = Set(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, aPower));
1535
1536 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1537 mTransmitPower = aPower;
1538 mTransmitPowerSet = true;
1539 #endif
1540
1541 exit:
1542 LogIfFail("Set transmit power failed", error);
1543 return error;
1544 }
1545
1546 template <typename InterfaceType, typename ProcessContextType>
SetCcaEnergyDetectThreshold(int8_t aThreshold)1547 otError RadioSpinel<InterfaceType, ProcessContextType>::SetCcaEnergyDetectThreshold(int8_t aThreshold)
1548 {
1549 otError error;
1550
1551 SuccessOrExit(error = Set(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S, aThreshold));
1552
1553 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1554 mCcaEnergyDetectThreshold = aThreshold;
1555 mCcaEnergyDetectThresholdSet = true;
1556 #endif
1557
1558 exit:
1559 LogIfFail("Set CCA ED threshold failed", error);
1560 return error;
1561 }
1562
1563 template <typename InterfaceType, typename ProcessContextType>
SetFemLnaGain(int8_t aGain)1564 otError RadioSpinel<InterfaceType, ProcessContextType>::SetFemLnaGain(int8_t aGain)
1565 {
1566 otError error;
1567
1568 SuccessOrExit(error = Set(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, aGain));
1569
1570 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1571 mFemLnaGain = aGain;
1572 mFemLnaGainSet = true;
1573 #endif
1574
1575 exit:
1576 LogIfFail("Set FEM LNA gain failed", error);
1577 return error;
1578 }
1579
1580 template <typename InterfaceType, typename ProcessContextType>
EnergyScan(uint8_t aScanChannel,uint16_t aScanDuration)1581 otError RadioSpinel<InterfaceType, ProcessContextType>::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration)
1582 {
1583 otError error;
1584
1585 VerifyOrExit(mRadioCaps & OT_RADIO_CAPS_ENERGY_SCAN, error = OT_ERROR_NOT_CAPABLE);
1586
1587 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1588 mScanChannel = aScanChannel;
1589 mScanDuration = aScanDuration;
1590 mEnergyScanning = true;
1591 #endif
1592
1593 SuccessOrExit(error = Set(SPINEL_PROP_MAC_SCAN_MASK, SPINEL_DATATYPE_DATA_S, &aScanChannel, sizeof(uint8_t)));
1594 SuccessOrExit(error = Set(SPINEL_PROP_MAC_SCAN_PERIOD, SPINEL_DATATYPE_UINT16_S, aScanDuration));
1595 SuccessOrExit(error = Set(SPINEL_PROP_MAC_SCAN_STATE, SPINEL_DATATYPE_UINT8_S, SPINEL_SCAN_STATE_ENERGY));
1596
1597 mChannel = aScanChannel;
1598
1599 exit:
1600 return error;
1601 }
1602
1603 template <typename InterfaceType, typename ProcessContextType>
Get(spinel_prop_key_t aKey,const char * aFormat,...)1604 otError RadioSpinel<InterfaceType, ProcessContextType>::Get(spinel_prop_key_t aKey, const char *aFormat, ...)
1605 {
1606 otError error;
1607
1608 assert(mWaitingTid == 0);
1609
1610 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1611 do
1612 {
1613 RecoverFromRcpFailure();
1614 #endif
1615 va_start(mPropertyArgs, aFormat);
1616 error = RequestWithPropertyFormatV(aFormat, SPINEL_CMD_PROP_VALUE_GET, aKey, nullptr, mPropertyArgs);
1617 va_end(mPropertyArgs);
1618 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1619 } while (mRcpFailed);
1620 #endif
1621
1622 return error;
1623 }
1624
1625 // This is not a normal use case for VALUE_GET command and should be only used to get RCP timestamp with dummy payload
1626 template <typename InterfaceType, typename ProcessContextType>
GetWithParam(spinel_prop_key_t aKey,const uint8_t * aParam,spinel_size_t aParamSize,const char * aFormat,...)1627 otError RadioSpinel<InterfaceType, ProcessContextType>::GetWithParam(spinel_prop_key_t aKey,
1628 const uint8_t *aParam,
1629 spinel_size_t aParamSize,
1630 const char *aFormat,
1631 ...)
1632 {
1633 otError error;
1634
1635 assert(mWaitingTid == 0);
1636
1637 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1638 do
1639 {
1640 RecoverFromRcpFailure();
1641 #endif
1642 va_start(mPropertyArgs, aFormat);
1643 error = RequestWithPropertyFormat(aFormat, SPINEL_CMD_PROP_VALUE_GET, aKey, SPINEL_DATATYPE_DATA_S, aParam,
1644 aParamSize);
1645 va_end(mPropertyArgs);
1646 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1647 } while (mRcpFailed);
1648 #endif
1649
1650 return error;
1651 }
1652
1653 template <typename InterfaceType, typename ProcessContextType>
Set(spinel_prop_key_t aKey,const char * aFormat,...)1654 otError RadioSpinel<InterfaceType, ProcessContextType>::Set(spinel_prop_key_t aKey, const char *aFormat, ...)
1655 {
1656 otError error;
1657
1658 assert(mWaitingTid == 0);
1659
1660 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1661 do
1662 {
1663 RecoverFromRcpFailure();
1664 #endif
1665 va_start(mPropertyArgs, aFormat);
1666 error = RequestWithExpectedCommandV(SPINEL_CMD_PROP_VALUE_IS, SPINEL_CMD_PROP_VALUE_SET, aKey, aFormat,
1667 mPropertyArgs);
1668 va_end(mPropertyArgs);
1669 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1670 } while (mRcpFailed);
1671 #endif
1672
1673 return error;
1674 }
1675
1676 template <typename InterfaceType, typename ProcessContextType>
Insert(spinel_prop_key_t aKey,const char * aFormat,...)1677 otError RadioSpinel<InterfaceType, ProcessContextType>::Insert(spinel_prop_key_t aKey, const char *aFormat, ...)
1678 {
1679 otError error;
1680
1681 assert(mWaitingTid == 0);
1682
1683 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1684 do
1685 {
1686 RecoverFromRcpFailure();
1687 #endif
1688 va_start(mPropertyArgs, aFormat);
1689 error = RequestWithExpectedCommandV(SPINEL_CMD_PROP_VALUE_INSERTED, SPINEL_CMD_PROP_VALUE_INSERT, aKey, aFormat,
1690 mPropertyArgs);
1691 va_end(mPropertyArgs);
1692 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1693 } while (mRcpFailed);
1694 #endif
1695
1696 return error;
1697 }
1698
1699 template <typename InterfaceType, typename ProcessContextType>
Remove(spinel_prop_key_t aKey,const char * aFormat,...)1700 otError RadioSpinel<InterfaceType, ProcessContextType>::Remove(spinel_prop_key_t aKey, const char *aFormat, ...)
1701 {
1702 otError error;
1703
1704 assert(mWaitingTid == 0);
1705
1706 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1707 do
1708 {
1709 RecoverFromRcpFailure();
1710 #endif
1711 va_start(mPropertyArgs, aFormat);
1712 error = RequestWithExpectedCommandV(SPINEL_CMD_PROP_VALUE_REMOVED, SPINEL_CMD_PROP_VALUE_REMOVE, aKey, aFormat,
1713 mPropertyArgs);
1714 va_end(mPropertyArgs);
1715 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
1716 } while (mRcpFailed);
1717 #endif
1718
1719 return error;
1720 }
1721
1722 template <typename InterfaceType, typename ProcessContextType>
WaitResponse(bool aHandleRcpTimeout)1723 otError RadioSpinel<InterfaceType, ProcessContextType>::WaitResponse(bool aHandleRcpTimeout)
1724 {
1725 uint64_t end = otPlatTimeGet() + kMaxWaitTime * US_PER_MS;
1726
1727 otLogDebgPlat("Wait response: tid=%u key=%lu", mWaitingTid, ToUlong(mWaitingKey));
1728
1729 do
1730 {
1731 uint64_t now;
1732
1733 now = otPlatTimeGet();
1734 if ((end <= now) || (mSpinelInterface.WaitForFrame(end - now) != OT_ERROR_NONE))
1735 {
1736 otLogWarnPlat("Wait for response timeout");
1737 if (aHandleRcpTimeout)
1738 {
1739 HandleRcpTimeout();
1740 }
1741 ExitNow(mError = OT_ERROR_NONE);
1742 }
1743 } while (mWaitingTid || !mIsReady);
1744
1745 LogIfFail("Error waiting response", mError);
1746 // This indicates end of waiting response.
1747 mWaitingKey = SPINEL_PROP_LAST_STATUS;
1748
1749 exit:
1750 return mError;
1751 }
1752
1753 template <typename InterfaceType, typename ProcessContextType>
GetNextTid(void)1754 spinel_tid_t RadioSpinel<InterfaceType, ProcessContextType>::GetNextTid(void)
1755 {
1756 spinel_tid_t tid = mCmdNextTid;
1757
1758 while (((1 << tid) & mCmdTidsInUse) != 0)
1759 {
1760 tid = SPINEL_GET_NEXT_TID(tid);
1761
1762 if (tid == mCmdNextTid)
1763 {
1764 // We looped back to `mCmdNextTid` indicating that all
1765 // TIDs are in-use.
1766
1767 ExitNow(tid = 0);
1768 }
1769 }
1770
1771 mCmdTidsInUse |= (1 << tid);
1772 mCmdNextTid = SPINEL_GET_NEXT_TID(tid);
1773
1774 exit:
1775 return tid;
1776 }
1777
1778 template <typename InterfaceType, typename ProcessContextType>
SendReset(uint8_t aResetType)1779 otError RadioSpinel<InterfaceType, ProcessContextType>::SendReset(uint8_t aResetType)
1780 {
1781 otError error = OT_ERROR_NONE;
1782 uint8_t buffer[kMaxSpinelFrame];
1783 spinel_ssize_t packed;
1784
1785 // Pack the header, command and key
1786 packed = spinel_datatype_pack(buffer, sizeof(buffer), SPINEL_DATATYPE_COMMAND_S SPINEL_DATATYPE_UINT8_S,
1787 SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_RESET, aResetType);
1788
1789 VerifyOrExit(packed > 0 && static_cast<size_t>(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
1790
1791 SuccessOrExit(error = mSpinelInterface.SendFrame(buffer, static_cast<uint16_t>(packed)));
1792 LogSpinelFrame(buffer, static_cast<uint16_t>(packed), true);
1793
1794 exit:
1795 return error;
1796 }
1797
1798 template <typename InterfaceType, typename ProcessContextType>
SendCommand(uint32_t aCommand,spinel_prop_key_t aKey,spinel_tid_t tid,const char * aFormat,va_list args)1799 otError RadioSpinel<InterfaceType, ProcessContextType>::SendCommand(uint32_t aCommand,
1800 spinel_prop_key_t aKey,
1801 spinel_tid_t tid,
1802 const char *aFormat,
1803 va_list args)
1804 {
1805 otError error = OT_ERROR_NONE;
1806 uint8_t buffer[kMaxSpinelFrame];
1807 spinel_ssize_t packed;
1808 uint16_t offset;
1809
1810 // Pack the header, command and key
1811 packed = spinel_datatype_pack(buffer, sizeof(buffer), "Cii", SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | tid,
1812 aCommand, aKey);
1813
1814 VerifyOrExit(packed > 0 && static_cast<size_t>(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
1815
1816 offset = static_cast<uint16_t>(packed);
1817
1818 // Pack the data (if any)
1819 if (aFormat)
1820 {
1821 packed = spinel_datatype_vpack(buffer + offset, sizeof(buffer) - offset, aFormat, args);
1822 VerifyOrExit(packed > 0 && static_cast<size_t>(packed + offset) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
1823
1824 offset += static_cast<uint16_t>(packed);
1825 }
1826
1827 SuccessOrExit(error = mSpinelInterface.SendFrame(buffer, offset));
1828 LogSpinelFrame(buffer, offset, true);
1829
1830 exit:
1831 return error;
1832 }
1833
1834 template <typename InterfaceType, typename ProcessContextType>
RequestV(uint32_t command,spinel_prop_key_t aKey,const char * aFormat,va_list aArgs)1835 otError RadioSpinel<InterfaceType, ProcessContextType>::RequestV(uint32_t command,
1836 spinel_prop_key_t aKey,
1837 const char *aFormat,
1838 va_list aArgs)
1839 {
1840 otError error = OT_ERROR_NONE;
1841 spinel_tid_t tid = GetNextTid();
1842
1843 VerifyOrExit(tid > 0, error = OT_ERROR_BUSY);
1844
1845 error = SendCommand(command, aKey, tid, aFormat, aArgs);
1846 SuccessOrExit(error);
1847
1848 if (aKey == SPINEL_PROP_STREAM_RAW)
1849 {
1850 // not allowed to send another frame before the last frame is done.
1851 assert(mTxRadioTid == 0);
1852 VerifyOrExit(mTxRadioTid == 0, error = OT_ERROR_BUSY);
1853 mTxRadioTid = tid;
1854 }
1855 else
1856 {
1857 mWaitingKey = aKey;
1858 mWaitingTid = tid;
1859 error = WaitResponse();
1860 }
1861
1862 exit:
1863 return error;
1864 }
1865
1866 template <typename InterfaceType, typename ProcessContextType>
Request(uint32_t aCommand,spinel_prop_key_t aKey,const char * aFormat,...)1867 otError RadioSpinel<InterfaceType, ProcessContextType>::Request(uint32_t aCommand,
1868 spinel_prop_key_t aKey,
1869 const char *aFormat,
1870 ...)
1871 {
1872 va_list args;
1873 va_start(args, aFormat);
1874 otError status = RequestV(aCommand, aKey, aFormat, args);
1875 va_end(args);
1876 return status;
1877 }
1878
1879 template <typename InterfaceType, typename ProcessContextType>
RequestWithPropertyFormat(const char * aPropertyFormat,uint32_t aCommand,spinel_prop_key_t aKey,const char * aFormat,...)1880 otError RadioSpinel<InterfaceType, ProcessContextType>::RequestWithPropertyFormat(const char *aPropertyFormat,
1881 uint32_t aCommand,
1882 spinel_prop_key_t aKey,
1883 const char *aFormat,
1884 ...)
1885 {
1886 otError error;
1887 va_list args;
1888
1889 va_start(args, aFormat);
1890 error = RequestWithPropertyFormatV(aPropertyFormat, aCommand, aKey, aFormat, args);
1891 va_end(args);
1892
1893 return error;
1894 }
1895
1896 template <typename InterfaceType, typename ProcessContextType>
RequestWithPropertyFormatV(const char * aPropertyFormat,uint32_t aCommand,spinel_prop_key_t aKey,const char * aFormat,va_list aArgs)1897 otError RadioSpinel<InterfaceType, ProcessContextType>::RequestWithPropertyFormatV(const char *aPropertyFormat,
1898 uint32_t aCommand,
1899 spinel_prop_key_t aKey,
1900 const char *aFormat,
1901 va_list aArgs)
1902 {
1903 otError error;
1904
1905 mPropertyFormat = aPropertyFormat;
1906 error = RequestV(aCommand, aKey, aFormat, aArgs);
1907 mPropertyFormat = nullptr;
1908
1909 return error;
1910 }
1911
1912 template <typename InterfaceType, typename ProcessContextType>
RequestWithExpectedCommandV(uint32_t aExpectedCommand,uint32_t aCommand,spinel_prop_key_t aKey,const char * aFormat,va_list aArgs)1913 otError RadioSpinel<InterfaceType, ProcessContextType>::RequestWithExpectedCommandV(uint32_t aExpectedCommand,
1914 uint32_t aCommand,
1915 spinel_prop_key_t aKey,
1916 const char *aFormat,
1917 va_list aArgs)
1918 {
1919 otError error;
1920
1921 mExpectedCommand = aExpectedCommand;
1922 error = RequestV(aCommand, aKey, aFormat, aArgs);
1923 mExpectedCommand = SPINEL_CMD_NOOP;
1924
1925 return error;
1926 }
1927
1928 template <typename InterfaceType, typename ProcessContextType>
HandleTransmitDone(uint32_t aCommand,spinel_prop_key_t aKey,const uint8_t * aBuffer,uint16_t aLength)1929 void RadioSpinel<InterfaceType, ProcessContextType>::HandleTransmitDone(uint32_t aCommand,
1930 spinel_prop_key_t aKey,
1931 const uint8_t *aBuffer,
1932 uint16_t aLength)
1933 {
1934 otError error = OT_ERROR_NONE;
1935 spinel_status_t status = SPINEL_STATUS_OK;
1936 bool framePending = false;
1937 bool headerUpdated = false;
1938 spinel_ssize_t unpacked;
1939
1940 VerifyOrExit(aCommand == SPINEL_CMD_PROP_VALUE_IS && aKey == SPINEL_PROP_LAST_STATUS, error = OT_ERROR_FAILED);
1941
1942 unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status);
1943 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
1944
1945 aBuffer += unpacked;
1946 aLength -= static_cast<uint16_t>(unpacked);
1947
1948 unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_BOOL_S, &framePending);
1949 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
1950
1951 aBuffer += unpacked;
1952 aLength -= static_cast<uint16_t>(unpacked);
1953
1954 unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_BOOL_S, &headerUpdated);
1955 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
1956
1957 aBuffer += unpacked;
1958 aLength -= static_cast<uint16_t>(unpacked);
1959
1960 if (status == SPINEL_STATUS_OK)
1961 {
1962 SuccessOrExit(error = ParseRadioFrame(mAckRadioFrame, aBuffer, aLength, unpacked));
1963 aBuffer += unpacked;
1964 aLength -= static_cast<uint16_t>(unpacked);
1965 }
1966 else
1967 {
1968 error = SpinelStatusToOtError(status);
1969 }
1970
1971 static_cast<Mac::TxFrame *>(mTransmitFrame)->SetIsHeaderUpdated(headerUpdated);
1972
1973 if ((mRadioCaps & OT_RADIO_CAPS_TRANSMIT_SEC) && headerUpdated &&
1974 static_cast<Mac::TxFrame *>(mTransmitFrame)->GetSecurityEnabled())
1975 {
1976 uint8_t keyId;
1977 uint32_t frameCounter;
1978
1979 // Replace transmit frame security key index and frame counter with the one filled by RCP
1980 unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT32_S, &keyId,
1981 &frameCounter);
1982 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
1983 static_cast<Mac::TxFrame *>(mTransmitFrame)->SetKeyId(keyId);
1984 static_cast<Mac::TxFrame *>(mTransmitFrame)->SetFrameCounter(frameCounter);
1985 }
1986
1987 exit:
1988 mState = kStateTransmitDone;
1989 mTxError = error;
1990 UpdateParseErrorCount(error);
1991 LogIfFail("Handle transmit done failed", error);
1992 }
1993
1994 template <typename InterfaceType, typename ProcessContextType>
Transmit(otRadioFrame & aFrame)1995 otError RadioSpinel<InterfaceType, ProcessContextType>::Transmit(otRadioFrame &aFrame)
1996 {
1997 otError error = OT_ERROR_INVALID_STATE;
1998
1999 VerifyOrExit(mState == kStateReceive || (mState == kStateSleep && (mRadioCaps & OT_RADIO_CAPS_SLEEP_TO_TX)));
2000
2001 mTransmitFrame = &aFrame;
2002
2003 // `otPlatRadioTxStarted()` is triggered immediately for now, which may be earlier than real started time.
2004 otPlatRadioTxStarted(mInstance, mTransmitFrame);
2005
2006 error = Request(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_STREAM_RAW,
2007 SPINEL_DATATYPE_DATA_WLEN_S // Frame data
2008 SPINEL_DATATYPE_UINT8_S // Channel
2009 SPINEL_DATATYPE_UINT8_S // MaxCsmaBackoffs
2010 SPINEL_DATATYPE_UINT8_S // MaxFrameRetries
2011 SPINEL_DATATYPE_BOOL_S // CsmaCaEnabled
2012 SPINEL_DATATYPE_BOOL_S // IsHeaderUpdated
2013 SPINEL_DATATYPE_BOOL_S // IsARetx
2014 SPINEL_DATATYPE_BOOL_S // IsSecurityProcessed
2015 SPINEL_DATATYPE_UINT32_S // TxDelay
2016 SPINEL_DATATYPE_UINT32_S // TxDelayBaseTime
2017 SPINEL_DATATYPE_UINT8_S, // RxChannelAfterTxDone
2018 mTransmitFrame->mPsdu, mTransmitFrame->mLength, mTransmitFrame->mChannel,
2019 mTransmitFrame->mInfo.mTxInfo.mMaxCsmaBackoffs, mTransmitFrame->mInfo.mTxInfo.mMaxFrameRetries,
2020 mTransmitFrame->mInfo.mTxInfo.mCsmaCaEnabled, mTransmitFrame->mInfo.mTxInfo.mIsHeaderUpdated,
2021 mTransmitFrame->mInfo.mTxInfo.mIsARetx, mTransmitFrame->mInfo.mTxInfo.mIsSecurityProcessed,
2022 mTransmitFrame->mInfo.mTxInfo.mTxDelay, mTransmitFrame->mInfo.mTxInfo.mTxDelayBaseTime,
2023 mTransmitFrame->mInfo.mTxInfo.mRxChannelAfterTxDone);
2024
2025 if (error == OT_ERROR_NONE)
2026 {
2027 // Waiting for `TransmitDone` event.
2028 mState = kStateTransmitting;
2029 mTxRadioEndUs = otPlatTimeGet() + TX_WAIT_US;
2030 mChannel = mTransmitFrame->mChannel;
2031 }
2032
2033 exit:
2034 return error;
2035 }
2036
2037 template <typename InterfaceType, typename ProcessContextType>
Receive(uint8_t aChannel)2038 otError RadioSpinel<InterfaceType, ProcessContextType>::Receive(uint8_t aChannel)
2039 {
2040 otError error = OT_ERROR_NONE;
2041
2042 VerifyOrExit(mState != kStateDisabled, error = OT_ERROR_INVALID_STATE);
2043
2044 if (mChannel != aChannel)
2045 {
2046 error = Set(SPINEL_PROP_PHY_CHAN, SPINEL_DATATYPE_UINT8_S, aChannel);
2047 SuccessOrExit(error);
2048 mChannel = aChannel;
2049 }
2050
2051 if (mState == kStateSleep)
2052 {
2053 error = Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true);
2054 SuccessOrExit(error);
2055 }
2056
2057 if (mTxRadioTid != 0)
2058 {
2059 FreeTid(mTxRadioTid);
2060 mTxRadioTid = 0;
2061 }
2062
2063 mState = kStateReceive;
2064
2065 exit:
2066 return error;
2067 }
2068
2069 template <typename InterfaceType, typename ProcessContextType>
Sleep(void)2070 otError RadioSpinel<InterfaceType, ProcessContextType>::Sleep(void)
2071 {
2072 otError error = OT_ERROR_NONE;
2073
2074 switch (mState)
2075 {
2076 case kStateReceive:
2077 error = Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, false);
2078 SuccessOrExit(error);
2079
2080 mState = kStateSleep;
2081 break;
2082
2083 case kStateSleep:
2084 break;
2085
2086 default:
2087 error = OT_ERROR_INVALID_STATE;
2088 break;
2089 }
2090
2091 exit:
2092 return error;
2093 }
2094
2095 template <typename InterfaceType, typename ProcessContextType>
Enable(otInstance * aInstance)2096 otError RadioSpinel<InterfaceType, ProcessContextType>::Enable(otInstance *aInstance)
2097 {
2098 otError error = OT_ERROR_NONE;
2099
2100 VerifyOrExit(!IsEnabled());
2101
2102 mInstance = aInstance;
2103
2104 SuccessOrExit(error = Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
2105 SuccessOrExit(error = Set(SPINEL_PROP_MAC_15_4_PANID, SPINEL_DATATYPE_UINT16_S, mPanId));
2106 SuccessOrExit(error = Set(SPINEL_PROP_MAC_15_4_SADDR, SPINEL_DATATYPE_UINT16_S, mShortAddress));
2107 SuccessOrExit(error = Get(SPINEL_PROP_PHY_RX_SENSITIVITY, SPINEL_DATATYPE_INT8_S, &mRxSensitivity));
2108
2109 mState = kStateSleep;
2110
2111 exit:
2112 if (error != OT_ERROR_NONE)
2113 {
2114 otLogWarnPlat("RadioSpinel enable: %s", otThreadErrorToString(error));
2115 error = OT_ERROR_FAILED;
2116 }
2117
2118 return error;
2119 }
2120
2121 template <typename InterfaceType, typename ProcessContextType>
Disable(void)2122 otError RadioSpinel<InterfaceType, ProcessContextType>::Disable(void)
2123 {
2124 otError error = OT_ERROR_NONE;
2125
2126 VerifyOrExit(IsEnabled());
2127 VerifyOrExit(mState == kStateSleep, error = OT_ERROR_INVALID_STATE);
2128
2129 SuccessOrDie(Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, false));
2130 mState = kStateDisabled;
2131 mInstance = nullptr;
2132
2133 exit:
2134 return error;
2135 }
2136
2137 #if OPENTHREAD_CONFIG_DIAG_ENABLE
2138 template <typename InterfaceType, typename ProcessContextType>
PlatDiagProcess(const char * aString,char * aOutput,size_t aOutputMaxLen)2139 otError RadioSpinel<InterfaceType, ProcessContextType>::PlatDiagProcess(const char *aString,
2140 char *aOutput,
2141 size_t aOutputMaxLen)
2142 {
2143 otError error;
2144
2145 mDiagOutput = aOutput;
2146 mDiagOutputMaxLen = aOutputMaxLen;
2147
2148 error = Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString);
2149
2150 mDiagOutput = nullptr;
2151 mDiagOutputMaxLen = 0;
2152
2153 return error;
2154 }
2155 #endif
2156
2157 template <typename InterfaceType, typename ProcessContextType>
GetRadioChannelMask(bool aPreferred)2158 uint32_t RadioSpinel<InterfaceType, ProcessContextType>::GetRadioChannelMask(bool aPreferred)
2159 {
2160 uint8_t maskBuffer[kChannelMaskBufferSize];
2161 otError error = OT_ERROR_NONE;
2162 uint32_t channelMask = 0;
2163 const uint8_t *maskData = maskBuffer;
2164 spinel_size_t maskLength = sizeof(maskBuffer);
2165
2166 SuccessOrDie(Get(aPreferred ? SPINEL_PROP_PHY_CHAN_PREFERRED : SPINEL_PROP_PHY_CHAN_SUPPORTED,
2167 SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength));
2168
2169 while (maskLength > 0)
2170 {
2171 uint8_t channel;
2172 spinel_ssize_t unpacked;
2173
2174 unpacked = spinel_datatype_unpack(maskData, maskLength, SPINEL_DATATYPE_UINT8_S, &channel);
2175 VerifyOrExit(unpacked > 0, error = OT_ERROR_FAILED);
2176 VerifyOrExit(channel < kChannelMaskBufferSize, error = OT_ERROR_PARSE);
2177 channelMask |= (1UL << channel);
2178
2179 maskData += unpacked;
2180 maskLength -= static_cast<spinel_size_t>(unpacked);
2181 }
2182
2183 channelMask &= mMaxPowerTable.GetSupportedChannelMask();
2184
2185 exit:
2186 UpdateParseErrorCount(error);
2187 LogIfFail("Get radio channel mask failed", error);
2188 return channelMask;
2189 }
2190
2191 template <typename InterfaceType, typename ProcessContextType>
GetState(void) const2192 otRadioState RadioSpinel<InterfaceType, ProcessContextType>::GetState(void) const
2193 {
2194 static const otRadioState sOtRadioStateMap[] = {
2195 OT_RADIO_STATE_DISABLED, OT_RADIO_STATE_SLEEP, OT_RADIO_STATE_RECEIVE,
2196 OT_RADIO_STATE_TRANSMIT, OT_RADIO_STATE_TRANSMIT,
2197 };
2198
2199 return sOtRadioStateMap[mState];
2200 }
2201
2202 template <typename InterfaceType, typename ProcessContextType>
CalcRcpTimeOffset(void)2203 void RadioSpinel<InterfaceType, ProcessContextType>::CalcRcpTimeOffset(void)
2204 {
2205 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
2206 otError error = OT_ERROR_NONE;
2207 uint64_t localTxTimestamp;
2208 uint64_t localRxTimestamp;
2209 uint64_t remoteTimestamp = 0;
2210 uint8_t buffer[sizeof(remoteTimestamp)];
2211 spinel_ssize_t packed;
2212
2213 /*
2214 * Use a modified Network Time Protocol(NTP) to calculate the time offset
2215 * Assume the time offset is D so that local can calculate remote time with,
2216 * T' = T + D
2217 * Where T is the local time and T' is the remote time.
2218 * The time offset is calculated using timestamp measured at local and remote.
2219 *
2220 * T0 P P T2
2221 * local time --+----+----+--->
2222 * \ | ^
2223 * get\ | /is
2224 * v | /
2225 * remote time -------+--------->
2226 * T1'
2227 *
2228 * Based on the assumptions,
2229 * 1. If the propagation time(P) from local to remote and from remote to local are same.
2230 * 2. Both the host and RCP can accurately measure the time they send or receive a message.
2231 * The degree to which these assumptions hold true determines the accuracy of the offset.
2232 * Then,
2233 * T1' = T0 + P + D and T1' = T2 - P + D
2234 * Time offset can be calculated with,
2235 * D = T1' - ((T0 + T2)/ 2)
2236 */
2237
2238 VerifyOrExit(!mIsTimeSynced || (otPlatTimeGet() >= GetNextRadioTimeRecalcStart()));
2239
2240 otLogDebgPlat("Trying to get RCP time offset");
2241
2242 packed = spinel_datatype_pack(buffer, sizeof(buffer), SPINEL_DATATYPE_UINT64_S, remoteTimestamp);
2243 VerifyOrExit(packed > 0 && static_cast<size_t>(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
2244
2245 localTxTimestamp = otPlatTimeGet();
2246
2247 // Dummy timestamp payload to make request length same as response
2248 error = GetWithParam(SPINEL_PROP_RCP_TIMESTAMP, buffer, static_cast<spinel_size_t>(packed),
2249 SPINEL_DATATYPE_UINT64_S, &remoteTimestamp);
2250
2251 localRxTimestamp = otPlatTimeGet();
2252
2253 VerifyOrExit(error == OT_ERROR_NONE, mRadioTimeRecalcStart = localRxTimestamp);
2254
2255 mRadioTimeOffset = (remoteTimestamp - ((localRxTimestamp / 2) + (localTxTimestamp / 2)));
2256 mIsTimeSynced = true;
2257 mRadioTimeRecalcStart = localRxTimestamp + OPENTHREAD_POSIX_CONFIG_RCP_TIME_SYNC_INTERVAL;
2258
2259 exit:
2260 LogIfFail("Error calculating RCP time offset: %s", error);
2261 #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
2262 }
2263
2264 template <typename InterfaceType, typename ProcessContextType>
GetNow(void)2265 uint64_t RadioSpinel<InterfaceType, ProcessContextType>::GetNow(void)
2266 {
2267 return (mIsTimeSynced) ? (otPlatTimeGet() + mRadioTimeOffset) : UINT64_MAX;
2268 }
2269
2270 template <typename InterfaceType, typename ProcessContextType>
GetBusSpeed(void) const2271 uint32_t RadioSpinel<InterfaceType, ProcessContextType>::GetBusSpeed(void) const
2272 {
2273 return mSpinelInterface.GetBusSpeed();
2274 }
2275
2276 template <typename InterfaceType, typename ProcessContextType>
HandleRcpUnexpectedReset(spinel_status_t aStatus)2277 void RadioSpinel<InterfaceType, ProcessContextType>::HandleRcpUnexpectedReset(spinel_status_t aStatus)
2278 {
2279 OT_UNUSED_VARIABLE(aStatus);
2280
2281 mRadioSpinelMetrics.mRcpUnexpectedResetCount++;
2282 otLogCritPlat("Unexpected RCP reset: %s", spinel_status_to_cstr(aStatus));
2283
2284 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
2285 mRcpFailed = true;
2286 #elif OPENTHREAD_SPINEL_CONFIG_ABORT_ON_UNEXPECTED_RCP_RESET_ENABLE
2287 abort();
2288 #else
2289 DieNow(OT_EXIT_RADIO_SPINEL_RESET);
2290 #endif
2291 }
2292
2293 template <typename InterfaceType, typename ProcessContextType>
HandleRcpTimeout(void)2294 void RadioSpinel<InterfaceType, ProcessContextType>::HandleRcpTimeout(void)
2295 {
2296 mRadioSpinelMetrics.mRcpTimeoutCount++;
2297
2298 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
2299 mRcpFailed = true;
2300 #else
2301 if (!mIsReady)
2302 {
2303 otLogCritPlat("Failed to communicate with RCP - no response from RCP during initialization");
2304 otLogCritPlat("This is not a bug and typically due a config error (wrong URL parameters) or bad RCP image:");
2305 otLogCritPlat("- Make sure RCP is running the correct firmware");
2306 otLogCritPlat("- Double check the config parameters passed as `RadioURL` input");
2307 }
2308
2309 DieNow(OT_EXIT_RADIO_SPINEL_NO_RESPONSE);
2310 #endif
2311 }
2312
2313 template <typename InterfaceType, typename ProcessContextType>
RecoverFromRcpFailure(void)2314 void RadioSpinel<InterfaceType, ProcessContextType>::RecoverFromRcpFailure(void)
2315 {
2316 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
2317 constexpr int16_t kMaxFailureCount = OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT;
2318 State recoveringState = mState;
2319
2320 if (!mRcpFailed)
2321 {
2322 ExitNow();
2323 }
2324 mRcpFailed = false;
2325
2326 otLogWarnPlat("RCP failure detected");
2327
2328 ++mRadioSpinelMetrics.mRcpRestorationCount;
2329 ++mRcpFailureCount;
2330 if (mRcpFailureCount > kMaxFailureCount)
2331 {
2332 otLogCritPlat("Too many rcp failures, exiting");
2333 DieNow(OT_EXIT_FAILURE);
2334 }
2335
2336 otLogWarnPlat("Trying to recover (%d/%d)", mRcpFailureCount, kMaxFailureCount);
2337
2338 mState = kStateDisabled;
2339 mRxFrameBuffer.Clear();
2340 mCmdTidsInUse = 0;
2341 mCmdNextTid = 1;
2342 mTxRadioTid = 0;
2343 mWaitingTid = 0;
2344 mError = OT_ERROR_NONE;
2345 mIsTimeSynced = false;
2346
2347 ResetRcp(mResetRadioOnStartup);
2348 SuccessOrDie(Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
2349 mState = kStateSleep;
2350
2351 RestoreProperties();
2352
2353 switch (recoveringState)
2354 {
2355 case kStateDisabled:
2356 case kStateSleep:
2357 break;
2358 case kStateReceive:
2359 SuccessOrDie(Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
2360 mState = kStateReceive;
2361 break;
2362 case kStateTransmitting:
2363 case kStateTransmitDone:
2364 SuccessOrDie(Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
2365 mTxError = OT_ERROR_ABORT;
2366 mState = kStateTransmitDone;
2367 break;
2368 }
2369
2370 if (mEnergyScanning)
2371 {
2372 SuccessOrDie(EnergyScan(mScanChannel, mScanDuration));
2373 }
2374
2375 --mRcpFailureCount;
2376 otLogNotePlat("RCP recovery is done");
2377
2378 exit:
2379 return;
2380 #endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
2381 }
2382
2383 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
2384 template <typename InterfaceType, typename ProcessContextType>
RestoreProperties(void)2385 void RadioSpinel<InterfaceType, ProcessContextType>::RestoreProperties(void)
2386 {
2387 Settings::NetworkInfo networkInfo;
2388
2389 SuccessOrDie(Set(SPINEL_PROP_MAC_15_4_PANID, SPINEL_DATATYPE_UINT16_S, mPanId));
2390 SuccessOrDie(Set(SPINEL_PROP_MAC_15_4_SADDR, SPINEL_DATATYPE_UINT16_S, mShortAddress));
2391 SuccessOrDie(Set(SPINEL_PROP_MAC_15_4_LADDR, SPINEL_DATATYPE_EUI64_S, mExtendedAddress.m8));
2392 SuccessOrDie(Set(SPINEL_PROP_PHY_CHAN, SPINEL_DATATYPE_UINT8_S, mChannel));
2393
2394 if (mMacKeySet)
2395 {
2396 SuccessOrDie(Set(SPINEL_PROP_RCP_MAC_KEY,
2397 SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S
2398 SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_WLEN_S,
2399 mKeyIdMode, mKeyId, mPrevKey.m8, sizeof(otMacKey), mCurrKey.m8, sizeof(otMacKey), mNextKey.m8,
2400 sizeof(otMacKey)));
2401 }
2402
2403 if (mInstance != nullptr)
2404 {
2405 SuccessOrDie(static_cast<Instance *>(mInstance)->template Get<Settings>().Read(networkInfo));
2406 SuccessOrDie(
2407 Set(SPINEL_PROP_RCP_MAC_FRAME_COUNTER, SPINEL_DATATYPE_UINT32_S, networkInfo.GetMacFrameCounter()));
2408 }
2409
2410 for (int i = 0; i < mSrcMatchShortEntryCount; ++i)
2411 {
2412 SuccessOrDie(
2413 Insert(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, mSrcMatchShortEntries[i]));
2414 }
2415
2416 for (int i = 0; i < mSrcMatchExtEntryCount; ++i)
2417 {
2418 SuccessOrDie(
2419 Insert(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, mSrcMatchExtEntries[i].m8));
2420 }
2421
2422 if (mCcaEnergyDetectThresholdSet)
2423 {
2424 SuccessOrDie(Set(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S, mCcaEnergyDetectThreshold));
2425 }
2426
2427 if (mTransmitPowerSet)
2428 {
2429 SuccessOrDie(Set(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, mTransmitPower));
2430 }
2431
2432 if (mCoexEnabledSet)
2433 {
2434 SuccessOrDie(Set(SPINEL_PROP_RADIO_COEX_ENABLE, SPINEL_DATATYPE_BOOL_S, mCoexEnabled));
2435 }
2436
2437 if (mFemLnaGainSet)
2438 {
2439 SuccessOrDie(Set(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, mFemLnaGain));
2440 }
2441
2442 #if OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
2443 for (uint8_t channel = Radio::kChannelMin; channel <= Radio::kChannelMax; channel++)
2444 {
2445 int8_t power = mMaxPowerTable.GetTransmitPower(channel);
2446
2447 if (power != OT_RADIO_POWER_INVALID)
2448 {
2449 // Some old RCPs doesn't support max transmit power
2450 otError error = SetChannelMaxTransmitPower(channel, power);
2451
2452 if (error != OT_ERROR_NONE && error != OT_ERROR_NOT_FOUND)
2453 {
2454 DieNow(OT_EXIT_FAILURE);
2455 }
2456 }
2457 }
2458 #endif // OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
2459
2460 CalcRcpTimeOffset();
2461 }
2462 #endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
2463
2464 template <typename InterfaceType, typename ProcessContextType>
SetChannelMaxTransmitPower(uint8_t aChannel,int8_t aMaxPower)2465 otError RadioSpinel<InterfaceType, ProcessContextType>::SetChannelMaxTransmitPower(uint8_t aChannel, int8_t aMaxPower)
2466 {
2467 otError error = OT_ERROR_NONE;
2468 VerifyOrExit(aChannel >= Radio::kChannelMin && aChannel <= Radio::kChannelMax, error = OT_ERROR_INVALID_ARGS);
2469 mMaxPowerTable.SetTransmitPower(aChannel, aMaxPower);
2470 error = Set(SPINEL_PROP_PHY_CHAN_MAX_POWER, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S, aChannel, aMaxPower);
2471
2472 exit:
2473 return error;
2474 }
2475
2476 template <typename InterfaceType, typename ProcessContextType>
SetRadioRegion(uint16_t aRegionCode)2477 otError RadioSpinel<InterfaceType, ProcessContextType>::SetRadioRegion(uint16_t aRegionCode)
2478 {
2479 otError error;
2480
2481 error = Set(SPINEL_PROP_PHY_REGION_CODE, SPINEL_DATATYPE_UINT16_S, aRegionCode);
2482
2483 if (error == OT_ERROR_NONE)
2484 {
2485 otLogNotePlat("Set region code \"%c%c\" successfully", static_cast<char>(aRegionCode >> 8),
2486 static_cast<char>(aRegionCode));
2487 }
2488 else
2489 {
2490 otLogWarnPlat("Failed to set region code \"%c%c\": %s", static_cast<char>(aRegionCode >> 8),
2491 static_cast<char>(aRegionCode), otThreadErrorToString(error));
2492 }
2493
2494 return error;
2495 }
2496
2497 template <typename InterfaceType, typename ProcessContextType>
GetRadioRegion(uint16_t * aRegionCode)2498 otError RadioSpinel<InterfaceType, ProcessContextType>::GetRadioRegion(uint16_t *aRegionCode)
2499 {
2500 otError error = OT_ERROR_NONE;
2501
2502 VerifyOrExit(aRegionCode != nullptr, error = OT_ERROR_INVALID_ARGS);
2503 error = Get(SPINEL_PROP_PHY_REGION_CODE, SPINEL_DATATYPE_UINT16_S, aRegionCode);
2504
2505 exit:
2506 return error;
2507 }
2508
2509 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
2510 template <typename InterfaceType, typename ProcessContextType>
ConfigureEnhAckProbing(otLinkMetrics aLinkMetrics,const otShortAddress aShortAddress,const otExtAddress & aExtAddress)2511 otError RadioSpinel<InterfaceType, ProcessContextType>::ConfigureEnhAckProbing(otLinkMetrics aLinkMetrics,
2512 const otShortAddress aShortAddress,
2513 const otExtAddress &aExtAddress)
2514 {
2515 otError error = OT_ERROR_NONE;
2516 uint8_t flags = 0;
2517
2518 if (aLinkMetrics.mPduCount)
2519 {
2520 flags |= SPINEL_THREAD_LINK_METRIC_PDU_COUNT;
2521 }
2522
2523 if (aLinkMetrics.mLqi)
2524 {
2525 flags |= SPINEL_THREAD_LINK_METRIC_LQI;
2526 }
2527
2528 if (aLinkMetrics.mLinkMargin)
2529 {
2530 flags |= SPINEL_THREAD_LINK_METRIC_LINK_MARGIN;
2531 }
2532
2533 if (aLinkMetrics.mRssi)
2534 {
2535 flags |= SPINEL_THREAD_LINK_METRIC_RSSI;
2536 }
2537
2538 error =
2539 Set(SPINEL_PROP_RCP_ENH_ACK_PROBING, SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S,
2540 aShortAddress, aExtAddress.m8, flags);
2541
2542 return error;
2543 }
2544 #endif
2545
2546 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
2547 template <typename InterfaceType, typename ProcessContextType>
GetCslAccuracy(void)2548 uint8_t RadioSpinel<InterfaceType, ProcessContextType>::GetCslAccuracy(void)
2549 {
2550 uint8_t accuracy = UINT8_MAX;
2551 otError error = Get(SPINEL_PROP_RCP_CSL_ACCURACY, SPINEL_DATATYPE_UINT8_S, &accuracy);
2552
2553 LogIfFail("Get CSL Accuracy failed", error);
2554 return accuracy;
2555 }
2556 #endif
2557
2558 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
2559 template <typename InterfaceType, typename ProcessContextType>
GetCslUncertainty(void)2560 uint8_t RadioSpinel<InterfaceType, ProcessContextType>::GetCslUncertainty(void)
2561 {
2562 uint8_t uncertainty = UINT8_MAX;
2563 otError error = Get(SPINEL_PROP_RCP_CSL_UNCERTAINTY, SPINEL_DATATYPE_UINT8_S, &uncertainty);
2564
2565 LogIfFail("Get CSL Uncertainty failed", error);
2566 return uncertainty;
2567 }
2568 #endif
2569
2570 #if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
2571 template <typename InterfaceType, typename ProcessContextType>
AddCalibratedPower(uint8_t aChannel,int16_t aActualPower,const uint8_t * aRawPowerSetting,uint16_t aRawPowerSettingLength)2572 otError RadioSpinel<InterfaceType, ProcessContextType>::AddCalibratedPower(uint8_t aChannel,
2573 int16_t aActualPower,
2574 const uint8_t *aRawPowerSetting,
2575 uint16_t aRawPowerSettingLength)
2576 {
2577 otError error;
2578
2579 assert(aRawPowerSetting != nullptr);
2580 SuccessOrExit(error = Insert(SPINEL_PROP_PHY_CALIBRATED_POWER,
2581 SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_DATA_WLEN_S, aChannel,
2582 aActualPower, aRawPowerSetting, aRawPowerSettingLength));
2583
2584 exit:
2585 return error;
2586 }
2587
2588 template <typename InterfaceType, typename ProcessContextType>
ClearCalibratedPowers(void)2589 otError RadioSpinel<InterfaceType, ProcessContextType>::ClearCalibratedPowers(void)
2590 {
2591 return Set(SPINEL_PROP_PHY_CALIBRATED_POWER, nullptr);
2592 }
2593
2594 template <typename InterfaceType, typename ProcessContextType>
SetChannelTargetPower(uint8_t aChannel,int16_t aTargetPower)2595 otError RadioSpinel<InterfaceType, ProcessContextType>::SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower)
2596 {
2597 otError error = OT_ERROR_NONE;
2598 VerifyOrExit(aChannel >= Radio::kChannelMin && aChannel <= Radio::kChannelMax, error = OT_ERROR_INVALID_ARGS);
2599 error =
2600 Set(SPINEL_PROP_PHY_CHAN_TARGET_POWER, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, aChannel, aTargetPower);
2601
2602 exit:
2603 return error;
2604 }
2605 #endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
2606
2607 template <typename InterfaceType, typename ProcessContextType>
Snprintf(char * aDest,uint32_t aSize,const char * aFormat,...)2608 uint32_t RadioSpinel<InterfaceType, ProcessContextType>::Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...)
2609 {
2610 int len;
2611 va_list args;
2612
2613 va_start(args, aFormat);
2614 len = vsnprintf(aDest, static_cast<size_t>(aSize), aFormat, args);
2615 va_end(args);
2616
2617 return (len < 0) ? 0 : Min(static_cast<uint32_t>(len), aSize - 1);
2618 }
2619
2620 template <typename InterfaceType, typename ProcessContextType>
LogSpinelFrame(const uint8_t * aFrame,uint16_t aLength,bool aTx)2621 void RadioSpinel<InterfaceType, ProcessContextType>::LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx)
2622 {
2623 otError error = OT_ERROR_NONE;
2624 char buf[OPENTHREAD_CONFIG_LOG_MAX_SIZE] = {0};
2625 spinel_ssize_t unpacked;
2626 uint8_t header;
2627 uint32_t cmd;
2628 spinel_prop_key_t key;
2629 uint8_t *data;
2630 spinel_size_t len;
2631 const char *prefix = nullptr;
2632 char *start = buf;
2633 char *end = buf + sizeof(buf);
2634
2635 VerifyOrExit(otLoggingGetLevel() >= OT_LOG_LEVEL_DEBG);
2636
2637 prefix = aTx ? "Sent spinel frame" : "Received spinel frame";
2638 unpacked = spinel_datatype_unpack(aFrame, aLength, "CiiD", &header, &cmd, &key, &data, &len);
2639 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2640
2641 start += Snprintf(start, static_cast<uint32_t>(end - start), "%s, flg:0x%x, tid:%u, cmd:%s", prefix,
2642 SPINEL_HEADER_GET_FLAG(header), SPINEL_HEADER_GET_TID(header), spinel_command_to_cstr(cmd));
2643 VerifyOrExit(cmd != SPINEL_CMD_RESET);
2644
2645 start += Snprintf(start, static_cast<uint32_t>(end - start), ", key:%s", spinel_prop_key_to_cstr(key));
2646 VerifyOrExit(cmd != SPINEL_CMD_PROP_VALUE_GET);
2647
2648 switch (key)
2649 {
2650 case SPINEL_PROP_LAST_STATUS:
2651 {
2652 spinel_status_t status;
2653
2654 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &status);
2655 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2656 start += Snprintf(start, static_cast<uint32_t>(end - start), ", status:%s", spinel_status_to_cstr(status));
2657 }
2658 break;
2659
2660 case SPINEL_PROP_MAC_RAW_STREAM_ENABLED:
2661 case SPINEL_PROP_MAC_SRC_MATCH_ENABLED:
2662 case SPINEL_PROP_PHY_ENABLED:
2663 case SPINEL_PROP_RADIO_COEX_ENABLE:
2664 {
2665 bool enabled;
2666
2667 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_BOOL_S, &enabled);
2668 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2669 start += Snprintf(start, static_cast<uint32_t>(end - start), ", enabled:%u", enabled);
2670 }
2671 break;
2672
2673 case SPINEL_PROP_PHY_CCA_THRESHOLD:
2674 case SPINEL_PROP_PHY_FEM_LNA_GAIN:
2675 case SPINEL_PROP_PHY_RX_SENSITIVITY:
2676 case SPINEL_PROP_PHY_RSSI:
2677 case SPINEL_PROP_PHY_TX_POWER:
2678 {
2679 const char *name = nullptr;
2680 int8_t value;
2681
2682 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_INT8_S, &value);
2683 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2684
2685 switch (key)
2686 {
2687 case SPINEL_PROP_PHY_TX_POWER:
2688 name = "power";
2689 break;
2690 case SPINEL_PROP_PHY_CCA_THRESHOLD:
2691 name = "threshold";
2692 break;
2693 case SPINEL_PROP_PHY_FEM_LNA_GAIN:
2694 name = "gain";
2695 break;
2696 case SPINEL_PROP_PHY_RX_SENSITIVITY:
2697 name = "sensitivity";
2698 break;
2699 case SPINEL_PROP_PHY_RSSI:
2700 name = "rssi";
2701 break;
2702 }
2703
2704 start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%d", name, value);
2705 }
2706 break;
2707
2708 case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
2709 case SPINEL_PROP_MAC_SCAN_STATE:
2710 case SPINEL_PROP_PHY_CHAN:
2711 case SPINEL_PROP_RCP_CSL_ACCURACY:
2712 case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
2713 {
2714 const char *name = nullptr;
2715 uint8_t value;
2716
2717 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &value);
2718 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2719
2720 switch (key)
2721 {
2722 case SPINEL_PROP_MAC_SCAN_STATE:
2723 name = "state";
2724 break;
2725 case SPINEL_PROP_RCP_CSL_ACCURACY:
2726 name = "accuracy";
2727 break;
2728 case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
2729 name = "uncertainty";
2730 break;
2731 case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
2732 name = "mode";
2733 break;
2734 case SPINEL_PROP_PHY_CHAN:
2735 name = "channel";
2736 break;
2737 }
2738
2739 start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
2740 }
2741 break;
2742
2743 case SPINEL_PROP_MAC_15_4_PANID:
2744 case SPINEL_PROP_MAC_15_4_SADDR:
2745 case SPINEL_PROP_MAC_SCAN_PERIOD:
2746 case SPINEL_PROP_PHY_REGION_CODE:
2747 {
2748 const char *name = nullptr;
2749 uint16_t value;
2750
2751 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &value);
2752 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2753
2754 switch (key)
2755 {
2756 case SPINEL_PROP_MAC_SCAN_PERIOD:
2757 name = "period";
2758 break;
2759 case SPINEL_PROP_PHY_REGION_CODE:
2760 name = "region";
2761 break;
2762 case SPINEL_PROP_MAC_15_4_SADDR:
2763 name = "saddr";
2764 break;
2765 case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
2766 name = "saddr";
2767 break;
2768 case SPINEL_PROP_MAC_15_4_PANID:
2769 name = "panid";
2770 break;
2771 }
2772
2773 start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:0x%04x", name, value);
2774 }
2775 break;
2776
2777 case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
2778 {
2779 uint16_t saddr;
2780
2781 start += Snprintf(start, static_cast<uint32_t>(end - start), ", saddr:");
2782
2783 if (len < sizeof(saddr))
2784 {
2785 start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
2786 }
2787 else
2788 {
2789 while (len >= sizeof(saddr))
2790 {
2791 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &saddr);
2792 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2793 data += unpacked;
2794 len -= static_cast<spinel_size_t>(unpacked);
2795 start += Snprintf(start, static_cast<uint32_t>(end - start), "0x%04x ", saddr);
2796 }
2797 }
2798 }
2799 break;
2800
2801 case SPINEL_PROP_RCP_MAC_FRAME_COUNTER:
2802 case SPINEL_PROP_RCP_TIMESTAMP:
2803 {
2804 const char *name;
2805 uint32_t value;
2806
2807 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT32_S, &value);
2808 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2809
2810 name = (key == SPINEL_PROP_RCP_TIMESTAMP) ? "timestamp" : "counter";
2811 start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
2812 }
2813 break;
2814
2815 case SPINEL_PROP_RADIO_CAPS:
2816 case SPINEL_PROP_RCP_API_VERSION:
2817 case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
2818 {
2819 const char *name;
2820 unsigned int value;
2821
2822 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &value);
2823 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2824
2825 switch (key)
2826 {
2827 case SPINEL_PROP_RADIO_CAPS:
2828 name = "caps";
2829 break;
2830 case SPINEL_PROP_RCP_API_VERSION:
2831 name = "version";
2832 break;
2833 case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
2834 name = "min-host-version";
2835 break;
2836 default:
2837 name = "";
2838 break;
2839 }
2840
2841 start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
2842 }
2843 break;
2844
2845 case SPINEL_PROP_MAC_ENERGY_SCAN_RESULT:
2846 case SPINEL_PROP_PHY_CHAN_MAX_POWER:
2847 {
2848 const char *name;
2849 uint8_t channel;
2850 int8_t value;
2851
2852 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S, &channel, &value);
2853 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2854
2855 name = (key == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT) ? "rssi" : "power";
2856 start += Snprintf(start, static_cast<uint32_t>(end - start), ", channel:%u, %s:%d", channel, name, value);
2857 }
2858 break;
2859
2860 case SPINEL_PROP_CAPS:
2861 {
2862 unsigned int capability;
2863
2864 start += Snprintf(start, static_cast<uint32_t>(end - start), ", caps:");
2865
2866 while (len > 0)
2867 {
2868 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &capability);
2869 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2870 data += unpacked;
2871 len -= static_cast<spinel_size_t>(unpacked);
2872 start += Snprintf(start, static_cast<uint32_t>(end - start), "%s ", spinel_capability_to_cstr(capability));
2873 }
2874 }
2875 break;
2876
2877 case SPINEL_PROP_PROTOCOL_VERSION:
2878 {
2879 unsigned int major;
2880 unsigned int minor;
2881
2882 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S,
2883 &major, &minor);
2884 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2885 start += Snprintf(start, static_cast<uint32_t>(end - start), ", major:%u, minor:%u", major, minor);
2886 }
2887 break;
2888
2889 case SPINEL_PROP_PHY_CHAN_PREFERRED:
2890 case SPINEL_PROP_PHY_CHAN_SUPPORTED:
2891 {
2892 uint8_t maskBuffer[kChannelMaskBufferSize];
2893 uint32_t channelMask = 0;
2894 const uint8_t *maskData = maskBuffer;
2895 spinel_size_t maskLength = sizeof(maskBuffer);
2896
2897 unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength);
2898 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2899
2900 while (maskLength > 0)
2901 {
2902 uint8_t channel;
2903
2904 unpacked = spinel_datatype_unpack(maskData, maskLength, SPINEL_DATATYPE_UINT8_S, &channel);
2905 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2906 VerifyOrExit(channel < kChannelMaskBufferSize, error = OT_ERROR_PARSE);
2907 channelMask |= (1UL << channel);
2908
2909 maskData += unpacked;
2910 maskLength -= static_cast<spinel_size_t>(unpacked);
2911 }
2912
2913 start += Snprintf(start, static_cast<uint32_t>(end - start), ", channelMask:0x%08x", channelMask);
2914 }
2915 break;
2916
2917 case SPINEL_PROP_NCP_VERSION:
2918 {
2919 const char *version;
2920
2921 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &version);
2922 VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
2923 start += Snprintf(start, static_cast<uint32_t>(end - start), ", version:%s", version);
2924 }
2925 break;
2926
2927 case SPINEL_PROP_STREAM_RAW:
2928 {
2929 otRadioFrame frame;
2930
2931 if (cmd == SPINEL_CMD_PROP_VALUE_IS)
2932 {
2933 uint16_t flags;
2934 int8_t noiseFloor;
2935 unsigned int receiveError;
2936
2937 unpacked = spinel_datatype_unpack(data, len,
2938 SPINEL_DATATYPE_DATA_WLEN_S // Frame
2939 SPINEL_DATATYPE_INT8_S // RSSI
2940 SPINEL_DATATYPE_INT8_S // Noise Floor
2941 SPINEL_DATATYPE_UINT16_S // Flags
2942 SPINEL_DATATYPE_STRUCT_S( // PHY-data
2943 SPINEL_DATATYPE_UINT8_S // 802.15.4 channel
2944 SPINEL_DATATYPE_UINT8_S // 802.15.4 LQI
2945 SPINEL_DATATYPE_UINT64_S // Timestamp (us).
2946 ) SPINEL_DATATYPE_STRUCT_S( // Vendor-data
2947 SPINEL_DATATYPE_UINT_PACKED_S // Receive error
2948 ),
2949 &frame.mPsdu, &frame.mLength, &frame.mInfo.mRxInfo.mRssi, &noiseFloor,
2950 &flags, &frame.mChannel, &frame.mInfo.mRxInfo.mLqi,
2951 &frame.mInfo.mRxInfo.mTimestamp, &receiveError);
2952 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2953 start += Snprintf(start, static_cast<uint32_t>(end - start), ", len:%u, rssi:%d ...", frame.mLength,
2954 frame.mInfo.mRxInfo.mRssi);
2955 otLogDebgPlat("%s", buf);
2956
2957 start = buf;
2958 start += Snprintf(start, static_cast<uint32_t>(end - start),
2959 "... noise:%d, flags:0x%04x, channel:%u, lqi:%u, timestamp:%lu, rxerr:%u", noiseFloor,
2960 flags, frame.mChannel, frame.mInfo.mRxInfo.mLqi,
2961 static_cast<unsigned long>(frame.mInfo.mRxInfo.mTimestamp), receiveError);
2962 }
2963 else if (cmd == SPINEL_CMD_PROP_VALUE_SET)
2964 {
2965 bool csmaCaEnabled;
2966 bool isHeaderUpdated;
2967 bool isARetx;
2968 bool skipAes;
2969
2970 unpacked = spinel_datatype_unpack(
2971 data, len,
2972 SPINEL_DATATYPE_DATA_WLEN_S // Frame data
2973 SPINEL_DATATYPE_UINT8_S // Channel
2974 SPINEL_DATATYPE_UINT8_S // MaxCsmaBackoffs
2975 SPINEL_DATATYPE_UINT8_S // MaxFrameRetries
2976 SPINEL_DATATYPE_BOOL_S // CsmaCaEnabled
2977 SPINEL_DATATYPE_BOOL_S // IsHeaderUpdated
2978 SPINEL_DATATYPE_BOOL_S // IsARetx
2979 SPINEL_DATATYPE_BOOL_S // SkipAes
2980 SPINEL_DATATYPE_UINT32_S // TxDelay
2981 SPINEL_DATATYPE_UINT32_S, // TxDelayBaseTime
2982 &frame.mPsdu, &frame.mLength, &frame.mChannel, &frame.mInfo.mTxInfo.mMaxCsmaBackoffs,
2983 &frame.mInfo.mTxInfo.mMaxFrameRetries, &csmaCaEnabled, &isHeaderUpdated, &isARetx, &skipAes,
2984 &frame.mInfo.mTxInfo.mTxDelay, &frame.mInfo.mTxInfo.mTxDelayBaseTime);
2985
2986 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
2987 start += Snprintf(start, static_cast<uint32_t>(end - start),
2988 ", len:%u, channel:%u, maxbackoffs:%u, maxretries:%u ...", frame.mLength, frame.mChannel,
2989 frame.mInfo.mTxInfo.mMaxCsmaBackoffs, frame.mInfo.mTxInfo.mMaxFrameRetries);
2990 otLogDebgPlat("%s", buf);
2991
2992 start = buf;
2993 start += Snprintf(start, static_cast<uint32_t>(end - start),
2994 "... csmaCaEnabled:%u, isHeaderUpdated:%u, isARetx:%u, skipAes:%u"
2995 ", txDelay:%u, txDelayBase:%u",
2996 csmaCaEnabled, isHeaderUpdated, isARetx, skipAes, frame.mInfo.mTxInfo.mTxDelay,
2997 frame.mInfo.mTxInfo.mTxDelayBaseTime);
2998 }
2999 }
3000 break;
3001
3002 case SPINEL_PROP_STREAM_DEBUG:
3003 {
3004 char debugString[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE + 1];
3005 spinel_size_t stringLength = sizeof(debugString);
3006
3007 unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, debugString, &stringLength);
3008 assert(stringLength < sizeof(debugString));
3009 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
3010 debugString[stringLength] = '\0';
3011 start += Snprintf(start, static_cast<uint32_t>(end - start), ", debug:%s", debugString);
3012 }
3013 break;
3014
3015 case SPINEL_PROP_STREAM_LOG:
3016 {
3017 const char *logString;
3018 uint8_t logLevel;
3019
3020 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &logString);
3021 VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
3022 data += unpacked;
3023 len -= static_cast<spinel_size_t>(unpacked);
3024
3025 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &logLevel);
3026 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
3027 start += Snprintf(start, static_cast<uint32_t>(end - start), ", level:%u, log:%s", logLevel, logString);
3028 }
3029 break;
3030
3031 case SPINEL_PROP_NEST_STREAM_MFG:
3032 {
3033 const char *output;
3034 size_t outputLen;
3035
3036 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &output, &outputLen);
3037 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
3038 start += Snprintf(start, static_cast<uint32_t>(end - start), ", diag:%s", output);
3039 }
3040 break;
3041
3042 case SPINEL_PROP_RCP_MAC_KEY:
3043 {
3044 uint8_t keyIdMode;
3045 uint8_t keyId;
3046 otMacKey prevKey;
3047 unsigned int prevKeyLen = sizeof(otMacKey);
3048 otMacKey currKey;
3049 unsigned int currKeyLen = sizeof(otMacKey);
3050 otMacKey nextKey;
3051 unsigned int nextKeyLen = sizeof(otMacKey);
3052
3053 unpacked = spinel_datatype_unpack(data, len,
3054 SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S
3055 SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_WLEN_S,
3056 &keyIdMode, &keyId, prevKey.m8, &prevKeyLen, currKey.m8, &currKeyLen,
3057 nextKey.m8, &nextKeyLen);
3058 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
3059 start += Snprintf(start, static_cast<uint32_t>(end - start),
3060 ", keyIdMode:%u, keyId:%u, prevKey:***, currKey:***, nextKey:***", keyIdMode, keyId);
3061 }
3062 break;
3063
3064 case SPINEL_PROP_HWADDR:
3065 case SPINEL_PROP_MAC_15_4_LADDR:
3066 {
3067 const char *name = nullptr;
3068 uint8_t m8[OT_EXT_ADDRESS_SIZE] = {0};
3069
3070 unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, &m8[0]);
3071 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
3072
3073 name = (key == SPINEL_PROP_HWADDR) ? "eui64" : "laddr";
3074 start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%02x%02x%02x%02x%02x%02x%02x%02x", name,
3075 m8[0], m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
3076 }
3077 break;
3078
3079 case SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES:
3080 {
3081 uint8_t m8[OT_EXT_ADDRESS_SIZE];
3082
3083 start += Snprintf(start, static_cast<uint32_t>(end - start), ", extaddr:");
3084
3085 if (len < sizeof(m8))
3086 {
3087 start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
3088 }
3089 else
3090 {
3091 while (len >= sizeof(m8))
3092 {
3093 unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, m8);
3094 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
3095 data += unpacked;
3096 len -= static_cast<spinel_size_t>(unpacked);
3097 start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x%02x%02x%02x%02x%02x%02x%02x ", m8[0],
3098 m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
3099 }
3100 }
3101 }
3102 break;
3103
3104 case SPINEL_PROP_RADIO_COEX_METRICS:
3105 {
3106 otRadioCoexMetrics metrics;
3107 unpacked = spinel_datatype_unpack(
3108 data, len,
3109 SPINEL_DATATYPE_STRUCT_S( // Tx Coex Metrics Structure
3110 SPINEL_DATATYPE_UINT32_S // NumTxRequest
3111 SPINEL_DATATYPE_UINT32_S // NumTxGrantImmediate
3112 SPINEL_DATATYPE_UINT32_S // NumTxGrantWait
3113 SPINEL_DATATYPE_UINT32_S // NumTxGrantWaitActivated
3114 SPINEL_DATATYPE_UINT32_S // NumTxGrantWaitTimeout
3115 SPINEL_DATATYPE_UINT32_S // NumTxGrantDeactivatedDuringRequest
3116 SPINEL_DATATYPE_UINT32_S // NumTxDelayedGrant
3117 SPINEL_DATATYPE_UINT32_S // AvgTxRequestToGrantTime
3118 ) SPINEL_DATATYPE_STRUCT_S( // Rx Coex Metrics Structure
3119 SPINEL_DATATYPE_UINT32_S // NumRxRequest
3120 SPINEL_DATATYPE_UINT32_S // NumRxGrantImmediate
3121 SPINEL_DATATYPE_UINT32_S // NumRxGrantWait
3122 SPINEL_DATATYPE_UINT32_S // NumRxGrantWaitActivated
3123 SPINEL_DATATYPE_UINT32_S // NumRxGrantWaitTimeout
3124 SPINEL_DATATYPE_UINT32_S // NumRxGrantDeactivatedDuringRequest
3125 SPINEL_DATATYPE_UINT32_S // NumRxDelayedGrant
3126 SPINEL_DATATYPE_UINT32_S // AvgRxRequestToGrantTime
3127 SPINEL_DATATYPE_UINT32_S // NumRxGrantNone
3128 ) SPINEL_DATATYPE_BOOL_S // Stopped
3129 SPINEL_DATATYPE_UINT32_S, // NumGrantGlitch
3130 &metrics.mNumTxRequest, &metrics.mNumTxGrantImmediate, &metrics.mNumTxGrantWait,
3131 &metrics.mNumTxGrantWaitActivated, &metrics.mNumTxGrantWaitTimeout,
3132 &metrics.mNumTxGrantDeactivatedDuringRequest, &metrics.mNumTxDelayedGrant,
3133 &metrics.mAvgTxRequestToGrantTime, &metrics.mNumRxRequest, &metrics.mNumRxGrantImmediate,
3134 &metrics.mNumRxGrantWait, &metrics.mNumRxGrantWaitActivated, &metrics.mNumRxGrantWaitTimeout,
3135 &metrics.mNumRxGrantDeactivatedDuringRequest, &metrics.mNumRxDelayedGrant,
3136 &metrics.mAvgRxRequestToGrantTime, &metrics.mNumRxGrantNone, &metrics.mStopped, &metrics.mNumGrantGlitch);
3137
3138 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
3139
3140 otLogDebgPlat("%s ...", buf);
3141 otLogDebgPlat(" txRequest:%lu", ToUlong(metrics.mNumTxRequest));
3142 otLogDebgPlat(" txGrantImmediate:%lu", ToUlong(metrics.mNumTxGrantImmediate));
3143 otLogDebgPlat(" txGrantWait:%lu", ToUlong(metrics.mNumTxGrantWait));
3144 otLogDebgPlat(" txGrantWaitActivated:%lu", ToUlong(metrics.mNumTxGrantWaitActivated));
3145 otLogDebgPlat(" txGrantWaitTimeout:%lu", ToUlong(metrics.mNumTxGrantWaitTimeout));
3146 otLogDebgPlat(" txGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumTxGrantDeactivatedDuringRequest));
3147 otLogDebgPlat(" txDelayedGrant:%lu", ToUlong(metrics.mNumTxDelayedGrant));
3148 otLogDebgPlat(" avgTxRequestToGrantTime:%lu", ToUlong(metrics.mAvgTxRequestToGrantTime));
3149 otLogDebgPlat(" rxRequest:%lu", ToUlong(metrics.mNumRxRequest));
3150 otLogDebgPlat(" rxGrantImmediate:%lu", ToUlong(metrics.mNumRxGrantImmediate));
3151 otLogDebgPlat(" rxGrantWait:%lu", ToUlong(metrics.mNumRxGrantWait));
3152 otLogDebgPlat(" rxGrantWaitActivated:%lu", ToUlong(metrics.mNumRxGrantWaitActivated));
3153 otLogDebgPlat(" rxGrantWaitTimeout:%lu", ToUlong(metrics.mNumRxGrantWaitTimeout));
3154 otLogDebgPlat(" rxGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumRxGrantDeactivatedDuringRequest));
3155 otLogDebgPlat(" rxDelayedGrant:%lu", ToUlong(metrics.mNumRxDelayedGrant));
3156 otLogDebgPlat(" avgRxRequestToGrantTime:%lu", ToUlong(metrics.mAvgRxRequestToGrantTime));
3157 otLogDebgPlat(" rxGrantNone:%lu", ToUlong(metrics.mNumRxGrantNone));
3158 otLogDebgPlat(" stopped:%u", metrics.mStopped);
3159
3160 start = buf;
3161 start += Snprintf(start, static_cast<uint32_t>(end - start), " grantGlitch:%u", metrics.mNumGrantGlitch);
3162 }
3163 break;
3164
3165 case SPINEL_PROP_MAC_SCAN_MASK:
3166 {
3167 constexpr uint8_t kNumChannels = 16;
3168 uint8_t channels[kNumChannels];
3169 spinel_size_t size;
3170
3171 unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_DATA_S, channels, &size);
3172 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
3173 start += Snprintf(start, static_cast<uint32_t>(end - start), ", channels:");
3174
3175 for (uint8_t i = 0; i < size; i++)
3176 {
3177 start += Snprintf(start, static_cast<uint32_t>(end - start), "%u ", channels[i]);
3178 }
3179 }
3180 break;
3181
3182 case SPINEL_PROP_RCP_ENH_ACK_PROBING:
3183 {
3184 uint16_t saddr;
3185 uint8_t m8[OT_EXT_ADDRESS_SIZE];
3186 uint8_t flags;
3187
3188 unpacked = spinel_datatype_unpack(
3189 data, len, SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S, &saddr, m8, &flags);
3190
3191 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
3192 start += Snprintf(start, static_cast<uint32_t>(end - start),
3193 ", saddr:%04x, extaddr:%02x%02x%02x%02x%02x%02x%02x%02x, flags:0x%02x", saddr, m8[0], m8[1],
3194 m8[2], m8[3], m8[4], m8[5], m8[6], m8[7], flags);
3195 }
3196 break;
3197
3198 case SPINEL_PROP_PHY_CALIBRATED_POWER:
3199 {
3200 if (cmd == SPINEL_CMD_PROP_VALUE_INSERT)
3201 {
3202 uint8_t channel;
3203 int16_t actualPower;
3204 uint8_t *rawPowerSetting;
3205 unsigned int rawPowerSettingLength;
3206
3207 unpacked = spinel_datatype_unpack(
3208 data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_DATA_WLEN_S, &channel,
3209 &actualPower, &rawPowerSetting, &rawPowerSettingLength);
3210 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
3211
3212 start += Snprintf(start, static_cast<uint32_t>(end - start),
3213 ", ch:%u, actualPower:%d, rawPowerSetting:", channel, actualPower);
3214 for (uint16_t i = 0; i < rawPowerSettingLength; i++)
3215 {
3216 start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x", rawPowerSetting[i]);
3217 }
3218 }
3219 }
3220 break;
3221
3222 case SPINEL_PROP_PHY_CHAN_TARGET_POWER:
3223 {
3224 uint8_t channel;
3225 int16_t targetPower;
3226
3227 unpacked =
3228 spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, &channel, &targetPower);
3229 VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
3230 start += Snprintf(start, static_cast<uint32_t>(end - start), ", ch:%u, targetPower:%d", channel, targetPower);
3231 }
3232 break;
3233 }
3234
3235 exit:
3236 if (error == OT_ERROR_NONE)
3237 {
3238 otLogDebgPlat("%s", buf);
3239 }
3240 else
3241 {
3242 otLogDebgPlat("%s, failed to parse spinel frame !", prefix);
3243 }
3244
3245 return;
3246 }
3247
3248 } // namespace Spinel
3249 } // namespace ot
3250