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