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