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