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