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