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