1 /*
2 * Copyright (c) 2016-2017, 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" AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /**
29 * @file
30 * This file implements general thread device required Spinel interface to the OpenThread stack.
31 */
32
33 #include "ncp_base.hpp"
34
35 #include <stdarg.h>
36 #include <stdlib.h>
37
38 #include <openthread/diag.h>
39 #include <openthread/icmp6.h>
40 #include <openthread/link.h>
41 #include <openthread/logging.h>
42 #include <openthread/ncp.h>
43 #include <openthread/network_time.h>
44 #include <openthread/platform/misc.h>
45 #include <openthread/platform/radio.h>
46
47 #include "common/code_utils.hpp"
48 #include "common/debug.hpp"
49 #include "radio/radio.hpp"
50
51 namespace ot {
52 namespace Ncp {
53
54 // ----------------------------------------------------------------------------
55 // MARK: Utility Functions
56 // ----------------------------------------------------------------------------
57
InstanceToIid(Instance * aInstance)58 uint8_t NcpBase::InstanceToIid(Instance *aInstance)
59 {
60 uint8_t index = 0;
61
62 OT_UNUSED_VARIABLE(aInstance);
63
64 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
65 index = SPINEL_HEADER_GET_IID(SPINEL_HEADER_IID_BROADCAST); // use broadcast if no match
66
67 for (int i = 0; i < kSpinelInterfaceCount; i++)
68 {
69 if (aInstance == mInstances[i])
70 {
71 index = i;
72 break;
73 }
74 }
75 #endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
76
77 return index;
78 }
79
IidToInstance(uint8_t aIid)80 Instance *NcpBase::IidToInstance(uint8_t aIid)
81 {
82 Instance *instance;
83 OT_ASSERT(aIid < kSpinelInterfaceCount);
84
85 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
86 instance = mInstances[aIid];
87 #else
88 OT_UNUSED_VARIABLE(aIid);
89 instance = mInstance;
90 #endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
91
92 return instance;
93 }
94
95 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
HasOnly1BitSet(uint32_t aValue)96 static bool HasOnly1BitSet(uint32_t aValue) { return aValue != 0 && ((aValue & (aValue - 1)) == 0); }
97
IndexOfMSB(uint32_t aValue)98 static uint8_t IndexOfMSB(uint32_t aValue)
99 {
100 uint8_t index = 0;
101
102 while (aValue >>= 1)
103 {
104 index++;
105 }
106
107 return index;
108 }
109 #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
110
ThreadErrorToSpinelStatus(otError aError)111 spinel_status_t NcpBase::ThreadErrorToSpinelStatus(otError aError)
112 {
113 spinel_status_t ret;
114
115 switch (aError)
116 {
117 case OT_ERROR_NONE:
118 ret = SPINEL_STATUS_OK;
119 break;
120
121 case OT_ERROR_FAILED:
122 ret = SPINEL_STATUS_FAILURE;
123 break;
124
125 case OT_ERROR_DROP:
126 ret = SPINEL_STATUS_DROPPED;
127 break;
128
129 case OT_ERROR_NO_BUFS:
130 ret = SPINEL_STATUS_NOMEM;
131 break;
132
133 case OT_ERROR_BUSY:
134 ret = SPINEL_STATUS_BUSY;
135 break;
136
137 case OT_ERROR_PARSE:
138 ret = SPINEL_STATUS_PARSE_ERROR;
139 break;
140
141 case OT_ERROR_INVALID_ARGS:
142 ret = SPINEL_STATUS_INVALID_ARGUMENT;
143 break;
144
145 case OT_ERROR_NOT_IMPLEMENTED:
146 ret = SPINEL_STATUS_UNIMPLEMENTED;
147 break;
148
149 case OT_ERROR_INVALID_STATE:
150 ret = SPINEL_STATUS_INVALID_STATE;
151 break;
152
153 case OT_ERROR_NO_ACK:
154 ret = SPINEL_STATUS_NO_ACK;
155 break;
156
157 case OT_ERROR_CHANNEL_ACCESS_FAILURE:
158 ret = SPINEL_STATUS_CCA_FAILURE;
159 break;
160
161 case OT_ERROR_ALREADY:
162 ret = SPINEL_STATUS_ALREADY;
163 break;
164
165 case OT_ERROR_NOT_FOUND:
166 ret = SPINEL_STATUS_ITEM_NOT_FOUND;
167 break;
168
169 case OT_ERROR_UNKNOWN_NEIGHBOR:
170 ret = SPINEL_STATUS_UNKNOWN_NEIGHBOR;
171 break;
172
173 case OT_ERROR_NOT_CAPABLE:
174 ret = SPINEL_STATUS_NOT_CAPABLE;
175 break;
176
177 case OT_ERROR_RESPONSE_TIMEOUT:
178 ret = SPINEL_STATUS_RESPONSE_TIMEOUT;
179 break;
180
181 default:
182 // Unknown error code. Wrap it as a Spinel status and return that.
183 ret = static_cast<spinel_status_t>(SPINEL_STATUS_STACK_NATIVE__BEGIN + static_cast<uint32_t>(aError));
184 break;
185 }
186
187 return ret;
188 }
189
ResetReasonToSpinelStatus(otPlatResetReason aReason)190 static spinel_status_t ResetReasonToSpinelStatus(otPlatResetReason aReason)
191 {
192 spinel_status_t ret;
193
194 switch (aReason)
195 {
196 case OT_PLAT_RESET_REASON_POWER_ON:
197 ret = SPINEL_STATUS_RESET_POWER_ON;
198 break;
199
200 case OT_PLAT_RESET_REASON_EXTERNAL:
201 ret = SPINEL_STATUS_RESET_EXTERNAL;
202 break;
203
204 case OT_PLAT_RESET_REASON_SOFTWARE:
205 ret = SPINEL_STATUS_RESET_SOFTWARE;
206 break;
207
208 case OT_PLAT_RESET_REASON_FAULT:
209 ret = SPINEL_STATUS_RESET_FAULT;
210 break;
211
212 case OT_PLAT_RESET_REASON_CRASH:
213 ret = SPINEL_STATUS_RESET_CRASH;
214 break;
215
216 case OT_PLAT_RESET_REASON_ASSERT:
217 ret = SPINEL_STATUS_RESET_ASSERT;
218 break;
219
220 case OT_PLAT_RESET_REASON_WATCHDOG:
221 ret = SPINEL_STATUS_RESET_WATCHDOG;
222 break;
223
224 case OT_PLAT_RESET_REASON_OTHER:
225 ret = SPINEL_STATUS_RESET_OTHER;
226 break;
227
228 default:
229 ret = SPINEL_STATUS_RESET_UNKNOWN;
230 break;
231 }
232
233 return ret;
234 }
235
236 // ----------------------------------------------------------------------------
237 // MARK: Class Boilerplate
238 // ----------------------------------------------------------------------------
239
240 NcpBase *NcpBase::sNcpInstance = nullptr;
241
242 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
NcpBase(Instance ** aInstances,uint8_t aCount)243 NcpBase::NcpBase(Instance **aInstances, uint8_t aCount)
244 : NcpBase(aInstances[0])
245 {
246 OT_ASSERT(aCount > 0);
247 OT_ASSERT(aCount < SPINEL_HEADER_IID_MAX); // One IID reserved for broadcast
248
249 uint8_t skipped = 0;
250
251 for (int i = 0; i < aCount; i++)
252 {
253 if ((i + skipped) == SPINEL_HEADER_GET_IID(SPINEL_HEADER_IID_BROADCAST))
254 {
255 mInstances[i + skipped] = nullptr;
256 skipped++;
257 }
258
259 OT_ASSERT(i + skipped <= SPINEL_HEADER_IID_MAX);
260 mInstances[i + skipped] = aInstances[i];
261 }
262 }
263 #endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
264
NcpBase(Instance * aInstance)265 NcpBase::NcpBase(Instance *aInstance)
266 : mInstance(aInstance)
267 , mTxFrameBuffer(mTxBuffer, sizeof(mTxBuffer))
268 , mEncoder(mTxFrameBuffer)
269 , mHostPowerStateInProgress(false)
270 , mLastStatus(SPINEL_STATUS_OK)
271 , mScanChannelMask(Radio::kSupportedChannels)
272 , mScanPeriod(200)
273 , mDiscoveryScanJoinerFlag(false)
274 , mDiscoveryScanEnableFiltering(false)
275 , mDiscoveryScanPanId(0xffff)
276 , mUpdateChangedPropsTask(*aInstance, NcpBase::UpdateChangedProps)
277 , mThreadChangedFlags(0)
278 , mHostPowerState(SPINEL_HOST_POWER_STATE_ONLINE)
279 , mHostPowerReplyFrameTag(Spinel::Buffer::kInvalidTag)
280 , mHostPowerStateHeader(0)
281 #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
282 , mAllowPeekDelegate(nullptr)
283 , mAllowPokeDelegate(nullptr)
284 #endif
285 , mResponseQueueHead(0)
286 , mResponseQueueTail(0)
287 , mAllowLocalNetworkDataChange(false)
288 , mRequireJoinExistingNetwork(false)
289 , mPcapEnabled(false)
290 , mDisableStreamWrite(false)
291 , mShouldEmitChildTableUpdate(false)
292 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
293 , mAllowLocalServerDataChange(false)
294 #endif
295 #if OPENTHREAD_FTD
296 , mPreferredRouteId(0)
297 #endif
298 , mCurCommandIid(0)
299 #if OPENTHREAD_MTD || OPENTHREAD_FTD
300 , mInboundSecureIpFrameCounter(0)
301 , mInboundInsecureIpFrameCounter(0)
302 , mOutboundSecureIpFrameCounter(0)
303 , mOutboundInsecureIpFrameCounter(0)
304 , mDroppedOutboundIpFrameCounter(0)
305 , mDroppedInboundIpFrameCounter(0)
306 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
307 , mSrpClientCallbackEnabled(false)
308 #endif
309 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
310 , mFramingErrorCounter(0)
311 , mRxSpinelFrameCounter(0)
312 , mRxSpinelOutOfOrderTidCounter(0)
313 , mTxSpinelFrameCounter(0)
314 , mDidInitialUpdates(false)
315 , mLogTimestampBase(0)
316 {
317 OT_ASSERT(mInstance != nullptr);
318
319 sNcpInstance = this;
320
321 mTxFrameBuffer.SetFrameRemovedCallback(&NcpBase::HandleFrameRemovedFromNcpBuffer, this);
322
323 memset(&mResponseQueue, 0, sizeof(mResponseQueue));
324 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
325 memset(mCurTransmitTID, 0, sizeof(mCurTransmitTID));
326 memset(mSrcMatchEnabled, 0, sizeof(mSrcMatchEnabled));
327 memset(mCurScanChannel, kInvalidScanChannel, sizeof(mCurScanChannel));
328 #endif
329 memset(mIsRawStreamEnabled, 0, sizeof(mIsRawStreamEnabled));
330 memset(mNextExpectedTid, 0, sizeof(mNextExpectedTid));
331
332 #if OPENTHREAD_MTD || OPENTHREAD_FTD
333 otMessageQueueInit(&mMessageQueue);
334 IgnoreError(otSetStateChangedCallback(mInstance, &NcpBase::HandleStateChanged, this));
335 otIp6SetReceiveCallback(mInstance, &NcpBase::HandleDatagramFromStack, this);
336 otIp6SetReceiveFilterEnabled(mInstance, true);
337 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
338 otNetworkTimeSyncSetCallback(mInstance, &NcpBase::HandleTimeSyncUpdate, this);
339 #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
340 #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE
341 otUdpForwardSetForwarder(mInstance, &NcpBase::HandleUdpForwardStream, this);
342 #endif
343 otIcmp6SetEchoMode(mInstance, OT_ICMP6_ECHO_HANDLER_RLOC_ALOC_ONLY);
344 #if OPENTHREAD_FTD
345 otThreadRegisterNeighborTableCallback(mInstance, &NcpBase::HandleNeighborTableChanged);
346 #if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE
347 memset(&mSteeringDataAddress, 0, sizeof(mSteeringDataAddress));
348 #endif
349 #if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE
350 otThreadRegisterParentResponseCallback(mInstance, &NcpBase::HandleParentResponseInfo, static_cast<void *>(this));
351 #endif
352 #endif // OPENTHREAD_FTD
353 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
354 otSrpClientSetCallback(mInstance, HandleSrpClientCallback, this);
355 #endif
356 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
357 mChangedPropsSet.AddLastStatus(SPINEL_STATUS_RESET_UNKNOWN);
358 mUpdateChangedPropsTask.Post();
359
360 #if OPENTHREAD_ENABLE_VENDOR_EXTENSION
361 aInstance->Get<Extension::ExtensionBase>().SignalNcpInit(*this);
362 #endif
363 }
364
GetNcpInstance(void)365 NcpBase *NcpBase::GetNcpInstance(void) { return sNcpInstance; }
366
GetCurCommandIid(void) const367 spinel_iid_t NcpBase::GetCurCommandIid(void) const { return mCurCommandIid; }
368
ResetCounters(void)369 void NcpBase::ResetCounters(void)
370 {
371 mFramingErrorCounter = 0;
372 mRxSpinelFrameCounter = 0;
373 mRxSpinelOutOfOrderTidCounter = 0;
374 mTxSpinelFrameCounter = 0;
375
376 #if OPENTHREAD_MTD || OPENTHREAD_FTD
377 mInboundSecureIpFrameCounter = 0;
378 mInboundInsecureIpFrameCounter = 0;
379 mOutboundSecureIpFrameCounter = 0;
380 mOutboundInsecureIpFrameCounter = 0;
381 mDroppedOutboundIpFrameCounter = 0;
382 mDroppedInboundIpFrameCounter = 0;
383 #endif
384 }
385
386 // ----------------------------------------------------------------------------
387 // MARK: Serial Traffic Glue
388 // ----------------------------------------------------------------------------
389
GetLastOutboundFrameTag(void)390 Spinel::Buffer::FrameTag NcpBase::GetLastOutboundFrameTag(void) { return mTxFrameBuffer.InFrameGetLastTag(); }
391
HandleReceive(const uint8_t * aBuf,uint16_t aBufLength)392 void NcpBase::HandleReceive(const uint8_t *aBuf, uint16_t aBufLength)
393 {
394 otError error = OT_ERROR_NONE;
395 uint8_t header = 0;
396 spinel_tid_t tid = 0;
397
398 mDisableStreamWrite = true;
399
400 // Initialize the decoder with the newly received spinel frame.
401 mDecoder.Init(aBuf, aBufLength);
402
403 // Receiving any message from the host has the side effect of transitioning the host power state to online.
404 mHostPowerState = SPINEL_HOST_POWER_STATE_ONLINE;
405 mHostPowerStateInProgress = false;
406
407 // Skip if there is no header byte to read or this isn't a spinel frame.
408
409 SuccessOrExit(mDecoder.ReadUint8(header));
410 VerifyOrExit((SPINEL_HEADER_FLAG & header) == SPINEL_HEADER_FLAG);
411
412 mRxSpinelFrameCounter++;
413
414 mCurCommandIid = SPINEL_HEADER_GET_IID(header);
415
416 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
417 if (mCurCommandIid > SPINEL_HEADER_IID_MAX)
418 #else
419 if (mCurCommandIid != 0)
420 #endif
421 {
422 IgnoreError(WriteLastStatusFrame(header, SPINEL_STATUS_INVALID_INTERFACE));
423 ExitNow();
424 }
425
426 mInstance = IidToInstance(mCurCommandIid);
427 if (mInstance == nullptr)
428 {
429 IgnoreError(WriteLastStatusFrame(header, SPINEL_STATUS_INVALID_INTERFACE));
430 ExitNow();
431 }
432
433 error = HandleCommand(header);
434
435 if (error != OT_ERROR_NONE)
436 {
437 IgnoreError(PrepareLastStatusResponse(header, ThreadErrorToSpinelStatus(error)));
438 }
439
440 if (!IsResponseQueueEmpty())
441 {
442 // A response may have been prepared and queued for this command,
443 // so we attempt to send/write any queued responses. Note that
444 // if the response was prepared but cannot be sent now (not
445 // enough buffer space available), it will be attempted again
446 // from `HandleFrameRemovedFromNcpBuffer()` when buffer space
447 // becomes available.
448
449 IgnoreError(SendQueuedResponses());
450 }
451
452 // Check for out of sequence TIDs and update `mNextExpectedTid`,
453
454 tid = SPINEL_HEADER_GET_TID(header);
455
456 if ((mNextExpectedTid[mCurCommandIid] != 0) && (tid != mNextExpectedTid[mCurCommandIid]))
457 {
458 mRxSpinelOutOfOrderTidCounter++;
459 }
460
461 mNextExpectedTid[mCurCommandIid] = SPINEL_GET_NEXT_TID(tid);
462
463 exit:
464 mDisableStreamWrite = false;
465 }
466
HandleFrameRemovedFromNcpBuffer(void * aContext,Spinel::Buffer::FrameTag aFrameTag,Spinel::Buffer::Priority aPriority,Spinel::Buffer * aNcpBuffer)467 void NcpBase::HandleFrameRemovedFromNcpBuffer(void *aContext,
468 Spinel::Buffer::FrameTag aFrameTag,
469 Spinel::Buffer::Priority aPriority,
470 Spinel::Buffer *aNcpBuffer)
471 {
472 OT_UNUSED_VARIABLE(aNcpBuffer);
473 OT_UNUSED_VARIABLE(aPriority);
474
475 static_cast<NcpBase *>(aContext)->HandleFrameRemovedFromNcpBuffer(aFrameTag);
476 }
477
HandleFrameRemovedFromNcpBuffer(Spinel::Buffer::FrameTag aFrameTag)478 void NcpBase::HandleFrameRemovedFromNcpBuffer(Spinel::Buffer::FrameTag aFrameTag)
479 {
480 if (mHostPowerStateInProgress)
481 {
482 if (aFrameTag == mHostPowerReplyFrameTag)
483 {
484 mHostPowerStateInProgress = false;
485 }
486 }
487
488 // A frame was removed from NCP TX buffer, so more space is now available.
489 // We attempt to write/send any pending frames. Order of the checks
490 // below is important: First any queued command responses, then
491 // any queued IPv6 datagram messages, then any asynchronous property updates.
492 // If a frame still can not fit in the available buffer, we exit immediately
493 // and wait for next time this callback is invoked (when another frame is
494 // removed and more buffer space becomes available).
495
496 SuccessOrExit(SendQueuedResponses());
497
498 #if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK
499 VendorHandleFrameRemovedFromNcpBuffer(aFrameTag);
500 #endif
501
502 // Check if `HOST_POWER_STATE` property update is required.
503
504 if (mHostPowerStateHeader)
505 {
506 SuccessOrExit(WritePropertyValueIsFrame(mHostPowerStateHeader, SPINEL_PROP_HOST_POWER_STATE));
507
508 mHostPowerStateHeader = 0;
509
510 if (mHostPowerState != SPINEL_HOST_POWER_STATE_ONLINE)
511 {
512 mHostPowerReplyFrameTag = GetLastOutboundFrameTag();
513 mHostPowerStateInProgress = true;
514 }
515 }
516
517 #if OPENTHREAD_MTD || OPENTHREAD_FTD
518
519 // Send any queued IPv6 datagram message.
520
521 SuccessOrExit(SendQueuedDatagramMessages());
522 #endif
523
524 // Send any unsolicited event-triggered property updates.
525
526 UpdateChangedProps();
527
528 exit:
529 return;
530 }
531
ShouldWakeHost(void)532 bool NcpBase::ShouldWakeHost(void)
533 {
534 return (mHostPowerState != SPINEL_HOST_POWER_STATE_ONLINE && !mHostPowerStateInProgress);
535 }
536
ShouldDeferHostSend(void)537 bool NcpBase::ShouldDeferHostSend(void)
538 {
539 return (mHostPowerState == SPINEL_HOST_POWER_STATE_DEEP_SLEEP && !mHostPowerStateInProgress);
540 }
541
IncrementFrameErrorCounter(void)542 void NcpBase::IncrementFrameErrorCounter(void) { mFramingErrorCounter++; }
543
StreamWrite(int aStreamId,const uint8_t * aDataPtr,int aDataLen)544 otError NcpBase::StreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen)
545 {
546 otError error = OT_ERROR_NONE;
547 uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID;
548 spinel_prop_key_t streamPropKey;
549
550 if (aStreamId == 0)
551 {
552 streamPropKey = SPINEL_PROP_STREAM_DEBUG;
553 }
554 else
555 {
556 streamPropKey = static_cast<spinel_prop_key_t>(aStreamId);
557 }
558
559 VerifyOrExit(!mDisableStreamWrite, error = OT_ERROR_INVALID_STATE);
560 VerifyOrExit(!mChangedPropsSet.IsPropertyFiltered(streamPropKey), error = OT_ERROR_INVALID_STATE);
561
562 // If there is a pending queued response we do not allow any new log
563 // stream writes. This is to ensure that log messages can not continue
564 // to use the NCP buffer space and block other spinel frames.
565
566 VerifyOrExit(IsResponseQueueEmpty(), error = OT_ERROR_NO_BUFS);
567
568 SuccessOrExit(error = mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, streamPropKey));
569 SuccessOrExit(error = mEncoder.WriteData(aDataPtr, static_cast<uint16_t>(aDataLen)));
570 SuccessOrExit(error = mEncoder.EndFrame());
571
572 exit:
573
574 if (error == OT_ERROR_NO_BUFS)
575 {
576 mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM);
577 mUpdateChangedPropsTask.Post();
578 }
579
580 return error;
581 }
582
ConvertLogLevel(otLogLevel aLogLevel)583 uint8_t NcpBase::ConvertLogLevel(otLogLevel aLogLevel)
584 {
585 uint8_t spinelLogLevel = SPINEL_NCP_LOG_LEVEL_EMERG;
586
587 switch (aLogLevel)
588 {
589 case OT_LOG_LEVEL_NONE:
590 spinelLogLevel = SPINEL_NCP_LOG_LEVEL_EMERG;
591 break;
592
593 case OT_LOG_LEVEL_CRIT:
594 spinelLogLevel = SPINEL_NCP_LOG_LEVEL_CRIT;
595 break;
596
597 case OT_LOG_LEVEL_WARN:
598 spinelLogLevel = SPINEL_NCP_LOG_LEVEL_WARN;
599 break;
600
601 case OT_LOG_LEVEL_NOTE:
602 spinelLogLevel = SPINEL_NCP_LOG_LEVEL_NOTICE;
603 break;
604
605 case OT_LOG_LEVEL_INFO:
606 spinelLogLevel = SPINEL_NCP_LOG_LEVEL_INFO;
607 break;
608
609 case OT_LOG_LEVEL_DEBG:
610 spinelLogLevel = SPINEL_NCP_LOG_LEVEL_DEBUG;
611 break;
612 }
613
614 return spinelLogLevel;
615 }
616
ConvertLogRegion(otLogRegion aLogRegion)617 unsigned int NcpBase::ConvertLogRegion(otLogRegion aLogRegion)
618 {
619 unsigned int spinelLogRegion = SPINEL_NCP_LOG_REGION_NONE;
620
621 switch (aLogRegion)
622 {
623 case OT_LOG_REGION_API:
624 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_API;
625 break;
626
627 case OT_LOG_REGION_MLE:
628 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_MLE;
629 break;
630
631 case OT_LOG_REGION_ARP:
632 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_ARP;
633 break;
634
635 case OT_LOG_REGION_NET_DATA:
636 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_NET_DATA;
637 break;
638
639 case OT_LOG_REGION_ICMP:
640 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_ICMP;
641 break;
642
643 case OT_LOG_REGION_IP6:
644 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_IP6;
645 break;
646
647 case OT_LOG_REGION_TCP:
648 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_TCP;
649 break;
650
651 case OT_LOG_REGION_MAC:
652 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_MAC;
653 break;
654
655 case OT_LOG_REGION_MEM:
656 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_MEM;
657 break;
658
659 case OT_LOG_REGION_NCP:
660 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_NCP;
661 break;
662
663 case OT_LOG_REGION_MESH_COP:
664 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_MESH_COP;
665 break;
666
667 case OT_LOG_REGION_NET_DIAG:
668 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_NET_DIAG;
669 break;
670
671 case OT_LOG_REGION_PLATFORM:
672 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_PLATFORM;
673 break;
674
675 case OT_LOG_REGION_COAP:
676 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_COAP;
677 break;
678
679 case OT_LOG_REGION_CLI:
680 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_CLI;
681 break;
682
683 case OT_LOG_REGION_CORE:
684 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_CORE;
685 break;
686
687 case OT_LOG_REGION_UTIL:
688 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_UTIL;
689 break;
690
691 case OT_LOG_REGION_BBR:
692 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_BBR;
693 break;
694
695 case OT_LOG_REGION_MLR:
696 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_MLR;
697 break;
698
699 case OT_LOG_REGION_DUA:
700 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_DUA;
701 break;
702
703 case OT_LOG_REGION_BR:
704 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_BR;
705 break;
706
707 case OT_LOG_REGION_SRP:
708 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_SRP;
709 break;
710
711 case OT_LOG_REGION_DNS:
712 spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_DNS;
713 break;
714 }
715
716 return spinelLogRegion;
717 }
718
Log(otLogLevel aLogLevel,otLogRegion aLogRegion,const char * aLogString)719 void NcpBase::Log(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogString)
720 {
721 otError error = OT_ERROR_NONE;
722 uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID;
723
724 VerifyOrExit(!mDisableStreamWrite, error = OT_ERROR_INVALID_STATE);
725 VerifyOrExit(!mChangedPropsSet.IsPropertyFiltered(SPINEL_PROP_STREAM_LOG));
726
727 // If there is a pending queued response we do not allow any new log
728 // stream writes. This is to ensure that log messages can not continue
729 // to use the NCP buffer space and block other spinel frames.
730
731 VerifyOrExit(IsResponseQueueEmpty(), error = OT_ERROR_NO_BUFS);
732
733 SuccessOrExit(error = mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_STREAM_LOG));
734 SuccessOrExit(error = mEncoder.WriteUtf8(aLogString));
735 SuccessOrExit(error = mEncoder.WriteUint8(ConvertLogLevel(aLogLevel)));
736 SuccessOrExit(error = mEncoder.WriteUintPacked(ConvertLogRegion(aLogRegion)));
737 SuccessOrExit(error = mEncoder.WriteUint64(mLogTimestampBase + otPlatAlarmMilliGetNow()));
738 SuccessOrExit(error = mEncoder.EndFrame());
739
740 exit:
741
742 if (error == OT_ERROR_NO_BUFS)
743 {
744 mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM);
745 mUpdateChangedPropsTask.Post();
746 }
747 }
748
749 #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
750
RegisterPeekPokeDelegates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate,otNcpDelegateAllowPeekPoke aAllowPokeDelegate)751 void NcpBase::RegisterPeekPokeDelegates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate,
752 otNcpDelegateAllowPeekPoke aAllowPokeDelegate)
753 {
754 mAllowPeekDelegate = aAllowPeekDelegate;
755 mAllowPokeDelegate = aAllowPokeDelegate;
756 }
757
758 #endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
759
760 // ----------------------------------------------------------------------------
761 // MARK: Spinel Response Handling
762 // ----------------------------------------------------------------------------
763
GetWrappedResponseQueueIndex(uint8_t aPosition)764 uint8_t NcpBase::GetWrappedResponseQueueIndex(uint8_t aPosition)
765 {
766 while (aPosition >= kResponseQueueSize)
767 {
768 aPosition -= kResponseQueueSize;
769 }
770
771 return aPosition;
772 }
773
EnqueueResponse(uint8_t aHeader,ResponseType aType,unsigned int aPropKeyOrStatus)774 otError NcpBase::EnqueueResponse(uint8_t aHeader, ResponseType aType, unsigned int aPropKeyOrStatus)
775 {
776 otError error = OT_ERROR_NONE;
777 spinel_iid_t iid = SPINEL_HEADER_GET_IID(aHeader);
778 spinel_tid_t tid = SPINEL_HEADER_GET_TID(aHeader);
779 ResponseEntry *entry;
780
781 if (tid == 0)
782 {
783 // No response is required for TID zero. But we may emit a
784 // `LAST_STATUS` error status (if not filtered) for TID
785 // zero (e.g., for a dropped `STREAM_NET` set command).
786
787 if (aType == kResponseTypeLastStatus)
788 {
789 mChangedPropsSet.AddLastStatus(static_cast<spinel_status_t>(aPropKeyOrStatus));
790 }
791
792 ExitNow();
793 }
794
795 if ((mResponseQueueTail - mResponseQueueHead) >= kResponseQueueSize)
796 {
797 // If there is no room a for a response, emit an unsolicited
798 // `DROPPED` error status to indicate a spinel response was
799 // dropped.
800
801 mChangedPropsSet.AddLastStatus(SPINEL_STATUS_DROPPED);
802
803 ExitNow(error = OT_ERROR_NO_BUFS);
804 }
805
806 // Transaction IDs are expected to come in sequence, if however, we
807 // get an out of sequence TID, check if we already have a response
808 // queued for this TID and if so mark the old entry as deleted.
809
810 if (tid != mNextExpectedTid[iid])
811 {
812 for (uint8_t cur = mResponseQueueHead; cur < mResponseQueueTail; cur++)
813 {
814 entry = &mResponseQueue[GetWrappedResponseQueueIndex(cur)];
815
816 if (entry->mIsInUse && (entry->mIid == iid) && (entry->mTid == tid))
817 {
818 // Entry is just marked here and will be removed
819 // from `SendQueuedResponses()`.
820
821 entry->mIsInUse = false;
822 break;
823 }
824 }
825 }
826
827 // Add the new entry in the queue at tail.
828
829 entry = &mResponseQueue[GetWrappedResponseQueueIndex(mResponseQueueTail)];
830
831 entry->mIid = iid;
832 entry->mTid = tid;
833 entry->mIsInUse = true;
834 entry->mType = aType;
835 entry->mPropKeyOrStatus = aPropKeyOrStatus;
836
837 mResponseQueueTail++;
838
839 exit:
840 return error;
841 }
842
SendQueuedResponses(void)843 otError NcpBase::SendQueuedResponses(void)
844 {
845 otError error = OT_ERROR_NONE;
846
847 while (mResponseQueueHead != mResponseQueueTail)
848 {
849 ResponseEntry &entry = mResponseQueue[mResponseQueueHead];
850
851 if (entry.mIsInUse)
852 {
853 uint8_t header = SPINEL_HEADER_FLAG;
854 header |= SPINEL_HEADER_IID(entry.mIid);
855 header |= static_cast<uint8_t>(entry.mTid << SPINEL_HEADER_TID_SHIFT);
856
857 if (entry.mType == kResponseTypeLastStatus)
858 {
859 spinel_status_t status = static_cast<spinel_status_t>(entry.mPropKeyOrStatus);
860
861 SuccessOrExit(error = WriteLastStatusFrame(header, status));
862 }
863 else
864 {
865 spinel_prop_key_t propKey = static_cast<spinel_prop_key_t>(entry.mPropKeyOrStatus);
866 bool isGetResponse = (entry.mType == kResponseTypeGet);
867
868 SuccessOrExit(error = WritePropertyValueIsFrame(header, propKey, isGetResponse));
869 }
870 }
871
872 // Remove the response entry.
873
874 entry.mIsInUse = false;
875
876 mResponseQueueHead++;
877
878 if (mResponseQueueHead == kResponseQueueSize)
879 {
880 // Only when `head` wraps, the `tail` will be wrapped as well.
881 //
882 // This ensures that `tail` is always bigger than `head` and
883 // `(tail - head)` to correctly give the number of items in
884 // the queue.
885
886 mResponseQueueHead = 0;
887 mResponseQueueTail = GetWrappedResponseQueueIndex(mResponseQueueTail);
888 }
889 }
890
891 exit:
892 return error;
893 }
894
895 // ----------------------------------------------------------------------------
896 // MARK: Property/Status Changed
897 // ----------------------------------------------------------------------------
898
UpdateChangedProps(Tasklet & aTasklet)899 void NcpBase::UpdateChangedProps(Tasklet &aTasklet)
900 {
901 OT_UNUSED_VARIABLE(aTasklet);
902 GetNcpInstance()->UpdateChangedProps();
903 }
904
UpdateChangedProps(void)905 void NcpBase::UpdateChangedProps(void)
906 {
907 uint8_t numEntries;
908 spinel_prop_key_t propKey;
909 const ChangedPropsSet::Entry *entry;
910
911 #if OPENTHREAD_MTD || OPENTHREAD_FTD
912 ProcessThreadChangedFlags();
913 #endif
914
915 VerifyOrExit(!mChangedPropsSet.IsEmpty());
916
917 entry = mChangedPropsSet.GetSupportedEntries(numEntries);
918
919 for (uint8_t index = 0; index < numEntries; index++, entry++)
920 {
921 if (!mChangedPropsSet.IsEntryChanged(index))
922 {
923 continue;
924 }
925
926 propKey = entry->mPropKey;
927
928 if (propKey == SPINEL_PROP_LAST_STATUS)
929 {
930 spinel_status_t status = entry->mStatus;
931
932 if (status == SPINEL_STATUS_RESET_UNKNOWN)
933 {
934 status = ResetReasonToSpinelStatus(otPlatGetResetReason(mInstance));
935 }
936
937 SuccessOrExit(WriteLastStatusFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID, status));
938 }
939 else if (mDidInitialUpdates)
940 {
941 SuccessOrExit(WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID, propKey));
942 }
943
944 mChangedPropsSet.RemoveEntry(index);
945 VerifyOrExit(!mChangedPropsSet.IsEmpty());
946 }
947
948 exit:
949 mDidInitialUpdates = true;
950 }
951
952 // ----------------------------------------------------------------------------
953 // MARK: Inbound Command Handler
954 // ----------------------------------------------------------------------------
955
HandleCommand(uint8_t aHeader)956 otError NcpBase::HandleCommand(uint8_t aHeader)
957 {
958 otError error = OT_ERROR_NONE;
959 unsigned int command;
960
961 SuccessOrExit(error = mDecoder.ReadUintPacked(command));
962
963 switch (command)
964 {
965 case SPINEL_CMD_NOOP:
966 error = CommandHandler_NOOP(aHeader);
967 break;
968
969 case SPINEL_CMD_RESET:
970 error = CommandHandler_RESET(aHeader);
971 break;
972
973 case SPINEL_CMD_PROP_VALUE_GET:
974 case SPINEL_CMD_PROP_VALUE_SET:
975 case SPINEL_CMD_PROP_VALUE_INSERT:
976 case SPINEL_CMD_PROP_VALUE_REMOVE:
977 error = CommandHandler_PROP_VALUE_update(aHeader, command);
978 break;
979
980 #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
981 case SPINEL_CMD_PEEK:
982 error = CommandHandler_PEEK(aHeader);
983 break;
984
985 case SPINEL_CMD_POKE:
986 error = CommandHandler_POKE(aHeader);
987 break;
988 #endif
989
990 #if OPENTHREAD_MTD || OPENTHREAD_FTD
991 case SPINEL_CMD_NET_SAVE:
992 case SPINEL_CMD_NET_RECALL:
993 error = OT_ERROR_NOT_IMPLEMENTED;
994 break;
995
996 case SPINEL_CMD_NET_CLEAR:
997 error = CommandHandler_NET_CLEAR(aHeader);
998 break;
999 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
1000
1001 default:
1002
1003 #if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK
1004 if (command >= SPINEL_CMD_VENDOR__BEGIN && command < SPINEL_CMD_VENDOR__END)
1005 {
1006 error = VendorCommandHandler(aHeader, command);
1007 break;
1008 }
1009 #endif
1010
1011 error = PrepareLastStatusResponse(aHeader, SPINEL_STATUS_INVALID_COMMAND);
1012 break;
1013 }
1014
1015 exit:
1016 return error;
1017 }
1018
1019 // ----------------------------------------------------------------------------
1020 // MARK: Property Get/Set/Insert/Remove Commands
1021 // ----------------------------------------------------------------------------
1022
1023 // Returns `true` and updates the `aError` on success.
HandlePropertySetForSpecialProperties(uint8_t aHeader,spinel_prop_key_t aKey,otError & aError)1024 bool NcpBase::HandlePropertySetForSpecialProperties(uint8_t aHeader, spinel_prop_key_t aKey, otError &aError)
1025 {
1026 bool didHandle = true;
1027
1028 // Here the properties that require special treatment are handled.
1029 // These properties are expected to form/write the response from
1030 // their set handler directly.
1031
1032 switch (aKey)
1033 {
1034 case SPINEL_PROP_HOST_POWER_STATE:
1035 ExitNow(aError = HandlePropertySet_SPINEL_PROP_HOST_POWER_STATE(aHeader));
1036
1037 #if OPENTHREAD_CONFIG_DIAG_ENABLE
1038 case SPINEL_PROP_NEST_STREAM_MFG:
1039 ExitNow(aError = HandlePropertySet_SPINEL_PROP_NEST_STREAM_MFG(aHeader));
1040 #endif
1041
1042 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
1043 case SPINEL_PROP_MESHCOP_COMMISSIONER_GENERATE_PSKC:
1044 ExitNow(aError = HandlePropertySet_SPINEL_PROP_MESHCOP_COMMISSIONER_GENERATE_PSKC(aHeader));
1045
1046 case SPINEL_PROP_THREAD_COMMISSIONER_ENABLED:
1047 ExitNow(aError = HandlePropertySet_SPINEL_PROP_THREAD_COMMISSIONER_ENABLED(aHeader));
1048 #endif
1049
1050 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1051 case SPINEL_PROP_STREAM_RAW:
1052 ExitNow(aError = HandlePropertySet_SPINEL_PROP_STREAM_RAW(aHeader));
1053 #endif
1054
1055 default:
1056 didHandle = false;
1057 break;
1058 }
1059
1060 exit:
1061 return didHandle;
1062 }
1063
HandleCommandPropertySet(uint8_t aHeader,spinel_prop_key_t aKey)1064 otError NcpBase::HandleCommandPropertySet(uint8_t aHeader, spinel_prop_key_t aKey)
1065 {
1066 otError error = OT_ERROR_NONE;
1067 PropertyHandler handler = FindSetPropertyHandler(aKey);
1068
1069 if (handler != nullptr)
1070 {
1071 mDisableStreamWrite = false;
1072 error = (this->*handler)();
1073 mDisableStreamWrite = true;
1074 }
1075 else
1076 {
1077 // If there is no "set" handler, check if this property is one of the
1078 // ones that require different treatment.
1079
1080 bool didHandle = HandlePropertySetForSpecialProperties(aHeader, aKey, error);
1081
1082 VerifyOrExit(!didHandle);
1083
1084 #if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK
1085 if (aKey >= SPINEL_PROP_VENDOR__BEGIN && aKey < SPINEL_PROP_VENDOR__END)
1086 {
1087 mDisableStreamWrite = false;
1088 error = VendorSetPropertyHandler(aKey);
1089 mDisableStreamWrite = true;
1090
1091 // An `OT_ERROR_NOT_FOUND` status from vendor handler indicates
1092 // that it does not support the given property key. In that
1093 // case, `didHandle` is set to `false` so a `LAST_STATUS` with
1094 // `PROP_NOT_FOUND` is emitted. Otherwise, we fall through to
1095 // prepare the response.
1096
1097 didHandle = (error != OT_ERROR_NOT_FOUND);
1098 }
1099 #endif
1100
1101 VerifyOrExit(didHandle, error = PrepareLastStatusResponse(aHeader, SPINEL_STATUS_PROP_NOT_FOUND));
1102 }
1103
1104 if (error == OT_ERROR_NONE)
1105 {
1106 error = PrepareSetResponse(aHeader, aKey);
1107 }
1108 else
1109 {
1110 error = PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(error));
1111 }
1112
1113 exit:
1114 return error;
1115 }
1116
HandleCommandPropertyInsertRemove(uint8_t aHeader,spinel_prop_key_t aKey,unsigned int aCommand)1117 otError NcpBase::HandleCommandPropertyInsertRemove(uint8_t aHeader, spinel_prop_key_t aKey, unsigned int aCommand)
1118 {
1119 otError error = OT_ERROR_NONE;
1120 PropertyHandler handler = nullptr;
1121 unsigned int responseCommand = 0;
1122 const uint8_t *valuePtr;
1123 uint16_t valueLen;
1124
1125 switch (aCommand)
1126 {
1127 case SPINEL_CMD_PROP_VALUE_INSERT:
1128 handler = FindInsertPropertyHandler(aKey);
1129 responseCommand = SPINEL_CMD_PROP_VALUE_INSERTED;
1130 break;
1131
1132 case SPINEL_CMD_PROP_VALUE_REMOVE:
1133 handler = FindRemovePropertyHandler(aKey);
1134 responseCommand = SPINEL_CMD_PROP_VALUE_REMOVED;
1135 break;
1136
1137 default:
1138 OT_ASSERT(false);
1139 }
1140
1141 VerifyOrExit(handler != nullptr, error = PrepareLastStatusResponse(aHeader, SPINEL_STATUS_PROP_NOT_FOUND));
1142
1143 // Save current read position in the decoder. Read the entire
1144 // content as a data blob (which is used in forming the response
1145 // in case of success), then reset the read position back so
1146 // that the `PropertyHandler` method can parse the content.
1147
1148 mDecoder.SavePosition();
1149 IgnoreError(mDecoder.ReadData(valuePtr, valueLen));
1150 IgnoreError(mDecoder.ResetToSaved());
1151
1152 mDisableStreamWrite = false;
1153
1154 error = (this->*handler)();
1155
1156 mDisableStreamWrite = true;
1157
1158 VerifyOrExit(error == OT_ERROR_NONE, error = PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(error)));
1159
1160 error = WritePropertyValueInsertedRemovedFrame(aHeader, responseCommand, aKey, valuePtr, valueLen);
1161
1162 // If the full response cannot be written now, instead prepare
1163 // a `LAST_STATUS(STATUS_OK)` update as response.
1164
1165 if (error != OT_ERROR_NONE)
1166 {
1167 error = PrepareLastStatusResponse(aHeader, SPINEL_STATUS_OK);
1168 }
1169
1170 exit:
1171 return error;
1172 }
1173
1174 // ----------------------------------------------------------------------------
1175 // MARK: Outbound Frame Methods
1176 // ----------------------------------------------------------------------------
1177
WriteLastStatusFrame(uint8_t aHeader,spinel_status_t aLastStatus)1178 otError NcpBase::WriteLastStatusFrame(uint8_t aHeader, spinel_status_t aLastStatus)
1179 {
1180 otError error = OT_ERROR_NONE;
1181
1182 if (SPINEL_HEADER_GET_IID(aHeader) == SPINEL_HEADER_GET_IID(SPINEL_HEADER_TX_NOTIFICATION_IID))
1183 {
1184 mLastStatus = aLastStatus;
1185 }
1186
1187 SuccessOrExit(error = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_LAST_STATUS));
1188 SuccessOrExit(error = mEncoder.WriteUintPacked(aLastStatus));
1189 SuccessOrExit(error = mEncoder.EndFrame());
1190
1191 exit:
1192 return error;
1193 }
1194
WritePropertyValueIsFrame(uint8_t aHeader,spinel_prop_key_t aPropKey,bool aIsGetResponse)1195 otError NcpBase::WritePropertyValueIsFrame(uint8_t aHeader, spinel_prop_key_t aPropKey, bool aIsGetResponse)
1196 {
1197 otError error = OT_ERROR_NONE;
1198 PropertyHandler handler = FindGetPropertyHandler(aPropKey);
1199
1200 if (handler != nullptr)
1201 {
1202 SuccessOrExit(error = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PROP_VALUE_IS, aPropKey));
1203 SuccessOrExit(error = (this->*handler)());
1204 ExitNow(error = mEncoder.EndFrame());
1205 }
1206
1207 #if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK
1208 if (aPropKey >= SPINEL_PROP_VENDOR__BEGIN && aPropKey < SPINEL_PROP_VENDOR__END)
1209 {
1210 SuccessOrExit(error = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PROP_VALUE_IS, aPropKey));
1211
1212 error = VendorGetPropertyHandler(aPropKey);
1213
1214 // An `OT_ERROR_NOT_FOUND` status from vendor handler indicates that
1215 // it did not support the given property key. In that case, we fall
1216 // through to prepare a `LAST_STATUS` response.
1217
1218 if (error != OT_ERROR_NOT_FOUND)
1219 {
1220 SuccessOrExit(error);
1221 ExitNow(error = mEncoder.EndFrame());
1222 }
1223 }
1224 #endif
1225
1226 if (aIsGetResponse)
1227 {
1228 SuccessOrExit(error = WriteLastStatusFrame(aHeader, SPINEL_STATUS_PROP_NOT_FOUND));
1229 }
1230 else
1231 {
1232 // Send a STATUS_OK for "set" response to a property that
1233 // has no corresponding get handler.
1234
1235 SuccessOrExit(error = WriteLastStatusFrame(aHeader, SPINEL_STATUS_OK));
1236 }
1237
1238 exit:
1239 return error;
1240 }
1241
WritePropertyValueInsertedRemovedFrame(uint8_t aHeader,unsigned int aResponseCommand,spinel_prop_key_t aPropKey,const uint8_t * aValuePtr,uint16_t aValueLen)1242 otError NcpBase::WritePropertyValueInsertedRemovedFrame(uint8_t aHeader,
1243 unsigned int aResponseCommand,
1244 spinel_prop_key_t aPropKey,
1245 const uint8_t *aValuePtr,
1246 uint16_t aValueLen)
1247 {
1248 otError error = OT_ERROR_NONE;
1249
1250 SuccessOrExit(error = mEncoder.BeginFrame(aHeader, aResponseCommand, aPropKey));
1251 SuccessOrExit(error = mEncoder.WriteData(aValuePtr, aValueLen));
1252 SuccessOrExit(error = mEncoder.EndFrame());
1253
1254 exit:
1255 return error;
1256 }
1257
1258 // ----------------------------------------------------------------------------
1259 // MARK: Individual Command Handlers
1260 // ----------------------------------------------------------------------------
1261
CommandHandler_NOOP(uint8_t aHeader)1262 otError NcpBase::CommandHandler_NOOP(uint8_t aHeader) { return PrepareLastStatusResponse(aHeader, SPINEL_STATUS_OK); }
1263
CommandHandler_RESET(uint8_t aHeader)1264 otError NcpBase::CommandHandler_RESET(uint8_t aHeader)
1265 {
1266 OT_UNUSED_VARIABLE(aHeader);
1267
1268 otError error = OT_ERROR_NONE;
1269 uint8_t reset_type = SPINEL_RESET_STACK;
1270
1271 if (mDecoder.GetRemainingLengthInStruct() > 0)
1272 {
1273 SuccessOrAssert(error = mDecoder.ReadUint8(reset_type));
1274 }
1275
1276 #if OPENTHREAD_RADIO
1277 if (reset_type == SPINEL_RESET_STACK)
1278 {
1279 otInstanceResetRadioStack(mInstance);
1280
1281 mIsRawStreamEnabled[mCurCommandIid] = false;
1282 mCurTransmitTID[mCurCommandIid] = 0;
1283 mCurScanChannel[mCurCommandIid] = kInvalidScanChannel;
1284 mSrcMatchEnabled[mCurCommandIid] = false;
1285
1286 ResetCounters();
1287
1288 mEncoder.ClearNcpBuffer();
1289
1290 SuccessOrAssert(error = WriteLastStatusFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID,
1291 SPINEL_STATUS_RESET_POWER_ON));
1292 }
1293 #if OPENTHREAD_CONFIG_PLATFORM_BOOTLOADER_MODE_ENABLE
1294 else if (reset_type == SPINEL_RESET_BOOTLOADER)
1295 {
1296 // Signal a platform reset to bootloader mode.
1297 // If implemented, this function shouldn't return.
1298 error = otInstanceResetToBootloader(mInstance);
1299 }
1300 #endif
1301 else
1302 #endif
1303 {
1304 // Signal a platform reset. If implemented, this function
1305 // shouldn't return.
1306 otInstanceReset(mInstance);
1307
1308 #if OPENTHREAD_MTD || OPENTHREAD_FTD
1309 // We only get to this point if the
1310 // platform doesn't support resetting.
1311 // In such a case we fake it.
1312 IgnoreError(otThreadSetEnabled(mInstance, false));
1313 IgnoreError(otIp6SetEnabled(mInstance, false));
1314 #endif
1315
1316 sNcpInstance = nullptr;
1317 }
1318
1319 return error;
1320 }
1321
CommandHandler_PROP_VALUE_update(uint8_t aHeader,unsigned int aCommand)1322 otError NcpBase::CommandHandler_PROP_VALUE_update(uint8_t aHeader, unsigned int aCommand)
1323 {
1324 otError error = OT_ERROR_NONE;
1325 unsigned int propKey = 0;
1326
1327 error = mDecoder.ReadUintPacked(propKey);
1328
1329 VerifyOrExit(error == OT_ERROR_NONE, error = PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(error)));
1330
1331 switch (aCommand)
1332 {
1333 case SPINEL_CMD_PROP_VALUE_GET:
1334 error = PrepareGetResponse(aHeader, static_cast<spinel_prop_key_t>(propKey));
1335 break;
1336
1337 case SPINEL_CMD_PROP_VALUE_SET:
1338 error = HandleCommandPropertySet(aHeader, static_cast<spinel_prop_key_t>(propKey));
1339 break;
1340
1341 case SPINEL_CMD_PROP_VALUE_INSERT:
1342 case SPINEL_CMD_PROP_VALUE_REMOVE:
1343 error = HandleCommandPropertyInsertRemove(aHeader, static_cast<spinel_prop_key_t>(propKey), aCommand);
1344 break;
1345
1346 default:
1347 break;
1348 }
1349
1350 exit:
1351 return error;
1352 }
1353
1354 #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
1355
CommandHandler_PEEK(uint8_t aHeader)1356 otError NcpBase::CommandHandler_PEEK(uint8_t aHeader)
1357 {
1358 otError parseError = OT_ERROR_NONE;
1359 otError responseError = OT_ERROR_NONE;
1360 uint32_t address;
1361 uint16_t count;
1362
1363 SuccessOrExit(parseError = mDecoder.ReadUint32(address));
1364 SuccessOrExit(parseError = mDecoder.ReadUint16(count));
1365
1366 VerifyOrExit(count != 0, parseError = OT_ERROR_INVALID_ARGS);
1367
1368 if (mAllowPeekDelegate != nullptr)
1369 {
1370 VerifyOrExit(mAllowPeekDelegate(address, count), parseError = OT_ERROR_INVALID_ARGS);
1371 }
1372
1373 SuccessOrExit(responseError = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PEEK_RET));
1374 SuccessOrExit(responseError = mEncoder.WriteUint32(address));
1375 SuccessOrExit(responseError = mEncoder.WriteUint16(count));
1376 SuccessOrExit(responseError = mEncoder.WriteData(reinterpret_cast<const uint8_t *>(address), count));
1377 SuccessOrExit(responseError = mEncoder.EndFrame());
1378
1379 exit:
1380 if (parseError != OT_ERROR_NONE)
1381 {
1382 responseError = PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(parseError));
1383 }
1384
1385 return responseError;
1386 }
1387
CommandHandler_POKE(uint8_t aHeader)1388 otError NcpBase::CommandHandler_POKE(uint8_t aHeader)
1389 {
1390 otError parseError = OT_ERROR_NONE;
1391 uint32_t address;
1392 uint16_t count;
1393 const uint8_t *dataPtr = nullptr;
1394 uint16_t dataLen;
1395
1396 SuccessOrExit(parseError = mDecoder.ReadUint32(address));
1397 SuccessOrExit(parseError = mDecoder.ReadUint16(count));
1398 SuccessOrExit(parseError = mDecoder.ReadData(dataPtr, dataLen));
1399
1400 VerifyOrExit(count != 0, parseError = OT_ERROR_INVALID_ARGS);
1401 VerifyOrExit(count <= dataLen, parseError = OT_ERROR_INVALID_ARGS);
1402
1403 if (mAllowPokeDelegate != nullptr)
1404 {
1405 VerifyOrExit(mAllowPokeDelegate(address, count), parseError = OT_ERROR_INVALID_ARGS);
1406 }
1407
1408 memcpy(reinterpret_cast<uint8_t *>(address), dataPtr, count);
1409
1410 exit:
1411 return PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(parseError));
1412 }
1413
1414 #endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
1415
1416 // ----------------------------------------------------------------------------
1417 // MARK: Individual Property Getters and Setters
1418 // ----------------------------------------------------------------------------
1419
1420 #if OPENTHREAD_CONFIG_DIAG_ENABLE
1421
HandlePropertySet_SPINEL_PROP_NEST_STREAM_MFG(uint8_t aHeader)1422 otError NcpBase::HandlePropertySet_SPINEL_PROP_NEST_STREAM_MFG(uint8_t aHeader)
1423 {
1424 const char *string = nullptr;
1425 char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE];
1426 otError error = OT_ERROR_NONE;
1427
1428 error = mDecoder.ReadUtf8(string);
1429
1430 VerifyOrExit(error == OT_ERROR_NONE, error = WriteLastStatusFrame(aHeader, ThreadErrorToSpinelStatus(error)));
1431
1432 #if OPENTHREAD_MTD || OPENTHREAD_FTD
1433 // TODO do not pass mfg prefix
1434 // skip mfg prefix from wpantund
1435 if (memcmp(string, "mfg ", 4) == 0)
1436 {
1437 string += 4;
1438 }
1439 #endif
1440
1441 SuccessOrExit(error = otDiagProcessCmdLine(mInstance, string, output, sizeof(output)));
1442
1443 // Prepare the response
1444 SuccessOrExit(error = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_NEST_STREAM_MFG));
1445 SuccessOrExit(error = mEncoder.WriteUtf8(output));
1446 SuccessOrExit(error = mEncoder.EndFrame());
1447
1448 exit:
1449 return error;
1450 }
1451
1452 #endif // OPENTHREAD_CONFIG_DIAG_ENABLE
1453
HandlePropertyGet(void)1454 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_PHY_ENABLED>(void)
1455 {
1456 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1457 return mEncoder.WriteBool(otLinkRawIsEnabled(mInstance));
1458 #else
1459 return mEncoder.WriteBool(false);
1460 #endif
1461 }
1462
HandlePropertyGet(void)1463 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_PHY_CHAN>(void)
1464 {
1465 return mEncoder.WriteUint8(otLinkGetChannel(mInstance));
1466 }
1467
HandlePropertySet(void)1468 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_CHAN>(void)
1469 {
1470 unsigned int channel = 0;
1471 otError error = OT_ERROR_NONE;
1472
1473 SuccessOrExit(error = mDecoder.ReadUintPacked(channel));
1474
1475 error = otLinkSetChannel(mInstance, static_cast<uint8_t>(channel));
1476
1477 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1478
1479 SuccessOrExit(error);
1480
1481 // Make sure we are update the receiving channel if raw link is enabled and we have raw
1482 // stream enabled already
1483 if (otLinkRawIsEnabled(mInstance) && mIsRawStreamEnabled[mCurCommandIid])
1484 {
1485 error = otLinkRawReceive(mInstance);
1486 }
1487
1488 #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1489
1490 exit:
1491 return error;
1492 }
1493
HandlePropertyGet(void)1494 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_PROMISCUOUS_MODE>(void)
1495 {
1496 bool isPromiscuous;
1497
1498 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1499 isPromiscuous = otLinkRawGetPromiscuous(mInstance);
1500 #else
1501 isPromiscuous = otLinkIsPromiscuous(mInstance);
1502 #endif
1503
1504 return mEncoder.WriteUint8(isPromiscuous ? SPINEL_MAC_PROMISCUOUS_MODE_FULL : SPINEL_MAC_PROMISCUOUS_MODE_OFF);
1505 }
1506
HandlePropertySet(void)1507 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_PROMISCUOUS_MODE>(void)
1508 {
1509 uint8_t mode = 0;
1510 otError error = OT_ERROR_NONE;
1511
1512 SuccessOrExit(error = mDecoder.ReadUint8(mode));
1513
1514 switch (mode)
1515 {
1516 case SPINEL_MAC_PROMISCUOUS_MODE_OFF:
1517 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1518 error = otLinkRawSetPromiscuous(mInstance, false);
1519 #else
1520 error = otLinkSetPromiscuous(mInstance, false);
1521 #endif
1522 break;
1523
1524 case SPINEL_MAC_PROMISCUOUS_MODE_NETWORK:
1525 case SPINEL_MAC_PROMISCUOUS_MODE_FULL:
1526 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1527 error = otLinkRawSetPromiscuous(mInstance, true);
1528 #else
1529 error = otLinkSetPromiscuous(mInstance, true);
1530 #endif
1531 break;
1532
1533 default:
1534 error = OT_ERROR_INVALID_ARGS;
1535 break;
1536 }
1537
1538 exit:
1539 return error;
1540 }
1541
HandlePropertySet(void)1542 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE>(void)
1543 {
1544 bool enabled;
1545 otError error = OT_ERROR_NONE;
1546
1547 SuccessOrExit(error = mDecoder.ReadBool(enabled));
1548 otPlatRadioSetRxOnWhenIdle(mInstance, enabled);
1549
1550 exit:
1551 return error;
1552 }
1553
HandlePropertyGet(void)1554 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_15_4_PANID>(void)
1555 {
1556 return mEncoder.WriteUint16(otLinkGetPanId(mInstance));
1557 }
1558
HandlePropertySet(void)1559 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_15_4_PANID>(void)
1560 {
1561 uint16_t panid;
1562 otError error = OT_ERROR_NONE;
1563
1564 SuccessOrExit(error = mDecoder.ReadUint16(panid));
1565
1566 error = otLinkSetPanId(mInstance, panid);
1567
1568 exit:
1569 return error;
1570 }
1571
HandlePropertyGet(void)1572 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_15_4_LADDR>(void)
1573 {
1574 return mEncoder.WriteEui64(*otLinkGetExtendedAddress(mInstance));
1575 }
1576
HandlePropertySet(void)1577 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_15_4_LADDR>(void)
1578 {
1579 const otExtAddress *extAddress;
1580 otError error = OT_ERROR_NONE;
1581
1582 SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
1583
1584 error = otLinkSetExtendedAddress(mInstance, extAddress);
1585
1586 exit:
1587 return error;
1588 }
1589
HandlePropertyGet(void)1590 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_15_4_SADDR>(void)
1591 {
1592 return mEncoder.WriteUint16(otLinkGetShortAddress(mInstance));
1593 }
1594
HandlePropertyGet(void)1595 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_RAW_STREAM_ENABLED>(void)
1596 {
1597 return mEncoder.WriteBool(mIsRawStreamEnabled[mCurCommandIid]);
1598 }
1599
HandlePropertySet(void)1600 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_RAW_STREAM_ENABLED>(void)
1601 {
1602 bool enabled = false;
1603 otError error = OT_ERROR_NONE;
1604
1605 SuccessOrExit(error = mDecoder.ReadBool(enabled));
1606
1607 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1608
1609 if (otLinkRawIsEnabled(mInstance))
1610 {
1611 if (enabled)
1612 {
1613 error = otLinkRawReceive(mInstance);
1614 }
1615 else
1616 {
1617 error = otLinkRawSleep(mInstance);
1618 }
1619 }
1620
1621 #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1622
1623 mIsRawStreamEnabled[mCurCommandIid] = enabled;
1624
1625 exit:
1626 return error;
1627 }
1628
1629 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
HandlePropertyGet(void)1630 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_RCP_CSL_ACCURACY>(void)
1631 {
1632 return mEncoder.WriteUint8(otPlatRadioGetCslAccuracy(mInstance));
1633 }
1634 #endif
1635
1636 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
HandlePropertyGet(void)1637 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_RCP_CSL_UNCERTAINTY>(void)
1638 {
1639 return mEncoder.WriteUint8(otPlatRadioGetCslUncertainty(mInstance));
1640 }
1641 #endif
1642
EncodeChannelMask(uint32_t aChannelMask)1643 otError NcpBase::EncodeChannelMask(uint32_t aChannelMask)
1644 {
1645 otError error = OT_ERROR_NONE;
1646
1647 for (uint8_t i = 0; i < 32; i++)
1648 {
1649 if (0 != (aChannelMask & (1 << i)))
1650 {
1651 SuccessOrExit(error = mEncoder.WriteUint8(i));
1652 }
1653 }
1654
1655 exit:
1656 return error;
1657 }
1658
DecodeChannelMask(uint32_t & aChannelMask)1659 otError NcpBase::DecodeChannelMask(uint32_t &aChannelMask)
1660 {
1661 otError error = OT_ERROR_NONE;
1662 uint8_t channel;
1663
1664 aChannelMask = 0;
1665
1666 while (!mDecoder.IsAllReadInStruct())
1667 {
1668 SuccessOrExit(error = mDecoder.ReadUint8(channel));
1669 VerifyOrExit(channel <= 31, error = OT_ERROR_INVALID_ARGS);
1670 aChannelMask |= (1UL << channel);
1671 }
1672
1673 exit:
1674 return error;
1675 }
1676
HandlePropertyGet(void)1677 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_SCAN_MASK>(void)
1678 {
1679 return EncodeChannelMask(mScanChannelMask);
1680 }
1681
HandlePropertySet(void)1682 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_SCAN_MASK>(void)
1683 {
1684 uint32_t newMask = 0;
1685 otError error = OT_ERROR_NONE;
1686
1687 SuccessOrExit(error = DecodeChannelMask(newMask));
1688 mScanChannelMask = newMask;
1689
1690 exit:
1691 return error;
1692 }
1693
HandlePropertyGet(void)1694 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_SCAN_PERIOD>(void)
1695 {
1696 return mEncoder.WriteUint16(mScanPeriod);
1697 }
1698
HandlePropertySet(void)1699 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_SCAN_PERIOD>(void)
1700 {
1701 return mDecoder.ReadUint16(mScanPeriod);
1702 }
1703
HandlePropertyGet(void)1704 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_SCAN_STATE>(void)
1705 {
1706 uint8_t scanState = SPINEL_SCAN_STATE_IDLE;
1707
1708 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1709
1710 if (otLinkRawIsEnabled(mInstance))
1711 {
1712 scanState = (mCurScanChannel[mCurCommandIid] == kInvalidScanChannel) ? SPINEL_SCAN_STATE_IDLE
1713 : SPINEL_SCAN_STATE_ENERGY;
1714 }
1715 else
1716
1717 #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1718
1719 {
1720 #if OPENTHREAD_MTD || OPENTHREAD_FTD
1721 if (otLinkIsActiveScanInProgress(mInstance))
1722 {
1723 scanState = SPINEL_SCAN_STATE_BEACON;
1724 }
1725 else if (otLinkIsEnergyScanInProgress(mInstance))
1726 {
1727 scanState = SPINEL_SCAN_STATE_ENERGY;
1728 }
1729 else if (otThreadIsDiscoverInProgress(mInstance))
1730 {
1731 scanState = SPINEL_SCAN_STATE_DISCOVER;
1732 }
1733 else
1734 {
1735 scanState = SPINEL_SCAN_STATE_IDLE;
1736 }
1737 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
1738 }
1739
1740 return mEncoder.WriteUint8(scanState);
1741 }
1742
HandlePropertySet(void)1743 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_SCAN_STATE>(void)
1744 {
1745 uint8_t state = 0;
1746 otError error = OT_ERROR_NONE;
1747
1748 SuccessOrExit(error = mDecoder.ReadUint8(state));
1749
1750 switch (state)
1751 {
1752 case SPINEL_SCAN_STATE_IDLE:
1753 error = OT_ERROR_NONE;
1754 break;
1755
1756 #if OPENTHREAD_MTD || OPENTHREAD_FTD
1757 case SPINEL_SCAN_STATE_BEACON:
1758 error = otLinkActiveScan(mInstance, mScanChannelMask, mScanPeriod, &HandleActiveScanResult_Jump, this);
1759 SuccessOrExit(error);
1760 break;
1761 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
1762
1763 case SPINEL_SCAN_STATE_ENERGY:
1764 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1765 if (otLinkRawIsEnabled(mInstance))
1766 {
1767 uint8_t scanChannel;
1768
1769 // Make sure we aren't already scanning and that we have
1770 // only 1 bit set for the channel mask.
1771 VerifyOrExit(mCurScanChannel[mCurCommandIid] == kInvalidScanChannel, error = OT_ERROR_INVALID_STATE);
1772 VerifyOrExit(HasOnly1BitSet(mScanChannelMask), error = OT_ERROR_INVALID_ARGS);
1773
1774 scanChannel = IndexOfMSB(mScanChannelMask);
1775 mCurScanChannel[mCurCommandIid] = static_cast<int8_t>(scanChannel);
1776
1777 error = otLinkRawEnergyScan(mInstance, scanChannel, mScanPeriod, LinkRawEnergyScanDone);
1778 }
1779 else
1780 #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1781 {
1782 #if OPENTHREAD_MTD || OPENTHREAD_FTD
1783 error = otLinkEnergyScan(mInstance, mScanChannelMask, mScanPeriod, &HandleEnergyScanResult_Jump, this);
1784 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
1785 }
1786
1787 SuccessOrExit(error);
1788 break;
1789
1790 #if OPENTHREAD_MTD || OPENTHREAD_FTD
1791 case SPINEL_SCAN_STATE_DISCOVER:
1792 error = otThreadDiscover(mInstance, mScanChannelMask, mDiscoveryScanPanId, mDiscoveryScanJoinerFlag,
1793 mDiscoveryScanEnableFiltering, &HandleActiveScanResult_Jump, this);
1794
1795 SuccessOrExit(error);
1796 break;
1797 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
1798
1799 default:
1800 error = OT_ERROR_NOT_IMPLEMENTED;
1801 break;
1802 }
1803
1804 exit:
1805 return error;
1806 }
1807
HandlePropertyGet(void)1808 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_UNSOL_UPDATE_FILTER>(void)
1809 {
1810 otError error = OT_ERROR_NONE;
1811 uint8_t numEntries;
1812 const ChangedPropsSet::Entry *entry;
1813
1814 entry = mChangedPropsSet.GetSupportedEntries(numEntries);
1815
1816 for (uint8_t index = 0; index < numEntries; index++, entry++)
1817 {
1818 if (mChangedPropsSet.IsEntryFiltered(index))
1819 {
1820 SuccessOrExit(error = mEncoder.WriteUintPacked(entry->mPropKey));
1821 }
1822 }
1823
1824 exit:
1825 return error;
1826 }
1827
HandlePropertySet(void)1828 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_UNSOL_UPDATE_FILTER>(void)
1829 {
1830 unsigned int propKey;
1831 otError error = OT_ERROR_NONE;
1832
1833 // First clear the current filter.
1834 mChangedPropsSet.ClearFilter();
1835
1836 while (mDecoder.GetRemainingLengthInStruct() > 0)
1837 {
1838 SuccessOrExit(error = mDecoder.ReadUintPacked(propKey));
1839
1840 IgnoreError(mChangedPropsSet.EnablePropertyFilter(static_cast<spinel_prop_key_t>(propKey), true));
1841 }
1842
1843 exit:
1844 // If we had an error, we may have actually changed
1845 // the state of the filter, So we need to report
1846 // those incomplete changes via an asynchronous
1847 // change event.
1848
1849 if (error != OT_ERROR_NONE)
1850 {
1851 IgnoreError(WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID,
1852 SPINEL_PROP_UNSOL_UPDATE_FILTER));
1853 }
1854
1855 return error;
1856 }
1857
HandlePropertyInsert(void)1858 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_UNSOL_UPDATE_FILTER>(void)
1859 {
1860 otError error = OT_ERROR_NONE;
1861 unsigned int propKey;
1862
1863 SuccessOrExit(error = mDecoder.ReadUintPacked(propKey));
1864
1865 error = mChangedPropsSet.EnablePropertyFilter(static_cast<spinel_prop_key_t>(propKey), true);
1866
1867 exit:
1868 return error;
1869 }
1870
HandlePropertyRemove(void)1871 template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_UNSOL_UPDATE_FILTER>(void)
1872 {
1873 otError error = OT_ERROR_NONE;
1874 unsigned int propKey;
1875
1876 SuccessOrExit(error = mDecoder.ReadUintPacked(propKey));
1877
1878 error = mChangedPropsSet.EnablePropertyFilter(static_cast<spinel_prop_key_t>(propKey), false);
1879
1880 exit:
1881 return error;
1882 }
1883
HandlePropertyGet(void)1884 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_LAST_STATUS>(void)
1885 {
1886 return mEncoder.WriteUintPacked(mLastStatus);
1887 }
1888
HandlePropertyGet(void)1889 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_PROTOCOL_VERSION>(void)
1890 {
1891 otError error = OT_ERROR_NONE;
1892
1893 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROTOCOL_VERSION_THREAD_MAJOR));
1894 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROTOCOL_VERSION_THREAD_MINOR));
1895
1896 exit:
1897 return error;
1898 }
1899
HandlePropertyGet(void)1900 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_INTERFACE_TYPE>(void)
1901 {
1902 return mEncoder.WriteUintPacked(SPINEL_PROTOCOL_TYPE_THREAD);
1903 }
1904
HandlePropertyGet(void)1905 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_VENDOR_ID>(void)
1906 {
1907 return mEncoder.WriteUintPacked(0); // Vendor ID. Zero for unknown.
1908 }
1909
HandlePropertyGet(void)1910 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CAPS>(void)
1911 {
1912 otError error = OT_ERROR_NONE;
1913
1914 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_COUNTERS));
1915 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_UNSOL_UPDATE_FILTER));
1916
1917 #if OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
1918 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_MCU_POWER_STATE));
1919 #endif
1920
1921 #if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT
1922 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_802_15_4_2450MHZ_OQPSK));
1923 #endif
1924
1925 #if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT
1926 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_802_15_4_915MHZ_OQPSK));
1927 #endif
1928
1929 #if OPENTHREAD_FTD
1930 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_CONFIG_FTD));
1931 #elif OPENTHREAD_MTD
1932 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_CONFIG_MTD));
1933 #elif OPENTHREAD_RADIO
1934 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_CONFIG_RADIO));
1935 #endif
1936
1937 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
1938 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_MAC_RAW));
1939 #endif
1940
1941 #if OPENTHREAD_RADIO
1942 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_RCP_API_VERSION));
1943 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_RCP_MIN_HOST_API_VERSION));
1944 #endif
1945
1946 #if OPENTHREAD_CONFIG_PLATFORM_BOOTLOADER_MODE_ENABLE
1947 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_RCP_RESET_TO_BOOTLOADER));
1948 #endif
1949
1950 #if OPENTHREAD_CONFIG_PLATFORM_LOG_CRASH_DUMP_ENABLE
1951 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_RCP_LOG_CRASH_DUMP));
1952 #endif
1953
1954 #if OPENTHREAD_PLATFORM_POSIX
1955 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_POSIX));
1956 #endif
1957
1958 #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_APP)
1959 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_OPENTHREAD_LOG_METADATA));
1960 #endif
1961
1962 #if OPENTHREAD_MTD || OPENTHREAD_FTD
1963
1964 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_NET_THREAD_1_1));
1965
1966 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
1967 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_NET_THREAD_1_2));
1968 #endif
1969
1970 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_PCAP));
1971
1972 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
1973 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_MAC_ALLOWLIST));
1974 #endif
1975
1976 #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE
1977 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_JAM_DETECT));
1978 #endif
1979
1980 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_CHILD_SUPERVISION));
1981
1982 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
1983 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_CHANNEL_MONITOR));
1984 #endif
1985
1986 #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
1987 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_CHANNEL_MANAGER));
1988 #endif
1989
1990 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
1991 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_TIME_SYNC));
1992 #endif
1993
1994 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_ERROR_RATE_TRACKING));
1995
1996 #if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE
1997 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_OOB_STEERING_DATA));
1998 #endif
1999
2000 #if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
2001 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_SLAAC));
2002 #endif
2003
2004 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
2005 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_RADIO_COEX));
2006 #endif
2007
2008 #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
2009 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_MAC_RETRY_HISTOGRAM));
2010 #endif
2011
2012 #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
2013 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_PEEK_POKE));
2014 #endif
2015
2016 #if OPENTHREAD_CONFIG_MLE_MAX_CHILDREN > 0
2017 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_ROLE_ROUTER));
2018 #endif
2019
2020 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_ROLE_SLEEPY));
2021
2022 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
2023 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_THREAD_COMMISSIONER));
2024 #endif
2025
2026 #if OPENTHREAD_CONFIG_JOINER_ENABLE
2027 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_THREAD_JOINER));
2028 #endif
2029
2030 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
2031 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_THREAD_BORDER_ROUTER));
2032 #endif
2033
2034 #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE
2035 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_THREAD_UDP_FORWARD));
2036 #endif
2037
2038 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
2039 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_THREAD_SERVICE));
2040 #endif
2041
2042 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
2043 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_THREAD_CSL_RECEIVER));
2044 #endif
2045
2046 #if OPENTHREAD_CONFIG_MULTI_RADIO
2047 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_MULTI_RADIO));
2048 #endif
2049
2050 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
2051 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_SRP_CLIENT));
2052 #endif
2053
2054 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
2055 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_THREAD_LINK_METRICS));
2056 #endif
2057
2058 #if OPENTHREAD_CONFIG_DUA_ENABLE
2059 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_DUA));
2060 #endif
2061
2062 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
2063 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_REFERENCE_DEVICE));
2064 #endif
2065
2066 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
2067 SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_THREAD_BACKBONE_ROUTER));
2068 #endif
2069
2070 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
2071
2072 exit:
2073 return error;
2074 }
2075
HandlePropertyGet(void)2076 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NCP_VERSION>(void)
2077 {
2078 return mEncoder.WriteUtf8(otGetVersionString());
2079 }
2080
HandlePropertyGet(void)2081 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_INTERFACE_COUNT>(void)
2082 {
2083 uint8_t instances = 1;
2084 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
2085 instances = 0;
2086 for (uint8_t i = 0; i <= SPINEL_HEADER_IID_MAX; i++)
2087 {
2088 if (mInstances[i] != nullptr)
2089 {
2090 instances++;
2091 }
2092 }
2093 #endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
2094
2095 return mEncoder.WriteUint8(instances);
2096 }
2097
2098 #if OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
2099
HandlePropertyGet(void)2100 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MCU_POWER_STATE>(void)
2101 {
2102 spinel_mcu_power_state_t state = SPINEL_MCU_POWER_STATE_ON;
2103
2104 switch (otPlatGetMcuPowerState(mInstance))
2105 {
2106 case OT_PLAT_MCU_POWER_STATE_ON:
2107 state = SPINEL_MCU_POWER_STATE_ON;
2108 break;
2109
2110 case OT_PLAT_MCU_POWER_STATE_LOW_POWER:
2111 state = SPINEL_MCU_POWER_STATE_LOW_POWER;
2112 break;
2113
2114 case OT_PLAT_MCU_POWER_STATE_OFF:
2115 state = SPINEL_MCU_POWER_STATE_OFF;
2116 break;
2117 }
2118
2119 return mEncoder.WriteUint8(state);
2120 }
2121
HandlePropertySet(void)2122 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MCU_POWER_STATE>(void)
2123 {
2124 otError error = OT_ERROR_NONE;
2125 otPlatMcuPowerState powerState;
2126 uint8_t state;
2127
2128 SuccessOrExit(error = mDecoder.ReadUint8(state));
2129
2130 switch (state)
2131 {
2132 case SPINEL_MCU_POWER_STATE_ON:
2133 powerState = OT_PLAT_MCU_POWER_STATE_ON;
2134 break;
2135
2136 case SPINEL_MCU_POWER_STATE_LOW_POWER:
2137 powerState = OT_PLAT_MCU_POWER_STATE_LOW_POWER;
2138 break;
2139
2140 case SPINEL_MCU_POWER_STATE_OFF:
2141 powerState = OT_PLAT_MCU_POWER_STATE_OFF;
2142 break;
2143
2144 default:
2145 ExitNow(error = OT_ERROR_INVALID_ARGS);
2146 }
2147
2148 SuccessOrExit(error = otPlatSetMcuPowerState(mInstance, powerState));
2149
2150 #if OPENTHREAD_FTD || OPENTHREAD_MTD
2151
2152 // If the call `otPlatSetMcuPowerState()` was successful and the desire
2153 // state is `OFF`, ensure to disable Thread (MLE) operation (and stop
2154 // legacy) and also bring the IPv6 interface down.
2155
2156 if (powerState == OT_PLAT_MCU_POWER_STATE_OFF)
2157 {
2158 if (otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED)
2159 {
2160 IgnoreError(otThreadSetEnabled(mInstance, false));
2161 }
2162
2163 if (otIp6IsEnabled(mInstance))
2164 {
2165 IgnoreError(otIp6SetEnabled(mInstance, false));
2166 }
2167 }
2168 #endif // #if OPENTHREAD_FTD || OPENTHREAD_MTD
2169
2170 exit:
2171 return error;
2172 }
2173
2174 #else // OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
2175
HandlePropertyGet(void)2176 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MCU_POWER_STATE>(void)
2177 {
2178 return mEncoder.WriteUint8(SPINEL_MCU_POWER_STATE_ON);
2179 }
2180
2181 #endif // OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
2182
HandlePropertyGet(void)2183 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_POWER_STATE>(void)
2184 {
2185 return mEncoder.WriteUint8(SPINEL_POWER_STATE_ONLINE);
2186 }
2187
HandlePropertySet(void)2188 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_POWER_STATE>(void) { return OT_ERROR_NOT_IMPLEMENTED; }
2189
HandlePropertyGet(void)2190 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_HWADDR>(void)
2191 {
2192 otExtAddress hwAddr;
2193
2194 otLinkGetFactoryAssignedIeeeEui64(mInstance, &hwAddr);
2195
2196 return mEncoder.WriteEui64(hwAddr);
2197 }
2198
HandlePropertyGet(void)2199 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_LOCK>(void)
2200 {
2201 // TODO: Implement property lock (Needs API!)
2202 return mEncoder.OverwriteWithLastStatusError(SPINEL_STATUS_UNIMPLEMENTED);
2203 }
2204
HandlePropertyGet(void)2205 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_HOST_POWER_STATE>(void)
2206 {
2207 return mEncoder.WriteUint8(mHostPowerState);
2208 }
2209
2210 // Setting `HOST_POWER_STATE` is treated and implemented differently from other
2211 // handlers as it requires two special behaviors (a) the response frame for the
2212 // set operation should be tracked and only when it is delivered we can assume
2213 // that host is sleep (b) the response is critical so if there is no spinel
2214 // buffer to prepare the response, the current spinel header is saved to
2215 // prepare and send the response as soon as buffer space becomes available.
HandlePropertySet_SPINEL_PROP_HOST_POWER_STATE(uint8_t aHeader)2216 otError NcpBase::HandlePropertySet_SPINEL_PROP_HOST_POWER_STATE(uint8_t aHeader)
2217 {
2218 uint8_t powerState;
2219 otError error = OT_ERROR_NONE;
2220
2221 error = mDecoder.ReadUint8(powerState);
2222
2223 if (error == OT_ERROR_NONE)
2224 {
2225 switch (powerState)
2226 {
2227 case SPINEL_HOST_POWER_STATE_OFFLINE:
2228 case SPINEL_HOST_POWER_STATE_DEEP_SLEEP:
2229 case SPINEL_HOST_POWER_STATE_LOW_POWER:
2230 case SPINEL_HOST_POWER_STATE_ONLINE:
2231 // Adopt the requested power state.
2232 mHostPowerState = static_cast<spinel_host_power_state_t>(powerState);
2233 break;
2234
2235 case SPINEL_HOST_POWER_STATE_RESERVED:
2236 // Per the specification, treat this as synonymous with SPINEL_HOST_POWER_STATE_DEEP_SLEEP.
2237 mHostPowerState = SPINEL_HOST_POWER_STATE_DEEP_SLEEP;
2238 break;
2239
2240 default:
2241 // Per the specification, treat unrecognized values as synonymous with SPINEL_HOST_POWER_STATE_LOW_POWER.
2242 mHostPowerState = SPINEL_HOST_POWER_STATE_LOW_POWER;
2243 break;
2244 }
2245
2246 mHostPowerStateHeader = 0;
2247
2248 error = WritePropertyValueIsFrame(aHeader, SPINEL_PROP_HOST_POWER_STATE);
2249
2250 if (mHostPowerState != SPINEL_HOST_POWER_STATE_ONLINE)
2251 {
2252 if (error == OT_ERROR_NONE)
2253 {
2254 mHostPowerReplyFrameTag = GetLastOutboundFrameTag();
2255 }
2256 else
2257 {
2258 mHostPowerReplyFrameTag = Spinel::Buffer::kInvalidTag;
2259 }
2260
2261 mHostPowerStateInProgress = true;
2262 }
2263
2264 if (error != OT_ERROR_NONE)
2265 {
2266 mHostPowerStateHeader = aHeader;
2267
2268 // The reply will be queued when buffer space becomes available
2269 // in the NCP tx buffer so we return `success` to avoid sending a
2270 // NOMEM status for the same tid through `mDroppedReplyTid` list.
2271
2272 error = OT_ERROR_NONE;
2273 }
2274 }
2275 else
2276 {
2277 error = WriteLastStatusFrame(aHeader, ThreadErrorToSpinelStatus(error));
2278 }
2279
2280 return error;
2281 }
2282
HandlePropertyGet(void)2283 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_UNSOL_UPDATE_LIST>(void)
2284 {
2285 otError error = OT_ERROR_NONE;
2286 uint8_t numEntries;
2287 const ChangedPropsSet::Entry *entry;
2288
2289 entry = mChangedPropsSet.GetSupportedEntries(numEntries);
2290
2291 for (uint8_t index = 0; index < numEntries; index++, entry++)
2292 {
2293 if (entry->mFilterable)
2294 {
2295 SuccessOrExit(error = mEncoder.WriteUintPacked(entry->mPropKey));
2296 }
2297 }
2298
2299 exit:
2300 return error;
2301 }
2302
HandlePropertyGet(void)2303 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_PHY_RSSI>(void)
2304 {
2305 return mEncoder.WriteInt8(otPlatRadioGetRssi(mInstance));
2306 }
2307
HandlePropertyGet(void)2308 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_PHY_RX_SENSITIVITY>(void)
2309 {
2310 return mEncoder.WriteInt8(otPlatRadioGetReceiveSensitivity(mInstance));
2311 }
2312
HandlePropertyGet(void)2313 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_PHY_FREQ>(void)
2314 {
2315 uint32_t freq_khz(0);
2316 const uint8_t chan(otLinkGetChannel(mInstance));
2317
2318 if (chan == 0)
2319 {
2320 freq_khz = 868300;
2321 }
2322 else if (chan < 11)
2323 {
2324 freq_khz = 906000 - (2000 * 1) + 2000 * (chan);
2325 }
2326 else if (chan < 26)
2327 {
2328 freq_khz = 2405000 - (5000 * 11) + 5000 * (chan);
2329 }
2330
2331 return mEncoder.WriteUint32(freq_khz);
2332 }
2333
HandlePropertyGet(void)2334 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_PHY_CCA_THRESHOLD>(void)
2335 {
2336 int8_t threshold;
2337 otError error = OT_ERROR_NONE;
2338
2339 error = otPlatRadioGetCcaEnergyDetectThreshold(mInstance, &threshold);
2340
2341 if (error == OT_ERROR_NONE)
2342 {
2343 error = mEncoder.WriteInt8(threshold);
2344 }
2345 else
2346 {
2347 error = mEncoder.OverwriteWithLastStatusError(ThreadErrorToSpinelStatus(error));
2348 }
2349
2350 return error;
2351 }
2352
HandlePropertySet(void)2353 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_CCA_THRESHOLD>(void)
2354 {
2355 int8_t threshold = 0;
2356 otError error = OT_ERROR_NONE;
2357
2358 SuccessOrExit(error = mDecoder.ReadInt8(threshold));
2359 error = otPlatRadioSetCcaEnergyDetectThreshold(mInstance, threshold);
2360
2361 exit:
2362 return error;
2363 }
2364
HandlePropertyGet(void)2365 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_PHY_TX_POWER>(void)
2366 {
2367 int8_t power;
2368 otError error;
2369
2370 error = otPlatRadioGetTransmitPower(mInstance, &power);
2371
2372 if (error == OT_ERROR_NONE)
2373 {
2374 error = mEncoder.WriteInt8(power);
2375 }
2376 else
2377 {
2378 error = mEncoder.OverwriteWithLastStatusError(ThreadErrorToSpinelStatus(error));
2379 }
2380
2381 return error;
2382 }
2383
HandlePropertySet(void)2384 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_TX_POWER>(void)
2385 {
2386 int8_t txPower = 0;
2387 otError error = OT_ERROR_NONE;
2388
2389 SuccessOrExit(error = mDecoder.ReadInt8(txPower));
2390 error = otPlatRadioSetTransmitPower(mInstance, txPower);
2391
2392 exit:
2393 return error;
2394 }
2395
HandlePropertyGet(void)2396 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_PHY_FEM_LNA_GAIN>(void)
2397 {
2398 int8_t gain;
2399 otError error = OT_ERROR_NONE;
2400
2401 error = otPlatRadioGetFemLnaGain(mInstance, &gain);
2402
2403 if (error == OT_ERROR_NONE)
2404 {
2405 error = mEncoder.WriteInt8(gain);
2406 }
2407 else
2408 {
2409 error = mEncoder.OverwriteWithLastStatusError(ThreadErrorToSpinelStatus(error));
2410 }
2411
2412 return error;
2413 }
2414
HandlePropertySet(void)2415 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_FEM_LNA_GAIN>(void)
2416 {
2417 int8_t gain = 0;
2418 otError error = OT_ERROR_NONE;
2419
2420 SuccessOrExit(error = mDecoder.ReadInt8(gain));
2421 error = otPlatRadioSetFemLnaGain(mInstance, gain);
2422
2423 exit:
2424 return error;
2425 }
2426
HandlePropertySet(void)2427 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_CHAN_MAX_POWER>(void)
2428 {
2429 uint8_t channel;
2430 int8_t maxPower;
2431 otError error = OT_ERROR_NONE;
2432
2433 SuccessOrExit(error = mDecoder.ReadUint8(channel));
2434 SuccessOrExit(error = mDecoder.ReadInt8(maxPower));
2435 error = otPlatRadioSetChannelMaxTransmitPower(mInstance, channel, maxPower);
2436
2437 exit:
2438 return error;
2439 }
2440
HandlePropertyGet(void)2441 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_PHY_REGION_CODE>(void)
2442 {
2443 uint16_t regionCode;
2444 otError error = OT_ERROR_NONE;
2445
2446 error = otPlatRadioGetRegion(mInstance, ®ionCode);
2447 if (error == OT_ERROR_NONE)
2448 {
2449 error = mEncoder.WriteUint16(regionCode);
2450 }
2451 else
2452 {
2453 error = mEncoder.OverwriteWithLastStatusError(ThreadErrorToSpinelStatus(error));
2454 }
2455
2456 return error;
2457 }
2458
HandlePropertySet(void)2459 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_REGION_CODE>(void)
2460 {
2461 uint16_t regionCode;
2462 otError error = OT_ERROR_NONE;
2463
2464 SuccessOrExit(error = mDecoder.ReadUint16(regionCode));
2465 error = otPlatRadioSetRegion(mInstance, regionCode);
2466
2467 exit:
2468 return error;
2469 }
2470
HandlePropertyGet(void)2471 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_DEBUG_TEST_ASSERT>(void)
2472 {
2473 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2474 OT_ASSERT(false);
2475 #endif
2476
2477 // We only get to this point if `OT_ASSERT(false)`
2478 // does not cause an NCP reset on the platform.
2479 // In such a case we return `false` as the
2480 // property value to indicate this.
2481
2482 OT_UNREACHABLE_CODE(return mEncoder.WriteBool(false);)
2483 }
2484
HandlePropertyGet(void)2485 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_DEBUG_TEST_WATCHDOG>(void)
2486 {
2487 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2488 while (true)
2489 ;
2490 #endif
2491
2492 OT_UNREACHABLE_CODE(return OT_ERROR_NONE;)
2493 }
2494
HandlePropertyGet(void)2495 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_DEBUG_NCP_LOG_LEVEL>(void)
2496 {
2497 return mEncoder.WriteUint8(ConvertLogLevel(otLoggingGetLevel()));
2498 }
2499
2500 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
HandlePropertySet(void)2501 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_DEBUG_NCP_LOG_LEVEL>(void)
2502 {
2503 otError error;
2504 uint8_t spinelNcpLogLevel = 0;
2505 otLogLevel logLevel;
2506
2507 SuccessOrExit(error = mDecoder.ReadUint8(spinelNcpLogLevel));
2508
2509 switch (spinelNcpLogLevel)
2510 {
2511 case SPINEL_NCP_LOG_LEVEL_EMERG:
2512 case SPINEL_NCP_LOG_LEVEL_ALERT:
2513 logLevel = OT_LOG_LEVEL_NONE;
2514 break;
2515
2516 case SPINEL_NCP_LOG_LEVEL_CRIT:
2517 logLevel = OT_LOG_LEVEL_CRIT;
2518 break;
2519
2520 case SPINEL_NCP_LOG_LEVEL_ERR:
2521 case SPINEL_NCP_LOG_LEVEL_WARN:
2522 logLevel = OT_LOG_LEVEL_WARN;
2523 break;
2524
2525 case SPINEL_NCP_LOG_LEVEL_NOTICE:
2526 logLevel = OT_LOG_LEVEL_NOTE;
2527 break;
2528
2529 case SPINEL_NCP_LOG_LEVEL_INFO:
2530 logLevel = OT_LOG_LEVEL_INFO;
2531 break;
2532
2533 case SPINEL_NCP_LOG_LEVEL_DEBUG:
2534 logLevel = OT_LOG_LEVEL_DEBG;
2535 break;
2536
2537 default:
2538 ExitNow(error = OT_ERROR_INVALID_ARGS);
2539 }
2540
2541 IgnoreError(otLoggingSetLevel(logLevel));
2542
2543 exit:
2544 return error;
2545 }
2546 #endif // OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
2547
HandlePropertySet(void)2548 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_DEBUG_LOG_TIMESTAMP_BASE>(void)
2549 {
2550 uint64_t timestampBase = 0;
2551 otError error = OT_ERROR_NONE;
2552 uint32_t currentTime = otPlatAlarmMilliGetNow();
2553
2554 SuccessOrExit(error = mDecoder.ReadUint64(timestampBase));
2555 VerifyOrExit(timestampBase >= currentTime, error = OT_ERROR_INVALID_ARGS);
2556
2557 mLogTimestampBase = timestampBase - currentTime;
2558
2559 exit:
2560 return error;
2561 }
2562
HandlePropertyGet(void)2563 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_DEBUG_LOG_TIMESTAMP_BASE>(void)
2564 {
2565 return mEncoder.WriteUint64(mLogTimestampBase);
2566 }
2567
HandlePropertyGet(void)2568 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_PHY_CHAN_SUPPORTED>(void)
2569 {
2570 #if OPENTHREAD_RADIO
2571 return EncodeChannelMask(otPlatRadioGetSupportedChannelMask(mInstance));
2572 #else
2573 return EncodeChannelMask(otLinkGetSupportedChannelMask(mInstance));
2574 #endif // OPENTHREAD_RADIO
2575 }
2576
HandlePropertyGet(void)2577 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_PHY_CHAN_PREFERRED>(void)
2578 {
2579 return EncodeChannelMask(otPlatRadioGetPreferredChannelMask(mInstance));
2580 }
2581
2582 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
HandlePropertySet(void)2583 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_RADIO_COEX_ENABLE>(void)
2584 {
2585 bool enabled;
2586 otError error = OT_ERROR_NONE;
2587
2588 SuccessOrExit(error = mDecoder.ReadBool(enabled));
2589 error = otPlatRadioSetCoexEnabled(mInstance, enabled);
2590
2591 exit:
2592 return error;
2593 }
2594
HandlePropertyGet(void)2595 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_RADIO_COEX_ENABLE>(void)
2596 {
2597 return mEncoder.WriteBool(otPlatRadioIsCoexEnabled(mInstance));
2598 }
2599
HandlePropertyGet(void)2600 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_RADIO_COEX_METRICS>(void)
2601 {
2602 otRadioCoexMetrics coexMetrics;
2603 otError error = otPlatRadioGetCoexMetrics(mInstance, &coexMetrics);
2604
2605 if (error != OT_ERROR_NONE)
2606 {
2607 error = mEncoder.OverwriteWithLastStatusError(ThreadErrorToSpinelStatus(error));
2608 ExitNow();
2609 }
2610
2611 // Encode Tx Request related metrics
2612 SuccessOrExit(error = mEncoder.OpenStruct());
2613 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumTxRequest));
2614 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumTxGrantImmediate));
2615 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumTxGrantWait));
2616 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumTxGrantWaitActivated));
2617 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumTxGrantWaitTimeout));
2618 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumTxGrantDeactivatedDuringRequest));
2619 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumTxDelayedGrant));
2620 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mAvgTxRequestToGrantTime));
2621 SuccessOrExit(error = mEncoder.CloseStruct());
2622
2623 // Encode Rx Request related metrics
2624 SuccessOrExit(error = mEncoder.OpenStruct());
2625 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumRxRequest));
2626 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumRxGrantImmediate));
2627 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumRxGrantWait));
2628 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumRxGrantWaitActivated));
2629 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumRxGrantWaitTimeout));
2630 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumRxGrantDeactivatedDuringRequest));
2631 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumRxDelayedGrant));
2632 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mAvgRxRequestToGrantTime));
2633 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumRxGrantNone));
2634 SuccessOrExit(error = mEncoder.CloseStruct());
2635
2636 // Encode common metrics
2637 SuccessOrExit(error = mEncoder.WriteBool(coexMetrics.mStopped));
2638 SuccessOrExit(error = mEncoder.WriteUint32(coexMetrics.mNumGrantGlitch));
2639
2640 exit:
2641 return error;
2642 }
2643 #endif
2644
2645 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
DecodeLinkMetrics(otLinkMetrics * aMetrics,bool aAllowPduCount)2646 otError NcpBase::DecodeLinkMetrics(otLinkMetrics *aMetrics, bool aAllowPduCount)
2647 {
2648 otError error = OT_ERROR_NONE;
2649 uint8_t metrics = 0;
2650
2651 SuccessOrExit(error = mDecoder.ReadUint8(metrics));
2652
2653 if (metrics & SPINEL_THREAD_LINK_METRIC_PDU_COUNT)
2654 {
2655 VerifyOrExit(aAllowPduCount, error = OT_ERROR_INVALID_ARGS);
2656 aMetrics->mPduCount = true;
2657 }
2658
2659 if (metrics & SPINEL_THREAD_LINK_METRIC_LQI)
2660 {
2661 aMetrics->mLqi = true;
2662 }
2663
2664 if (metrics & SPINEL_THREAD_LINK_METRIC_LINK_MARGIN)
2665 {
2666 aMetrics->mLinkMargin = true;
2667 }
2668
2669 if (metrics & SPINEL_THREAD_LINK_METRIC_RSSI)
2670 {
2671 aMetrics->mRssi = true;
2672 }
2673
2674 exit:
2675 return error;
2676 }
2677 #endif
2678
2679 #if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
HandlePropertySet(void)2680 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_CHAN_TARGET_POWER>(void)
2681 {
2682 otError error;
2683 uint8_t channel;
2684 int16_t targetPower;
2685
2686 SuccessOrExit(error = mDecoder.ReadUint8(channel));
2687 SuccessOrExit(error = mDecoder.ReadInt16(targetPower));
2688 error = otPlatRadioSetChannelTargetPower(mInstance, channel, targetPower);
2689
2690 exit:
2691 return error;
2692 }
2693
HandlePropertyInsert(void)2694 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_PHY_CALIBRATED_POWER>(void)
2695 {
2696 otError error;
2697 uint8_t channel;
2698 int16_t actualPower;
2699 const uint8_t *dataPtr;
2700 uint16_t dataLen;
2701
2702 SuccessOrExit(error = mDecoder.ReadUint8(channel));
2703 SuccessOrExit(error = mDecoder.ReadInt16(actualPower));
2704 SuccessOrExit(error = mDecoder.ReadDataWithLen(dataPtr, dataLen));
2705 error = otPlatRadioAddCalibratedPower(mInstance, channel, actualPower, dataPtr, dataLen);
2706
2707 exit:
2708 return error;
2709 }
2710
HandlePropertySet(void)2711 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_CALIBRATED_POWER>(void)
2712 {
2713 return otPlatRadioClearCalibratedPowers(mInstance);
2714 }
2715 #endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
2716
2717 } // namespace Ncp
2718 } // namespace ot
2719
2720 // ----------------------------------------------------------------------------
2721 // MARK: Peek/Poke delegate API
2722 // ----------------------------------------------------------------------------
2723
2724 #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
otNcpRegisterPeekPoke(otNcpDelegateAllowPeekPoke aAllowPeekDelegate,otNcpDelegateAllowPeekPoke aAllowPokeDelegate)2725 void otNcpRegisterPeekPoke(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, otNcpDelegateAllowPeekPoke aAllowPokeDelegate)
2726 {
2727 ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance();
2728
2729 if (ncp != nullptr)
2730 {
2731 ncp->RegisterPeekPokeDelegates(aAllowPeekDelegate, aAllowPokeDelegate);
2732 }
2733 }
2734 #endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
2735
2736 // ----------------------------------------------------------------------------
2737 // MARK: Virtual Datastream I/O (Public API)
2738 // ----------------------------------------------------------------------------
2739
otNcpStreamWrite(int aStreamId,const uint8_t * aDataPtr,int aDataLen)2740 otError otNcpStreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen)
2741 {
2742 otError error = OT_ERROR_INVALID_STATE;
2743 ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance();
2744
2745 if (ncp != nullptr)
2746 {
2747 error = ncp->StreamWrite(aStreamId, aDataPtr, aDataLen);
2748 }
2749
2750 return error;
2751 }
2752
2753 #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_APP)
2754
otPlatLog(otLogLevel aLogLevel,otLogRegion aLogRegion,const char * aFormat,...)2755 extern "C" void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
2756 {
2757 va_list args;
2758 char logString[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE];
2759 ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance();
2760
2761 va_start(args, aFormat);
2762
2763 if (vsnprintf(logString, sizeof(logString), aFormat, args) > 0)
2764 {
2765 if (ncp != nullptr)
2766 {
2767 ncp->Log(aLogLevel, aLogRegion, logString);
2768 }
2769 }
2770
2771 va_end(args);
2772 }
2773
2774 #endif // (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_APP)
2775