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