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