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, &regionCode);
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