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 minimal thread device required Spinel interface to the OpenThread stack.
31  */
32 
33 #include "openthread-core-config.h"
34 
35 #include "ncp_base.hpp"
36 
37 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
38 #include <openthread/border_router.h>
39 #endif
40 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
41 #include <openthread/channel_monitor.h>
42 #endif
43 #include <openthread/child_supervision.h>
44 #include <openthread/diag.h>
45 #include <openthread/icmp6.h>
46 #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE
47 #include <openthread/jam_detection.h>
48 #endif
49 #include <openthread/ncp.h>
50 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
51 #include <openthread/network_time.h>
52 #endif
53 #include <openthread/platform/misc.h>
54 #include <openthread/platform/radio.h>
55 #if OPENTHREAD_FTD
56 #include <openthread/thread_ftd.h>
57 #endif
58 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
59 #include <openthread/server.h>
60 #endif
61 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
62 #include "openthread/backbone_router.h"
63 #endif
64 #if OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_ENABLE
65 #include <openthread/srp_client_buffers.h>
66 #endif
67 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
68 #include <openthread/trel.h>
69 #endif
70 
71 #include "common/code_utils.hpp"
72 #include "common/debug.hpp"
73 #include "common/string.hpp"
74 #include "instance/instance.hpp"
75 #include "net/ip6.hpp"
76 
77 #if OPENTHREAD_MTD || OPENTHREAD_FTD
78 
79 namespace ot {
80 namespace Ncp {
81 
BorderRouterConfigToFlagByte(const otBorderRouterConfig & aConfig)82 static uint8_t BorderRouterConfigToFlagByte(const otBorderRouterConfig &aConfig)
83 {
84     uint8_t flags = 0;
85 
86     if (aConfig.mPreferred)
87     {
88         flags |= SPINEL_NET_FLAG_PREFERRED;
89     }
90 
91     if (aConfig.mSlaac)
92     {
93         flags |= SPINEL_NET_FLAG_SLAAC;
94     }
95 
96     if (aConfig.mDhcp)
97     {
98         flags |= SPINEL_NET_FLAG_DHCP;
99     }
100 
101     if (aConfig.mDefaultRoute)
102     {
103         flags |= SPINEL_NET_FLAG_DEFAULT_ROUTE;
104     }
105 
106     if (aConfig.mConfigure)
107     {
108         flags |= SPINEL_NET_FLAG_CONFIGURE;
109     }
110 
111     if (aConfig.mOnMesh)
112     {
113         flags |= SPINEL_NET_FLAG_ON_MESH;
114     }
115 
116     flags |= (static_cast<uint8_t>(aConfig.mPreference) << SPINEL_NET_FLAG_PREFERENCE_OFFSET);
117 
118     return flags;
119 }
120 
BorderRouterConfigToFlagByteExtended(const otBorderRouterConfig & aConfig)121 static uint8_t BorderRouterConfigToFlagByteExtended(const otBorderRouterConfig &aConfig)
122 {
123     uint8_t flags = 0;
124 
125     if (aConfig.mNdDns)
126     {
127         flags |= SPINEL_NET_FLAG_EXT_DNS;
128     }
129 
130     if (aConfig.mDp)
131     {
132         flags |= SPINEL_NET_FLAG_EXT_DP;
133     }
134 
135     return flags;
136 }
137 
ExternalRouteConfigToFlagByte(const otExternalRouteConfig & aConfig)138 static uint8_t ExternalRouteConfigToFlagByte(const otExternalRouteConfig &aConfig)
139 {
140     uint8_t flags = 0;
141 
142     switch (aConfig.mPreference)
143     {
144     case OT_ROUTE_PREFERENCE_LOW:
145         flags |= SPINEL_ROUTE_PREFERENCE_LOW;
146         break;
147 
148     case OT_ROUTE_PREFERENCE_HIGH:
149         flags |= SPINEL_ROUTE_PREFERENCE_HIGH;
150         break;
151 
152     case OT_ROUTE_PREFERENCE_MED:
153     default:
154         flags |= SPINEL_ROUTE_PREFERENCE_MEDIUM;
155         break;
156     }
157 
158     if (aConfig.mNat64)
159     {
160         flags |= SPINEL_ROUTE_FLAG_NAT64;
161     }
162 
163     return flags;
164 }
165 
LinkFlagsToFlagByte(bool aRxOnWhenIdle,bool aDeviceType,bool aNetworkData)166 uint8_t NcpBase::LinkFlagsToFlagByte(bool aRxOnWhenIdle, bool aDeviceType, bool aNetworkData)
167 {
168     uint8_t flags(0);
169 
170     if (aRxOnWhenIdle)
171     {
172         flags |= SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE;
173     }
174 
175     if (aDeviceType)
176     {
177         flags |= SPINEL_THREAD_MODE_FULL_THREAD_DEV;
178     }
179 
180     if (aNetworkData)
181     {
182         flags |= SPINEL_THREAD_MODE_FULL_NETWORK_DATA;
183     }
184 
185     return flags;
186 }
187 
EncodeNeighborInfo(const otNeighborInfo & aNeighborInfo)188 otError NcpBase::EncodeNeighborInfo(const otNeighborInfo &aNeighborInfo)
189 {
190     otError error;
191     uint8_t modeFlags;
192 
193     modeFlags = LinkFlagsToFlagByte(aNeighborInfo.mRxOnWhenIdle, aNeighborInfo.mFullThreadDevice,
194                                     aNeighborInfo.mFullNetworkData);
195 
196     SuccessOrExit(error = mEncoder.OpenStruct());
197 
198     SuccessOrExit(error = mEncoder.WriteEui64(aNeighborInfo.mExtAddress));
199     SuccessOrExit(error = mEncoder.WriteUint16(aNeighborInfo.mRloc16));
200     SuccessOrExit(error = mEncoder.WriteUint32(aNeighborInfo.mAge));
201     SuccessOrExit(error = mEncoder.WriteUint8(aNeighborInfo.mLinkQualityIn));
202     SuccessOrExit(error = mEncoder.WriteInt8(aNeighborInfo.mAverageRssi));
203     SuccessOrExit(error = mEncoder.WriteUint8(modeFlags));
204     SuccessOrExit(error = mEncoder.WriteBool(aNeighborInfo.mIsChild));
205     SuccessOrExit(error = mEncoder.WriteUint32(aNeighborInfo.mLinkFrameCounter));
206     SuccessOrExit(error = mEncoder.WriteUint32(aNeighborInfo.mMleFrameCounter));
207     SuccessOrExit(error = mEncoder.WriteInt8(aNeighborInfo.mLastRssi));
208 
209     SuccessOrExit(error = mEncoder.CloseStruct());
210 
211 exit:
212     return error;
213 }
214 
215 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
EncodeLinkMetricsValues(const otLinkMetricsValues * aMetricsValues)216 otError NcpBase::EncodeLinkMetricsValues(const otLinkMetricsValues *aMetricsValues)
217 {
218     otError error = OT_ERROR_NONE;
219 
220     SuccessOrExit(error = mEncoder.OpenStruct());
221 
222     if (aMetricsValues->mMetrics.mPduCount)
223     {
224         SuccessOrExit(error = mEncoder.OpenStruct());
225         SuccessOrExit(error = mEncoder.WriteUint8(SPINEL_THREAD_LINK_METRIC_PDU_COUNT));
226         SuccessOrExit(error = mEncoder.WriteUint32(aMetricsValues->mPduCountValue));
227         SuccessOrExit(error = mEncoder.CloseStruct());
228     }
229 
230     if (aMetricsValues->mMetrics.mLqi)
231     {
232         SuccessOrExit(error = mEncoder.OpenStruct());
233         SuccessOrExit(error = mEncoder.WriteUint8(SPINEL_THREAD_LINK_METRIC_LQI));
234         SuccessOrExit(error = mEncoder.WriteUint8(aMetricsValues->mLqiValue));
235         SuccessOrExit(error = mEncoder.CloseStruct());
236     }
237 
238     if (aMetricsValues->mMetrics.mLinkMargin)
239     {
240         SuccessOrExit(error = mEncoder.OpenStruct());
241         SuccessOrExit(error = mEncoder.WriteUint8(SPINEL_THREAD_LINK_METRIC_LINK_MARGIN));
242         SuccessOrExit(error = mEncoder.WriteUint8(aMetricsValues->mLinkMarginValue));
243         SuccessOrExit(error = mEncoder.CloseStruct());
244     }
245 
246     if (aMetricsValues->mMetrics.mRssi)
247     {
248         SuccessOrExit(error = mEncoder.OpenStruct());
249         SuccessOrExit(error = mEncoder.WriteUint8(SPINEL_THREAD_LINK_METRIC_RSSI));
250         SuccessOrExit(error = mEncoder.WriteInt8(aMetricsValues->mRssiValue));
251         SuccessOrExit(error = mEncoder.CloseStruct());
252     }
253 
254     SuccessOrExit(error = mEncoder.CloseStruct());
255 
256 exit:
257     return error;
258 }
259 #endif
260 
261 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
HandlePropertySet(void)262 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_CSL_PERIOD>(void)
263 {
264     uint32_t cslPeriod;
265     otError  error = OT_ERROR_NONE;
266 
267     SuccessOrExit(error = mDecoder.ReadUint32(cslPeriod));
268 
269     error = otLinkSetCslPeriod(mInstance, cslPeriod);
270 
271 exit:
272     return error;
273 }
274 
HandlePropertyGet(void)275 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_CSL_PERIOD>(void)
276 {
277     return mEncoder.WriteUint32(otLinkGetCslPeriod(mInstance));
278 }
279 
HandlePropertySet(void)280 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_CSL_TIMEOUT>(void)
281 {
282     uint32_t cslTimeout;
283     otError  error = OT_ERROR_NONE;
284 
285     SuccessOrExit(error = mDecoder.ReadUint32(cslTimeout));
286 
287     error = otLinkSetCslTimeout(mInstance, cslTimeout);
288 
289 exit:
290     return error;
291 }
292 
HandlePropertyGet(void)293 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_CSL_TIMEOUT>(void)
294 {
295     return mEncoder.WriteUint32(otLinkGetCslTimeout(mInstance));
296 }
297 
HandlePropertySet(void)298 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_CSL_CHANNEL>(void)
299 {
300     uint8_t cslChannel;
301     otError error = OT_ERROR_NONE;
302 
303     SuccessOrExit(error = mDecoder.ReadUint8(cslChannel));
304 
305     error = otLinkSetCslChannel(mInstance, cslChannel);
306 
307 exit:
308     return error;
309 }
310 
HandlePropertyGet(void)311 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_CSL_CHANNEL>(void)
312 {
313     return mEncoder.WriteUint8(otLinkGetCslChannel(mInstance));
314 }
315 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
316 
317 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
HandlePropertySet(void)318 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_MLR_REQUEST>(void)
319 {
320     otError      error = OT_ERROR_NONE;
321     otIp6Address addresses[OT_IP6_MAX_MLR_ADDRESSES];
322     uint8_t      addressesCount = 0U;
323     bool         timeoutPresent = false;
324     uint32_t     timeout;
325 
326     SuccessOrExit(error = mDecoder.OpenStruct());
327 
328     while (mDecoder.GetRemainingLengthInStruct())
329     {
330         VerifyOrExit(addressesCount < Ip6AddressesTlv::kMaxAddresses, error = OT_ERROR_NO_BUFS);
331         SuccessOrExit(error = mDecoder.ReadIp6Address(addresses[addressesCount]));
332         ++addressesCount;
333     }
334 
335     SuccessOrExit(error = mDecoder.CloseStruct());
336 
337     while (mDecoder.GetRemainingLengthInStruct())
338     {
339         uint8_t paramId;
340 
341         SuccessOrExit(error = mDecoder.OpenStruct());
342 
343         SuccessOrExit(error = mDecoder.ReadUint8(paramId));
344 
345         switch (paramId)
346         {
347         case SPINEL_THREAD_MLR_PARAMID_TIMEOUT:
348             SuccessOrExit(error = mDecoder.ReadUint32(timeout));
349             timeoutPresent = true;
350             break;
351 
352         default:
353             ExitNow(error = OT_ERROR_INVALID_ARGS);
354         }
355 
356         SuccessOrExit(error = mDecoder.CloseStruct());
357     }
358 
359     SuccessOrExit(error = otIp6RegisterMulticastListeners(mInstance, addresses, addressesCount,
360                                                           timeoutPresent ? &timeout : nullptr,
361                                                           &NcpBase::HandleMlrRegResult_Jump, this));
362 exit:
363     return error;
364 }
365 
HandleMlrRegResult_Jump(void * aContext,otError aError,uint8_t aMlrStatus,const otIp6Address * aFailedAddresses,uint8_t aFailedAddressNum)366 void NcpBase::HandleMlrRegResult_Jump(void               *aContext,
367                                       otError             aError,
368                                       uint8_t             aMlrStatus,
369                                       const otIp6Address *aFailedAddresses,
370                                       uint8_t             aFailedAddressNum)
371 {
372     static_cast<NcpBase *>(aContext)->HandleMlrRegResult(aError, aMlrStatus, aFailedAddresses, aFailedAddressNum);
373 }
374 
HandleMlrRegResult(otError aError,uint8_t aMlrStatus,const otIp6Address * aFailedAddresses,uint8_t aFailedAddressNum)375 void NcpBase::HandleMlrRegResult(otError             aError,
376                                  uint8_t             aMlrStatus,
377                                  const otIp6Address *aFailedAddresses,
378                                  uint8_t             aFailedAddressNum)
379 {
380     SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS,
381                                       SPINEL_PROP_THREAD_MLR_RESPONSE));
382 
383     SuccessOrExit(mEncoder.WriteUint8(static_cast<uint8_t>(ThreadErrorToSpinelStatus(aError))));
384     SuccessOrExit(mEncoder.WriteUint8(aMlrStatus));
385 
386     SuccessOrExit(mEncoder.OpenStruct());
387 
388     if (aError == OT_ERROR_NONE)
389     {
390         for (size_t i = 0U; i < aFailedAddressNum; ++i)
391         {
392             SuccessOrExit(mEncoder.WriteIp6Address(aFailedAddresses[i]));
393         }
394     }
395 
396     SuccessOrExit(mEncoder.CloseStruct());
397 
398     SuccessOrExit(mEncoder.EndFrame());
399 
400 exit:
401     return;
402 }
403 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
404 
405 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
HandlePropertyGet(void)406 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_BACKBONE_ROUTER_PRIMARY>(void)
407 {
408     otError                error = OT_ERROR_NONE;
409     otBackboneRouterConfig bbrConfig;
410 
411     SuccessOrExit(error = otBackboneRouterGetPrimary(mInstance, &bbrConfig));
412 
413     SuccessOrExit(error = mEncoder.WriteUint16(bbrConfig.mServer16));
414     SuccessOrExit(error = mEncoder.WriteUint16(bbrConfig.mReregistrationDelay));
415     SuccessOrExit(error = mEncoder.WriteUint32(bbrConfig.mMlrTimeout));
416     SuccessOrExit(error = mEncoder.WriteUint8(bbrConfig.mSequenceNumber));
417 
418 exit:
419     return error;
420 }
421 #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
422 
HandlePropertyGet(void)423 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_DATA_POLL_PERIOD>(void)
424 {
425     return mEncoder.WriteUint32(otLinkGetPollPeriod(mInstance));
426 }
427 
HandlePropertySet(void)428 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_DATA_POLL_PERIOD>(void)
429 {
430     uint32_t pollPeriod;
431     otError  error = OT_ERROR_NONE;
432 
433     SuccessOrExit(error = mDecoder.ReadUint32(pollPeriod));
434 
435     error = otLinkSetPollPeriod(mInstance, pollPeriod);
436 
437 exit:
438     return error;
439 }
440 
HandlePropertyGet(void)441 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_EXTENDED_ADDR>(void)
442 {
443     return mEncoder.WriteEui64(*otLinkGetExtendedAddress(mInstance));
444 }
445 
HandlePropertyGet(void)446 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_MAX_RETRY_NUMBER_DIRECT>(void)
447 {
448     return mEncoder.WriteUint8(otLinkGetMaxFrameRetriesDirect(mInstance));
449 }
450 
HandlePropertySet(void)451 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_MAX_RETRY_NUMBER_DIRECT>(void)
452 {
453     uint8_t maxFrameRetriesDirect;
454     otError error = OT_ERROR_NONE;
455 
456     SuccessOrExit(error = mDecoder.ReadUint8(maxFrameRetriesDirect));
457     otLinkSetMaxFrameRetriesDirect(mInstance, maxFrameRetriesDirect);
458 
459 exit:
460     return error;
461 }
462 
HandlePropertySet(void)463 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_CHAN_SUPPORTED>(void)
464 {
465     uint32_t newMask = 0;
466     otError  error   = OT_ERROR_NONE;
467 
468     SuccessOrExit(error = DecodeChannelMask(newMask));
469     error = otLinkSetSupportedChannelMask(mInstance, newMask);
470 
471 exit:
472     return error;
473 }
474 
CommandHandler_NET_CLEAR(uint8_t aHeader)475 otError NcpBase::CommandHandler_NET_CLEAR(uint8_t aHeader)
476 {
477     return PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(otInstanceErasePersistentInfo(mInstance)));
478 }
479 
HandlePropertyGet(void)480 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_SAVED>(void)
481 {
482     return mEncoder.WriteBool(otDatasetIsCommissioned(mInstance));
483 }
484 
HandlePropertyGet(void)485 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_IF_UP>(void)
486 {
487     return mEncoder.WriteBool(otIp6IsEnabled(mInstance));
488 }
489 
HandlePropertySet(void)490 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_IF_UP>(void)
491 {
492     bool    enabled = false;
493     otError error   = OT_ERROR_NONE;
494 
495     SuccessOrExit(error = mDecoder.ReadBool(enabled));
496 
497     error = otIp6SetEnabled(mInstance, enabled);
498 
499 exit:
500     return error;
501 }
502 
HandlePropertyGet(void)503 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_STACK_UP>(void)
504 {
505     return mEncoder.WriteBool(otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED);
506 }
507 
HandlePropertySet(void)508 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_STACK_UP>(void)
509 {
510     bool    enabled = false;
511     otError error   = OT_ERROR_NONE;
512 
513     SuccessOrExit(error = mDecoder.ReadBool(enabled));
514 
515     // If the value has changed...
516     if (enabled != (otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED))
517     {
518         if (enabled)
519         {
520             error = otThreadSetEnabled(mInstance, true);
521         }
522         else
523         {
524             error = otThreadSetEnabled(mInstance, false);
525         }
526     }
527 
528 exit:
529     return error;
530 }
531 
HandlePropertyGet(void)532 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_ROLE>(void)
533 {
534     spinel_net_role_t role(SPINEL_NET_ROLE_DETACHED);
535 
536     switch (otThreadGetDeviceRole(mInstance))
537     {
538     case OT_DEVICE_ROLE_DISABLED:
539         role = SPINEL_NET_ROLE_DISABLED;
540         break;
541 
542     case OT_DEVICE_ROLE_DETACHED:
543         role = SPINEL_NET_ROLE_DETACHED;
544         break;
545 
546     case OT_DEVICE_ROLE_CHILD:
547         role = SPINEL_NET_ROLE_CHILD;
548         break;
549 
550     case OT_DEVICE_ROLE_ROUTER:
551         role = SPINEL_NET_ROLE_ROUTER;
552         break;
553 
554     case OT_DEVICE_ROLE_LEADER:
555         role = SPINEL_NET_ROLE_LEADER;
556         break;
557     }
558 
559     return mEncoder.WriteUint8(role);
560 }
561 
HandlePropertySet(void)562 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_ROLE>(void)
563 {
564     unsigned int role  = 0;
565     otError      error = OT_ERROR_NONE;
566 
567     SuccessOrExit(error = mDecoder.ReadUintPacked(role));
568 
569     switch (role)
570     {
571     case SPINEL_NET_ROLE_DETACHED:
572         error = otThreadBecomeDetached(mInstance);
573         break;
574 
575 #if OPENTHREAD_FTD
576     case SPINEL_NET_ROLE_ROUTER:
577         error = otThreadBecomeRouter(mInstance);
578         break;
579 
580     case SPINEL_NET_ROLE_LEADER:
581         error = otThreadBecomeLeader(mInstance);
582         break;
583 #endif // OPENTHREAD_FTD
584 
585     case SPINEL_NET_ROLE_CHILD:
586         error = otThreadBecomeChild(mInstance);
587         break;
588     }
589 
590 exit:
591     return error;
592 }
593 
HandlePropertyGet(void)594 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_NETWORK_NAME>(void)
595 {
596     return mEncoder.WriteUtf8(otThreadGetNetworkName(mInstance));
597 }
598 
HandlePropertySet(void)599 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_NETWORK_NAME>(void)
600 {
601     const char *string = nullptr;
602     otError     error  = OT_ERROR_NONE;
603 
604     SuccessOrExit(error = mDecoder.ReadUtf8(string));
605 
606     error = otThreadSetNetworkName(mInstance, string);
607 
608 exit:
609     return error;
610 }
611 
HandlePropertyGet(void)612 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_XPANID>(void)
613 {
614     return mEncoder.WriteData(otThreadGetExtendedPanId(mInstance)->m8, sizeof(spinel_net_xpanid_t));
615 }
616 
HandlePropertySet(void)617 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_XPANID>(void)
618 {
619     const uint8_t *ptr = nullptr;
620     uint16_t       len;
621     otError        error = OT_ERROR_NONE;
622 
623     SuccessOrExit(error = mDecoder.ReadData(ptr, len));
624 
625     VerifyOrExit(len == sizeof(spinel_net_xpanid_t), error = OT_ERROR_PARSE);
626 
627     error = otThreadSetExtendedPanId(mInstance, reinterpret_cast<const otExtendedPanId *>(ptr));
628 
629 exit:
630     return error;
631 }
632 
HandlePropertyGet(void)633 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_NETWORK_KEY>(void)
634 {
635     otNetworkKey networkKey;
636 
637     otThreadGetNetworkKey(mInstance, &networkKey);
638 
639     return mEncoder.WriteData(networkKey.m8, OT_NETWORK_KEY_SIZE);
640 }
641 
HandlePropertySet(void)642 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_NETWORK_KEY>(void)
643 {
644     const uint8_t *ptr = nullptr;
645     uint16_t       len;
646     otError        error = OT_ERROR_NONE;
647 
648     SuccessOrExit(error = mDecoder.ReadData(ptr, len));
649 
650     VerifyOrExit(len == OT_NETWORK_KEY_SIZE, error = OT_ERROR_PARSE);
651 
652     error = otThreadSetNetworkKey(mInstance, reinterpret_cast<const otNetworkKey *>(ptr));
653 
654 exit:
655     return error;
656 }
657 
HandlePropertyGet(void)658 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER>(void)
659 {
660     return mEncoder.WriteUint32(otThreadGetKeySequenceCounter(mInstance));
661 }
662 
HandlePropertySet(void)663 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER>(void)
664 {
665     uint32_t keySeqCounter;
666     otError  error = OT_ERROR_NONE;
667 
668     SuccessOrExit(error = mDecoder.ReadUint32(keySeqCounter));
669 
670     otThreadSetKeySequenceCounter(mInstance, keySeqCounter);
671 
672 exit:
673     return error;
674 }
675 
HandlePropertyGet(void)676 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_PARTITION_ID>(void)
677 {
678     return mEncoder.WriteUint32(otThreadGetPartitionId(mInstance));
679 }
680 
HandlePropertyGet(void)681 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME>(void)
682 {
683     return mEncoder.WriteUint32(otThreadGetKeySwitchGuardTime(mInstance));
684 }
685 
HandlePropertySet(void)686 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME>(void)
687 {
688     uint32_t keyGuardTime;
689     otError  error = OT_ERROR_NONE;
690 
691     SuccessOrExit(error = mDecoder.ReadUint32(keyGuardTime));
692 
693     otThreadSetKeySwitchGuardTime(mInstance, keyGuardTime);
694 
695 exit:
696     return error;
697 }
698 
HandlePropertyGet(void)699 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_NETWORK_DATA_VERSION>(void)
700 {
701     return mEncoder.WriteUint8(otNetDataGetVersion(mInstance));
702 }
703 
HandlePropertyGet(void)704 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION>(void)
705 {
706     return mEncoder.WriteUint8(otNetDataGetStableVersion(mInstance));
707 }
708 
709 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
HandlePropertyGet(void)710 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_NETWORK_DATA>(void)
711 {
712     uint8_t networkData[255];
713     uint8_t networkDataLen = 255;
714 
715     IgnoreError(otBorderRouterGetNetData(mInstance,
716                                          false, // Stable?
717                                          networkData, &networkDataLen));
718 
719     return mEncoder.WriteData(networkData, networkDataLen);
720 }
721 
HandlePropertyGet(void)722 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_STABLE_NETWORK_DATA>(void)
723 {
724     uint8_t networkData[255];
725     uint8_t networkDataLen = 255;
726 
727     IgnoreError(otBorderRouterGetNetData(mInstance,
728                                          true, // Stable?
729                                          networkData, &networkDataLen));
730 
731     return mEncoder.WriteData(networkData, networkDataLen);
732 }
733 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
734 
HandlePropertyGet(void)735 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_LEADER_NETWORK_DATA>(void)
736 {
737     uint8_t networkData[255];
738     uint8_t networkDataLen = 255;
739 
740     IgnoreError(otNetDataGet(mInstance,
741                              false, // Stable?
742                              networkData, &networkDataLen));
743 
744     return mEncoder.WriteData(networkData, networkDataLen);
745 }
746 
HandlePropertyGet(void)747 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_STABLE_LEADER_NETWORK_DATA>(void)
748 {
749     uint8_t networkData[255];
750     uint8_t networkDataLen = 255;
751 
752     IgnoreError(otNetDataGet(mInstance,
753                              true, // Stable?
754                              networkData, &networkDataLen));
755 
756     return mEncoder.WriteData(networkData, networkDataLen);
757 }
758 
HandlePropertyGet(void)759 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_LEADER_RID>(void)
760 {
761     return mEncoder.WriteUint8(otThreadGetLeaderRouterId(mInstance));
762 }
763 
HandlePropertyGet(void)764 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_LEADER_ADDR>(void)
765 {
766     otError      error = OT_ERROR_NONE;
767     otIp6Address address;
768 
769     error = otThreadGetLeaderRloc(mInstance, &address);
770 
771     if (error == OT_ERROR_NONE)
772     {
773         error = mEncoder.WriteIp6Address(address);
774     }
775     else
776     {
777         error = mEncoder.OverwriteWithLastStatusError(ThreadErrorToSpinelStatus(error));
778     }
779 
780     return error;
781 }
782 
HandlePropertyGet(void)783 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_PARENT>(void)
784 {
785     otError error = OT_ERROR_NONE;
786 
787     otRouterInfo parentInfo;
788 
789     error = otThreadGetParentInfo(mInstance, &parentInfo);
790 
791     if (error == OT_ERROR_NONE)
792     {
793         if (parentInfo.mLinkEstablished)
794         {
795             int8_t averageRssi;
796             int8_t lastRssi;
797 
798             IgnoreError(otThreadGetParentAverageRssi(mInstance, &averageRssi));
799             IgnoreError(otThreadGetParentLastRssi(mInstance, &lastRssi));
800 
801             SuccessOrExit(error = mEncoder.WriteEui64(parentInfo.mExtAddress));
802             SuccessOrExit(error = mEncoder.WriteUint16(parentInfo.mRloc16));
803             SuccessOrExit(error = mEncoder.WriteUint32(parentInfo.mAge));
804             SuccessOrExit(error = mEncoder.WriteInt8(averageRssi));
805             SuccessOrExit(error = mEncoder.WriteInt8(lastRssi));
806             SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mLinkQualityIn));
807             SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mLinkQualityOut));
808             SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mVersion));
809 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
810             SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mCslClockAccuracy));
811             SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mCslUncertainty));
812 #endif
813         }
814         else
815         {
816             SuccessOrExit(error = mEncoder.OverwriteWithLastStatusError(SPINEL_STATUS_ITEM_NOT_FOUND));
817         }
818     }
819     else
820     {
821         error = mEncoder.OverwriteWithLastStatusError(ThreadErrorToSpinelStatus(error));
822     }
823 
824 exit:
825     return error;
826 }
827 
HandlePropertyGet(void)828 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_NEIGHBOR_TABLE>(void)
829 {
830     otError                error = OT_ERROR_NONE;
831     otNeighborInfoIterator iter  = OT_NEIGHBOR_INFO_ITERATOR_INIT;
832     otNeighborInfo         neighInfo;
833 
834     while (otThreadGetNextNeighborInfo(mInstance, &iter, &neighInfo) == OT_ERROR_NONE)
835     {
836         SuccessOrExit(error = EncodeNeighborInfo(neighInfo));
837     }
838 
839 exit:
840     return error;
841 }
842 
HandlePropertyGet(void)843 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_NEIGHBOR_TABLE_ERROR_RATES>(void)
844 {
845     otError                error = OT_ERROR_NONE;
846     otNeighborInfoIterator iter  = OT_NEIGHBOR_INFO_ITERATOR_INIT;
847     otNeighborInfo         neighInfo;
848 
849     while (otThreadGetNextNeighborInfo(mInstance, &iter, &neighInfo) == OT_ERROR_NONE)
850     {
851         SuccessOrExit(error = mEncoder.OpenStruct());
852 
853         SuccessOrExit(error = mEncoder.WriteEui64(neighInfo.mExtAddress));
854         SuccessOrExit(error = mEncoder.WriteUint16(neighInfo.mRloc16));
855         SuccessOrExit(error = mEncoder.WriteUint16(neighInfo.mFrameErrorRate));
856         SuccessOrExit(error = mEncoder.WriteUint16(neighInfo.mMessageErrorRate));
857         SuccessOrExit(error = mEncoder.WriteInt8(neighInfo.mAverageRssi));
858         SuccessOrExit(error = mEncoder.WriteInt8(neighInfo.mLastRssi));
859 
860         SuccessOrExit(error = mEncoder.CloseStruct());
861     }
862 
863 exit:
864     return error;
865 }
866 
HandlePropertyGet(void)867 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_ASSISTING_PORTS>(void)
868 {
869     otError         error      = OT_ERROR_NONE;
870     uint8_t         numEntries = 0;
871     const uint16_t *ports      = otIp6GetUnsecurePorts(mInstance, &numEntries);
872 
873     for (; numEntries != 0; ports++, numEntries--)
874     {
875         SuccessOrExit(error = mEncoder.WriteUint16(*ports));
876     }
877 
878 exit:
879     return error;
880 }
881 
HandlePropertySet(void)882 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_ASSISTING_PORTS>(void)
883 {
884     otError error = OT_ERROR_NONE;
885 
886     // First, we need to remove all of the current assisting ports.
887     otIp6RemoveAllUnsecurePorts(mInstance);
888 
889     while (mDecoder.GetRemainingLengthInStruct() >= sizeof(uint16_t))
890     {
891         uint16_t port;
892 
893         SuccessOrExit(error = mDecoder.ReadUint16(port));
894         SuccessOrExit(error = otIp6AddUnsecurePort(mInstance, port));
895     }
896 
897 exit:
898 
899     if (error != OT_ERROR_NONE)
900     {
901         // We had an error, but we've actually changed
902         // the state of these ports, so we need to report
903         // those incomplete changes via an asynchronous
904         // change event.
905         IgnoreError(
906             WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_THREAD_ASSISTING_PORTS));
907     }
908 
909     return error;
910 }
911 
HandlePropertyGet(void)912 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE>(void)
913 {
914     return mEncoder.WriteBool(mAllowLocalNetworkDataChange);
915 }
916 
917 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
HandlePropertySet(void)918 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE>(void)
919 {
920     bool    value                    = false;
921     otError error                    = OT_ERROR_NONE;
922     bool    shouldRegisterWithLeader = false;
923 
924     SuccessOrExit(error = mDecoder.ReadBool(value));
925 
926     // Register any net data changes on transition from `true` to `false`.
927     shouldRegisterWithLeader = mAllowLocalNetworkDataChange && !value;
928 
929     mAllowLocalNetworkDataChange = value;
930 
931 exit:
932 
933     if (shouldRegisterWithLeader)
934     {
935         IgnoreError(otBorderRouterRegister(mInstance));
936     }
937 
938     return error;
939 }
940 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
941 
HandlePropertyGet(void)942 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_ON_MESH_NETS>(void)
943 {
944     otError               error = OT_ERROR_NONE;
945     otBorderRouterConfig  borderRouterConfig;
946     otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT;
947 
948     // Fill from non-local network data first
949     while (otNetDataGetNextOnMeshPrefix(mInstance, &iter, &borderRouterConfig) == OT_ERROR_NONE)
950     {
951         SuccessOrExit(error = mEncoder.OpenStruct());
952 
953         SuccessOrExit(error = mEncoder.WriteIp6Address(borderRouterConfig.mPrefix.mPrefix));
954         SuccessOrExit(error = mEncoder.WriteUint8(borderRouterConfig.mPrefix.mLength));
955         SuccessOrExit(error = mEncoder.WriteBool(borderRouterConfig.mStable));
956         SuccessOrExit(error = mEncoder.WriteUint8(BorderRouterConfigToFlagByte(borderRouterConfig)));
957         SuccessOrExit(error = mEncoder.WriteBool(false)); // isLocal
958         SuccessOrExit(error = mEncoder.WriteUint16(borderRouterConfig.mRloc16));
959         SuccessOrExit(error = mEncoder.WriteUint8(BorderRouterConfigToFlagByteExtended(borderRouterConfig)));
960 
961         SuccessOrExit(error = mEncoder.CloseStruct());
962     }
963 
964 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
965 
966     iter = OT_NETWORK_DATA_ITERATOR_INIT;
967 
968     // Fill from local network data last
969     while (otBorderRouterGetNextOnMeshPrefix(mInstance, &iter, &borderRouterConfig) == OT_ERROR_NONE)
970     {
971         SuccessOrExit(error = mEncoder.OpenStruct());
972 
973         SuccessOrExit(error = mEncoder.WriteIp6Address(borderRouterConfig.mPrefix.mPrefix));
974         SuccessOrExit(error = mEncoder.WriteUint8(borderRouterConfig.mPrefix.mLength));
975         SuccessOrExit(error = mEncoder.WriteBool(borderRouterConfig.mStable));
976         SuccessOrExit(error = mEncoder.WriteUint8(BorderRouterConfigToFlagByte(borderRouterConfig)));
977         SuccessOrExit(error = mEncoder.WriteBool(true)); // isLocal
978         SuccessOrExit(error = mEncoder.WriteUint16(borderRouterConfig.mRloc16));
979         SuccessOrExit(error = mEncoder.WriteUint8(BorderRouterConfigToFlagByteExtended(borderRouterConfig)));
980 
981         SuccessOrExit(error = mEncoder.CloseStruct());
982     }
983 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
984 
985 exit:
986     return error;
987 }
988 
989 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
HandlePropertyInsert(void)990 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_THREAD_ON_MESH_NETS>(void)
991 {
992     otError              error = OT_ERROR_NONE;
993     otBorderRouterConfig borderRouterConfig;
994     bool                 stable = false;
995     bool                 isLocal;
996     uint8_t              flags         = 0;
997     uint8_t              flagsExtended = 0;
998     uint8_t              prefixLength;
999     uint16_t             rloc16;
1000 
1001     memset(&borderRouterConfig, 0, sizeof(otBorderRouterConfig));
1002 
1003     VerifyOrExit(mAllowLocalNetworkDataChange, error = OT_ERROR_INVALID_STATE);
1004 
1005     SuccessOrExit(error = mDecoder.ReadIp6Address(borderRouterConfig.mPrefix.mPrefix));
1006     SuccessOrExit(error = mDecoder.ReadUint8(prefixLength));
1007     SuccessOrExit(error = mDecoder.ReadBool(stable));
1008     SuccessOrExit(error = mDecoder.ReadUint8(flags));
1009 
1010     borderRouterConfig.mPrefix.mLength = prefixLength;
1011     borderRouterConfig.mStable         = stable;
1012     borderRouterConfig.mPreference   = ((flags & SPINEL_NET_FLAG_PREFERENCE_MASK) >> SPINEL_NET_FLAG_PREFERENCE_OFFSET);
1013     borderRouterConfig.mPreferred    = ((flags & SPINEL_NET_FLAG_PREFERRED) != 0);
1014     borderRouterConfig.mSlaac        = ((flags & SPINEL_NET_FLAG_SLAAC) != 0);
1015     borderRouterConfig.mDhcp         = ((flags & SPINEL_NET_FLAG_DHCP) != 0);
1016     borderRouterConfig.mConfigure    = ((flags & SPINEL_NET_FLAG_CONFIGURE) != 0);
1017     borderRouterConfig.mDefaultRoute = ((flags & SPINEL_NET_FLAG_DEFAULT_ROUTE) != 0);
1018     borderRouterConfig.mOnMesh       = ((flags & SPINEL_NET_FLAG_ON_MESH) != 0);
1019 
1020     // A new field 'TLV flags extended' has been added to the SPINEL_PROP_THREAD_ON_MESH_NETS property.
1021     // To correctly handle a new field for INSERT command, the additional fields 'isLocal' and 'rloc16' are read and
1022     // ignored.
1023     if ((mDecoder.ReadBool(isLocal) == OT_ERROR_NONE) && (mDecoder.ReadUint16(rloc16) == OT_ERROR_NONE) &&
1024         (mDecoder.ReadUint8(flagsExtended) == OT_ERROR_NONE))
1025     {
1026         borderRouterConfig.mNdDns = ((flagsExtended & SPINEL_NET_FLAG_EXT_DNS) != 0);
1027         borderRouterConfig.mDp    = ((flagsExtended & SPINEL_NET_FLAG_EXT_DP) != 0);
1028     }
1029 
1030     error = otBorderRouterAddOnMeshPrefix(mInstance, &borderRouterConfig);
1031 
1032 exit:
1033     return error;
1034 }
1035 
HandlePropertyRemove(void)1036 template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_THREAD_ON_MESH_NETS>(void)
1037 {
1038     otError     error = OT_ERROR_NONE;
1039     otIp6Prefix ip6Prefix;
1040     uint8_t     prefixLength;
1041 
1042     memset(&ip6Prefix, 0, sizeof(otIp6Prefix));
1043 
1044     VerifyOrExit(mAllowLocalNetworkDataChange, error = OT_ERROR_INVALID_STATE);
1045 
1046     SuccessOrExit(error = mDecoder.ReadIp6Address(ip6Prefix.mPrefix));
1047     SuccessOrExit(error = mDecoder.ReadUint8(prefixLength));
1048 
1049     ip6Prefix.mLength = prefixLength;
1050 
1051     error = otBorderRouterRemoveOnMeshPrefix(mInstance, &ip6Prefix);
1052 
1053     // If prefix was not on the list, "remove" command can be considered
1054     // successful.
1055 
1056     if (error == OT_ERROR_NOT_FOUND)
1057     {
1058         error = OT_ERROR_NONE;
1059     }
1060 
1061 exit:
1062     return error;
1063 }
1064 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
1065 
1066 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
1067 
HandlePropertyGet(void)1068 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SERVER_ALLOW_LOCAL_DATA_CHANGE>(void)
1069 {
1070     return mEncoder.WriteBool(mAllowLocalServerDataChange);
1071 }
1072 
HandlePropertySet(void)1073 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SERVER_ALLOW_LOCAL_DATA_CHANGE>(void)
1074 {
1075     bool    value                    = false;
1076     otError error                    = OT_ERROR_NONE;
1077     bool    shouldRegisterWithLeader = false;
1078 
1079     SuccessOrExit(error = mDecoder.ReadBool(value));
1080 
1081     // Register any server data changes on transition from `true` to `false`.
1082     shouldRegisterWithLeader = mAllowLocalServerDataChange && !value;
1083 
1084     mAllowLocalServerDataChange = value;
1085 
1086 exit:
1087 
1088     if (shouldRegisterWithLeader)
1089     {
1090         IgnoreError(otServerRegister(mInstance));
1091     }
1092 
1093     return error;
1094 }
1095 
HandlePropertyInsert(void)1096 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_SERVER_SERVICES>(void)
1097 {
1098     otError         error = OT_ERROR_NONE;
1099     otServiceConfig cfg;
1100     bool            stable;
1101     const uint8_t  *data;
1102     uint16_t        dataLen;
1103 
1104     VerifyOrExit(mAllowLocalServerDataChange, error = OT_ERROR_INVALID_STATE);
1105 
1106     SuccessOrExit(error = mDecoder.ReadUint32(cfg.mEnterpriseNumber));
1107     SuccessOrExit(error = mDecoder.ReadDataWithLen(data, dataLen));
1108 
1109     VerifyOrExit((dataLen <= sizeof(cfg.mServiceData)), error = OT_ERROR_INVALID_ARGS);
1110     memcpy(cfg.mServiceData, data, dataLen);
1111 
1112     static_assert((sizeof(cfg.mServiceData) <= UINT8_MAX), "Cannot handle full range of buffer length");
1113     cfg.mServiceDataLength = static_cast<uint8_t>(dataLen);
1114 
1115     SuccessOrExit(error = mDecoder.ReadBool(stable));
1116     cfg.mServerConfig.mStable = stable;
1117     SuccessOrExit(error = mDecoder.ReadDataWithLen(data, dataLen));
1118 
1119     VerifyOrExit((dataLen <= sizeof(cfg.mServerConfig.mServerData)), error = OT_ERROR_INVALID_ARGS);
1120     memcpy(cfg.mServerConfig.mServerData, data, dataLen);
1121 
1122     static_assert((sizeof(cfg.mServerConfig.mServerData) <= UINT8_MAX), "Cannot handle full range of buffer length");
1123     cfg.mServerConfig.mServerDataLength = static_cast<uint8_t>(dataLen);
1124 
1125     SuccessOrExit(error = otServerAddService(mInstance, &cfg));
1126 exit:
1127     return error;
1128 }
1129 
HandlePropertyRemove(void)1130 template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_SERVER_SERVICES>(void)
1131 {
1132     otError error = OT_ERROR_NONE;
1133 
1134     uint32_t       enterpriseNumber;
1135     const uint8_t *serviceData;
1136     uint16_t       serviceDataLength;
1137 
1138     VerifyOrExit(mAllowLocalServerDataChange, error = OT_ERROR_INVALID_STATE);
1139 
1140     SuccessOrExit(error = mDecoder.ReadUint32(enterpriseNumber));
1141     SuccessOrExit(error = mDecoder.ReadDataWithLen(serviceData, serviceDataLength));
1142 
1143     VerifyOrExit(serviceDataLength <= UINT8_MAX, error = OT_ERROR_INVALID_ARGS);
1144 
1145     SuccessOrExit(error = otServerRemoveService(mInstance, enterpriseNumber, serviceData,
1146                                                 static_cast<uint8_t>(serviceDataLength)));
1147 exit:
1148     return error;
1149 }
1150 
HandlePropertyGet(void)1151 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SERVER_SERVICES>(void)
1152 {
1153     otError               error    = OT_ERROR_NONE;
1154     otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
1155     otServiceConfig       cfg;
1156 
1157     while (otServerGetNextService(mInstance, &iterator, &cfg) == OT_ERROR_NONE)
1158     {
1159         SuccessOrExit(error = mEncoder.OpenStruct());
1160 
1161         SuccessOrExit(error = mEncoder.WriteUint32(cfg.mEnterpriseNumber));
1162         SuccessOrExit(error = mEncoder.WriteDataWithLen(cfg.mServiceData, cfg.mServiceDataLength));
1163         SuccessOrExit(error = mEncoder.WriteBool(cfg.mServerConfig.mStable));
1164         SuccessOrExit(
1165             error = mEncoder.WriteDataWithLen(cfg.mServerConfig.mServerData, cfg.mServerConfig.mServerDataLength));
1166         SuccessOrExit(error = mEncoder.WriteUint16(cfg.mServerConfig.mRloc16));
1167 
1168         SuccessOrExit(error = mEncoder.CloseStruct());
1169     }
1170 exit:
1171     return error;
1172 }
1173 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
1174 
HandlePropertyGet(void)1175 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SERVER_LEADER_SERVICES>(void)
1176 {
1177     otError               error    = OT_ERROR_NONE;
1178     otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
1179     otServiceConfig       cfg;
1180 
1181     while (otNetDataGetNextService(mInstance, &iterator, &cfg) == OT_ERROR_NONE)
1182     {
1183         SuccessOrExit(error = mEncoder.OpenStruct());
1184 
1185         SuccessOrExit(error = mEncoder.WriteUint8(cfg.mServiceId));
1186         SuccessOrExit(error = mEncoder.WriteUint32(cfg.mEnterpriseNumber));
1187         SuccessOrExit(error = mEncoder.WriteDataWithLen(cfg.mServiceData, cfg.mServiceDataLength));
1188         SuccessOrExit(error = mEncoder.WriteBool(cfg.mServerConfig.mStable));
1189         SuccessOrExit(
1190             error = mEncoder.WriteDataWithLen(cfg.mServerConfig.mServerData, cfg.mServerConfig.mServerDataLength));
1191         SuccessOrExit(error = mEncoder.WriteUint16(cfg.mServerConfig.mRloc16));
1192 
1193         SuccessOrExit(error = mEncoder.CloseStruct());
1194     }
1195 exit:
1196     return error;
1197 }
1198 
HandlePropertyGet(void)1199 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_DISCOVERY_SCAN_JOINER_FLAG>(void)
1200 {
1201     return mEncoder.WriteBool(mDiscoveryScanJoinerFlag);
1202 }
1203 
HandlePropertySet(void)1204 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_DISCOVERY_SCAN_JOINER_FLAG>(void)
1205 {
1206     return mDecoder.ReadBool(mDiscoveryScanJoinerFlag);
1207 }
1208 
HandlePropertyGet(void)1209 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING>(void)
1210 {
1211     return mEncoder.WriteBool(mDiscoveryScanEnableFiltering);
1212 }
1213 
HandlePropertySet(void)1214 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING>(void)
1215 {
1216     return mDecoder.ReadBool(mDiscoveryScanEnableFiltering);
1217 }
1218 
HandlePropertyGet(void)1219 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_DISCOVERY_SCAN_PANID>(void)
1220 {
1221     return mEncoder.WriteUint16(mDiscoveryScanPanId);
1222 }
1223 
HandlePropertySet(void)1224 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_DISCOVERY_SCAN_PANID>(void)
1225 {
1226     return mDecoder.ReadUint16(mDiscoveryScanPanId);
1227 }
1228 
EncodeOperationalDataset(const otOperationalDataset & aDataset)1229 otError NcpBase::EncodeOperationalDataset(const otOperationalDataset &aDataset)
1230 {
1231     otError error = OT_ERROR_NONE;
1232 
1233     if (aDataset.mComponents.mIsActiveTimestampPresent)
1234     {
1235         const otTimestamp &activeTimestamp = aDataset.mActiveTimestamp;
1236 
1237         SuccessOrExit(error = mEncoder.OpenStruct());
1238         SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP));
1239         SuccessOrExit(error = mEncoder.WriteUint64(activeTimestamp.mSeconds));
1240         SuccessOrExit(error = mEncoder.CloseStruct());
1241     }
1242 
1243     if (aDataset.mComponents.mIsPendingTimestampPresent)
1244     {
1245         const otTimestamp &pendingTimestamp = aDataset.mPendingTimestamp;
1246 
1247         SuccessOrExit(error = mEncoder.OpenStruct());
1248         SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_PENDING_TIMESTAMP));
1249         SuccessOrExit(error = mEncoder.WriteUint64(pendingTimestamp.mSeconds));
1250         SuccessOrExit(error = mEncoder.CloseStruct());
1251     }
1252 
1253     if (aDataset.mComponents.mIsNetworkKeyPresent)
1254     {
1255         SuccessOrExit(error = mEncoder.OpenStruct());
1256         SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_NET_NETWORK_KEY));
1257         SuccessOrExit(error = mEncoder.WriteData(aDataset.mNetworkKey.m8, OT_NETWORK_KEY_SIZE));
1258         SuccessOrExit(error = mEncoder.CloseStruct());
1259     }
1260 
1261     if (aDataset.mComponents.mIsNetworkNamePresent)
1262     {
1263         SuccessOrExit(error = mEncoder.OpenStruct());
1264         SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_NET_NETWORK_NAME));
1265         SuccessOrExit(error = mEncoder.WriteUtf8(aDataset.mNetworkName.m8));
1266         SuccessOrExit(error = mEncoder.CloseStruct());
1267     }
1268 
1269     if (aDataset.mComponents.mIsExtendedPanIdPresent)
1270     {
1271         SuccessOrExit(error = mEncoder.OpenStruct());
1272         SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_NET_XPANID));
1273         SuccessOrExit(error = mEncoder.WriteData(aDataset.mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE));
1274         SuccessOrExit(error = mEncoder.CloseStruct());
1275     }
1276 
1277     if (aDataset.mComponents.mIsMeshLocalPrefixPresent)
1278     {
1279         otIp6Address addr;
1280 
1281         memcpy(addr.mFields.m8, aDataset.mMeshLocalPrefix.m8, 8);
1282         memset(addr.mFields.m8 + 8, 0, 8); // Zero out the last 8 bytes.
1283 
1284         SuccessOrExit(error = mEncoder.OpenStruct());
1285         SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_IPV6_ML_PREFIX));
1286         SuccessOrExit(error = mEncoder.WriteIp6Address(addr));             // Mesh local prefix
1287         SuccessOrExit(error = mEncoder.WriteUint8(OT_IP6_PREFIX_BITSIZE)); // Prefix length (in bits)
1288         SuccessOrExit(error = mEncoder.CloseStruct());
1289     }
1290 
1291     if (aDataset.mComponents.mIsDelayPresent)
1292     {
1293         SuccessOrExit(error = mEncoder.OpenStruct());
1294         SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_DELAY_TIMER));
1295         SuccessOrExit(error = mEncoder.WriteUint32(aDataset.mDelay));
1296         SuccessOrExit(error = mEncoder.CloseStruct());
1297     }
1298 
1299     if (aDataset.mComponents.mIsPanIdPresent)
1300     {
1301         SuccessOrExit(error = mEncoder.OpenStruct());
1302         SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_MAC_15_4_PANID));
1303         SuccessOrExit(error = mEncoder.WriteUint16(aDataset.mPanId));
1304         SuccessOrExit(error = mEncoder.CloseStruct());
1305     }
1306 
1307     if (aDataset.mComponents.mIsChannelPresent)
1308     {
1309         SuccessOrExit(error = mEncoder.OpenStruct());
1310         SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_PHY_CHAN));
1311 
1312         // The channel is stored in Dataset as `uint16_t` (to accommodate
1313         // larger number of channels in sub-GHz band),  however the current
1314         // definition of `SPINEL_PROP_PHY_CHAN` property limits the channel
1315         // to a `uint8_t`.
1316 
1317         SuccessOrExit(error = mEncoder.WriteUint8(static_cast<uint8_t>(aDataset.mChannel)));
1318         SuccessOrExit(error = mEncoder.CloseStruct());
1319     }
1320 
1321     if (aDataset.mComponents.mIsPskcPresent)
1322     {
1323         SuccessOrExit(error = mEncoder.OpenStruct());
1324         SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_NET_PSKC));
1325         SuccessOrExit(error = mEncoder.WriteData(aDataset.mPskc.m8, sizeof(spinel_net_pskc_t)));
1326         SuccessOrExit(error = mEncoder.CloseStruct());
1327     }
1328 
1329     if (aDataset.mComponents.mIsSecurityPolicyPresent)
1330     {
1331         uint8_t flags[2];
1332 
1333         static_cast<const SecurityPolicy &>(aDataset.mSecurityPolicy).GetFlags(flags, sizeof(flags));
1334         SuccessOrExit(error = mEncoder.OpenStruct());
1335         SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_SECURITY_POLICY));
1336         SuccessOrExit(error = mEncoder.WriteUint16(aDataset.mSecurityPolicy.mRotationTime));
1337         SuccessOrExit(error = mEncoder.WriteUint8(flags[0]));
1338         if (otThreadGetVersion() >= OT_THREAD_VERSION_1_2)
1339         {
1340             SuccessOrExit(error = mEncoder.WriteUint8(flags[1]));
1341         }
1342         SuccessOrExit(error = mEncoder.CloseStruct());
1343     }
1344 
1345     if (aDataset.mComponents.mIsChannelMaskPresent)
1346     {
1347         SuccessOrExit(error = mEncoder.OpenStruct());
1348         SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_PHY_CHAN_SUPPORTED));
1349         SuccessOrExit(error = EncodeChannelMask(aDataset.mChannelMask));
1350         SuccessOrExit(error = mEncoder.CloseStruct());
1351     }
1352 
1353 exit:
1354     return error;
1355 }
1356 
HandlePropertyGet(void)1357 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_ACTIVE_DATASET>(void)
1358 {
1359     otOperationalDataset dataset;
1360 
1361     IgnoreError(otDatasetGetActive(mInstance, &dataset));
1362     return EncodeOperationalDataset(dataset);
1363 }
1364 
HandlePropertyGet(void)1365 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_PENDING_DATASET>(void)
1366 {
1367     otOperationalDataset dataset;
1368 
1369     IgnoreError(otDatasetGetPending(mInstance, &dataset));
1370     return EncodeOperationalDataset(dataset);
1371 }
1372 
DecodeOperationalDataset(otOperationalDataset & aDataset,const uint8_t ** aTlvs,uint8_t * aTlvsLength,const otIp6Address ** aDestIpAddress,bool aAllowEmptyValues)1373 otError NcpBase::DecodeOperationalDataset(otOperationalDataset &aDataset,
1374                                           const uint8_t       **aTlvs,
1375                                           uint8_t              *aTlvsLength,
1376                                           const otIp6Address  **aDestIpAddress,
1377                                           bool                  aAllowEmptyValues)
1378 {
1379     otError error = OT_ERROR_NONE;
1380 
1381     memset(&aDataset, 0, sizeof(otOperationalDataset));
1382 
1383     if (aTlvs != nullptr)
1384     {
1385         *aTlvs = nullptr;
1386     }
1387 
1388     if (aTlvsLength != nullptr)
1389     {
1390         *aTlvsLength = 0;
1391     }
1392 
1393     if (aDestIpAddress != nullptr)
1394     {
1395         *aDestIpAddress = nullptr;
1396     }
1397 
1398     while (!mDecoder.IsAllReadInStruct())
1399     {
1400         unsigned int propKey;
1401 
1402         SuccessOrExit(error = mDecoder.OpenStruct());
1403         SuccessOrExit(error = mDecoder.ReadUintPacked(propKey));
1404 
1405         switch (static_cast<spinel_prop_key_t>(propKey))
1406         {
1407         case SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP:
1408 
1409             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1410             {
1411                 SuccessOrExit(error = mDecoder.ReadUint64(aDataset.mActiveTimestamp.mSeconds));
1412                 aDataset.mActiveTimestamp.mTicks         = 0;
1413                 aDataset.mActiveTimestamp.mAuthoritative = false;
1414             }
1415 
1416             aDataset.mComponents.mIsActiveTimestampPresent = true;
1417             break;
1418 
1419         case SPINEL_PROP_DATASET_PENDING_TIMESTAMP:
1420 
1421             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1422             {
1423                 SuccessOrExit(error = mDecoder.ReadUint64(aDataset.mPendingTimestamp.mSeconds));
1424                 aDataset.mPendingTimestamp.mTicks         = 0;
1425                 aDataset.mPendingTimestamp.mAuthoritative = false;
1426             }
1427 
1428             aDataset.mComponents.mIsPendingTimestampPresent = true;
1429             break;
1430 
1431         case SPINEL_PROP_NET_NETWORK_KEY:
1432 
1433             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1434             {
1435                 const uint8_t *key;
1436                 uint16_t       len;
1437 
1438                 SuccessOrExit(error = mDecoder.ReadData(key, len));
1439                 VerifyOrExit(len == OT_NETWORK_KEY_SIZE, error = OT_ERROR_INVALID_ARGS);
1440                 memcpy(aDataset.mNetworkKey.m8, key, len);
1441             }
1442 
1443             aDataset.mComponents.mIsNetworkKeyPresent = true;
1444             break;
1445 
1446         case SPINEL_PROP_NET_NETWORK_NAME:
1447 
1448             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1449             {
1450                 const char *name;
1451                 size_t      len;
1452 
1453                 SuccessOrExit(error = mDecoder.ReadUtf8(name));
1454                 len = strlen(name);
1455                 VerifyOrExit(len <= OT_NETWORK_NAME_MAX_SIZE, error = OT_ERROR_INVALID_ARGS);
1456                 memcpy(aDataset.mNetworkName.m8, name, len + 1);
1457             }
1458 
1459             aDataset.mComponents.mIsNetworkNamePresent = true;
1460             break;
1461 
1462         case SPINEL_PROP_NET_XPANID:
1463 
1464             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1465             {
1466                 const uint8_t *xpanid;
1467                 uint16_t       len;
1468 
1469                 SuccessOrExit(error = mDecoder.ReadData(xpanid, len));
1470                 VerifyOrExit(len == OT_EXT_PAN_ID_SIZE, error = OT_ERROR_INVALID_ARGS);
1471                 memcpy(aDataset.mExtendedPanId.m8, xpanid, len);
1472             }
1473 
1474             aDataset.mComponents.mIsExtendedPanIdPresent = true;
1475             break;
1476 
1477         case SPINEL_PROP_IPV6_ML_PREFIX:
1478 
1479             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1480             {
1481                 const otIp6Address *addr;
1482                 uint8_t             prefixLen;
1483 
1484                 SuccessOrExit(error = mDecoder.ReadIp6Address(addr));
1485                 SuccessOrExit(error = mDecoder.ReadUint8(prefixLen));
1486                 VerifyOrExit(prefixLen == OT_IP6_PREFIX_BITSIZE, error = OT_ERROR_INVALID_ARGS);
1487                 memcpy(aDataset.mMeshLocalPrefix.m8, addr, OT_MESH_LOCAL_PREFIX_SIZE);
1488             }
1489 
1490             aDataset.mComponents.mIsMeshLocalPrefixPresent = true;
1491             break;
1492 
1493         case SPINEL_PROP_DATASET_DELAY_TIMER:
1494 
1495             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1496             {
1497                 SuccessOrExit(error = mDecoder.ReadUint32(aDataset.mDelay));
1498             }
1499 
1500             aDataset.mComponents.mIsDelayPresent = true;
1501             break;
1502 
1503         case SPINEL_PROP_MAC_15_4_PANID:
1504 
1505             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1506             {
1507                 SuccessOrExit(error = mDecoder.ReadUint16(aDataset.mPanId));
1508             }
1509 
1510             aDataset.mComponents.mIsPanIdPresent = true;
1511             break;
1512 
1513         case SPINEL_PROP_PHY_CHAN:
1514 
1515             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1516             {
1517                 uint8_t channel;
1518 
1519                 SuccessOrExit(error = mDecoder.ReadUint8(channel));
1520                 aDataset.mChannel = channel;
1521             }
1522 
1523             aDataset.mComponents.mIsChannelPresent = true;
1524             break;
1525 
1526         case SPINEL_PROP_NET_PSKC:
1527 
1528             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1529             {
1530                 const uint8_t *psk;
1531                 uint16_t       len;
1532 
1533                 SuccessOrExit(error = mDecoder.ReadData(psk, len));
1534                 VerifyOrExit(len == OT_PSKC_MAX_SIZE, error = OT_ERROR_INVALID_ARGS);
1535                 memcpy(aDataset.mPskc.m8, psk, OT_PSKC_MAX_SIZE);
1536             }
1537 
1538             aDataset.mComponents.mIsPskcPresent = true;
1539             break;
1540 
1541         case SPINEL_PROP_DATASET_SECURITY_POLICY:
1542 
1543             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1544             {
1545                 uint8_t flags[2];
1546                 uint8_t flagsLength = 1;
1547 
1548                 SuccessOrExit(error = mDecoder.ReadUint16(aDataset.mSecurityPolicy.mRotationTime));
1549                 SuccessOrExit(error = mDecoder.ReadUint8(flags[0]));
1550                 if (otThreadGetVersion() >= OT_THREAD_VERSION_1_2 && mDecoder.GetRemainingLengthInStruct() > 0)
1551                 {
1552                     SuccessOrExit(error = mDecoder.ReadUint8(flags[1]));
1553                     ++flagsLength;
1554                 }
1555                 static_cast<SecurityPolicy &>(aDataset.mSecurityPolicy).SetFlags(flags, flagsLength);
1556             }
1557 
1558             aDataset.mComponents.mIsSecurityPolicyPresent = true;
1559             break;
1560 
1561         case SPINEL_PROP_PHY_CHAN_SUPPORTED:
1562 
1563             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1564             {
1565                 uint8_t channel;
1566 
1567                 aDataset.mChannelMask = 0;
1568 
1569                 while (!mDecoder.IsAllReadInStruct())
1570                 {
1571                     SuccessOrExit(error = mDecoder.ReadUint8(channel));
1572                     VerifyOrExit(channel <= 31, error = OT_ERROR_INVALID_ARGS);
1573                     aDataset.mChannelMask |= (1UL << channel);
1574                 }
1575             }
1576 
1577             aDataset.mComponents.mIsChannelMaskPresent = true;
1578             break;
1579 
1580         case SPINEL_PROP_DATASET_RAW_TLVS:
1581 
1582             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1583             {
1584                 const uint8_t *tlvs;
1585                 uint16_t       len;
1586 
1587                 SuccessOrExit(error = mDecoder.ReadData(tlvs, len));
1588                 VerifyOrExit(len <= 255, error = OT_ERROR_INVALID_ARGS);
1589 
1590                 if (aTlvs != nullptr)
1591                 {
1592                     *aTlvs = tlvs;
1593                 }
1594 
1595                 if (aTlvsLength != nullptr)
1596                 {
1597                     *aTlvsLength = static_cast<uint8_t>(len);
1598                 }
1599             }
1600 
1601             break;
1602 
1603         case SPINEL_PROP_DATASET_DEST_ADDRESS:
1604 
1605             if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
1606             {
1607                 const otIp6Address *addr;
1608 
1609                 SuccessOrExit(error = mDecoder.ReadIp6Address(addr));
1610 
1611                 if (aDestIpAddress != nullptr)
1612                 {
1613                     *aDestIpAddress = addr;
1614                 }
1615             }
1616 
1617             break;
1618 
1619         default:
1620             break;
1621         }
1622 
1623         SuccessOrExit(error = mDecoder.CloseStruct());
1624     }
1625 
1626 exit:
1627     return error;
1628 }
1629 
HandlePropertySet(void)1630 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_ACTIVE_DATASET>(void)
1631 {
1632     otError              error = OT_ERROR_NONE;
1633     otOperationalDataset dataset;
1634 
1635     SuccessOrExit(error = DecodeOperationalDataset(dataset));
1636     error = otDatasetSetActive(mInstance, &dataset);
1637 
1638 exit:
1639     return error;
1640 }
1641 
HandlePropertySet(void)1642 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_PENDING_DATASET>(void)
1643 {
1644     otError              error = OT_ERROR_NONE;
1645     otOperationalDataset dataset;
1646 
1647     SuccessOrExit(error = DecodeOperationalDataset(dataset));
1648     error = otDatasetSetPending(mInstance, &dataset);
1649 
1650 exit:
1651     return error;
1652 }
1653 
HandlePropertySet(void)1654 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_MGMT_SET_ACTIVE_DATASET>(void)
1655 {
1656     otError              error = OT_ERROR_NONE;
1657     otOperationalDataset dataset;
1658     const uint8_t       *extraTlvs;
1659     uint8_t              extraTlvsLength;
1660 
1661     SuccessOrExit(error = DecodeOperationalDataset(dataset, &extraTlvs, &extraTlvsLength));
1662     error = otDatasetSendMgmtActiveSet(mInstance, &dataset, extraTlvs, extraTlvsLength, /* aCallback */ nullptr,
1663                                        /* aContext */ nullptr);
1664 
1665 exit:
1666     return error;
1667 }
1668 
HandlePropertySet(void)1669 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET>(void)
1670 {
1671     otError              error = OT_ERROR_NONE;
1672     otOperationalDataset dataset;
1673     const uint8_t       *extraTlvs;
1674     uint8_t              extraTlvsLength;
1675 
1676     SuccessOrExit(error = DecodeOperationalDataset(dataset, &extraTlvs, &extraTlvsLength));
1677     error = otDatasetSendMgmtPendingSet(mInstance, &dataset, extraTlvs, extraTlvsLength, /* aCallback */ nullptr,
1678                                         /* aContext */ nullptr);
1679 
1680 exit:
1681     return error;
1682 }
1683 
HandlePropertySet(void)1684 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_MGMT_GET_ACTIVE_DATASET>(void)
1685 {
1686     otError              error = OT_ERROR_NONE;
1687     otOperationalDataset dataset;
1688     const uint8_t       *extraTlvs;
1689     uint8_t              extraTlvsLength;
1690     const otIp6Address  *destIpAddress;
1691 
1692     SuccessOrExit(error = DecodeOperationalDataset(dataset, &extraTlvs, &extraTlvsLength, &destIpAddress, true));
1693     error = otDatasetSendMgmtActiveGet(mInstance, &dataset.mComponents, extraTlvs, extraTlvsLength, destIpAddress);
1694 
1695 exit:
1696     return error;
1697 }
1698 
HandlePropertySet(void)1699 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_MGMT_GET_PENDING_DATASET>(void)
1700 {
1701     otError              error = OT_ERROR_NONE;
1702     otOperationalDataset dataset;
1703     const uint8_t       *extraTlvs;
1704     uint8_t              extraTlvsLength;
1705     const otIp6Address  *destIpAddress;
1706 
1707     SuccessOrExit(error = DecodeOperationalDataset(dataset, &extraTlvs, &extraTlvsLength, &destIpAddress, true));
1708     error = otDatasetSendMgmtPendingGet(mInstance, &dataset.mComponents, extraTlvs, extraTlvsLength, destIpAddress);
1709 
1710 exit:
1711     return error;
1712 }
1713 #if OPENTHREAD_CONFIG_JOINER_ENABLE
HandlePropertyGet(void)1714 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MESHCOP_JOINER_STATE>(void)
1715 {
1716     spinel_meshcop_joiner_state_t state = SPINEL_MESHCOP_JOINER_STATE_IDLE;
1717 
1718     switch (otJoinerGetState(mInstance))
1719     {
1720     case OT_JOINER_STATE_IDLE:
1721         state = SPINEL_MESHCOP_JOINER_STATE_IDLE;
1722         break;
1723     case OT_JOINER_STATE_DISCOVER:
1724         state = SPINEL_MESHCOP_JOINER_STATE_DISCOVER;
1725         break;
1726     case OT_JOINER_STATE_CONNECT:
1727         state = SPINEL_MESHCOP_JOINER_STATE_CONNECTING;
1728         break;
1729     case OT_JOINER_STATE_CONNECTED:
1730         state = SPINEL_MESHCOP_JOINER_STATE_CONNECTED;
1731         break;
1732     case OT_JOINER_STATE_ENTRUST:
1733         state = SPINEL_MESHCOP_JOINER_STATE_ENTRUST;
1734         break;
1735     case OT_JOINER_STATE_JOINED:
1736         state = SPINEL_MESHCOP_JOINER_STATE_JOINED;
1737         break;
1738     }
1739 
1740     return mEncoder.WriteUint8(state);
1741 }
1742 
HandlePropertySet(void)1743 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MESHCOP_JOINER_COMMISSIONING>(void)
1744 {
1745     otError     error           = OT_ERROR_NONE;
1746     bool        action          = false;
1747     const char *psk             = nullptr;
1748     const char *provisioningUrl = nullptr;
1749     const char *vendorName      = nullptr;
1750     const char *vendorModel     = nullptr;
1751     const char *vendorSwVersion = nullptr;
1752     const char *vendorData      = nullptr;
1753 
1754     SuccessOrExit(error = mDecoder.ReadBool(action));
1755 
1756     if (!action)
1757     {
1758         otJoinerStop(mInstance);
1759         ExitNow();
1760     }
1761 
1762     SuccessOrExit(error = mDecoder.ReadUtf8(psk));
1763 
1764     // Parse optional fields
1765 
1766     if (!mDecoder.IsAllReadInStruct())
1767     {
1768         SuccessOrExit(error = mDecoder.ReadUtf8(provisioningUrl));
1769     }
1770 
1771     if (!mDecoder.IsAllReadInStruct())
1772     {
1773         SuccessOrExit(error = mDecoder.ReadUtf8(vendorName));
1774     }
1775 
1776     if (!mDecoder.IsAllReadInStruct())
1777     {
1778         SuccessOrExit(error = mDecoder.ReadUtf8(vendorModel));
1779     }
1780 
1781     if (!mDecoder.IsAllReadInStruct())
1782     {
1783         SuccessOrExit(error = mDecoder.ReadUtf8(vendorSwVersion));
1784     }
1785 
1786     if (!mDecoder.IsAllReadInStruct())
1787     {
1788         SuccessOrExit(error = mDecoder.ReadUtf8(vendorData));
1789     }
1790 
1791     // Use OpenThread default values for vendor name, mode, sw version if
1792     // not specified or an empty string is given.
1793 
1794     if ((vendorName == nullptr) || (vendorName[0] == 0))
1795     {
1796         vendorName = PACKAGE_NAME;
1797     }
1798 
1799     if ((vendorModel == nullptr) || (vendorModel[0] == 0))
1800     {
1801         vendorModel = OPENTHREAD_CONFIG_PLATFORM_INFO;
1802     }
1803 
1804     if ((vendorSwVersion == nullptr) || (vendorSwVersion[0] == 0))
1805     {
1806         vendorSwVersion = PACKAGE_VERSION;
1807     }
1808 
1809     error = otJoinerStart(mInstance, psk, provisioningUrl, vendorName, vendorModel, vendorSwVersion, vendorData,
1810                           &NcpBase::HandleJoinerCallback_Jump, this);
1811 
1812 exit:
1813     return error;
1814 }
1815 
HandlePropertyGet(void)1816 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MESHCOP_JOINER_DISCERNER>(void)
1817 {
1818     otError                  error;
1819     const otJoinerDiscerner *discerner = otJoinerGetDiscerner(mInstance);
1820 
1821     if (discerner == nullptr)
1822     {
1823         SuccessOrExit(error = mEncoder.WriteUint8(0));
1824     }
1825     else
1826     {
1827         SuccessOrExit(error = mEncoder.WriteUint8(discerner->mLength));
1828         SuccessOrExit(error = mEncoder.WriteUint64(discerner->mValue));
1829     }
1830 
1831 exit:
1832     return error;
1833 }
1834 
HandlePropertySet(void)1835 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MESHCOP_JOINER_DISCERNER>(void)
1836 {
1837     otError           error = OT_ERROR_NONE;
1838     otJoinerDiscerner discerner;
1839 
1840     SuccessOrExit(error = mDecoder.ReadUint8(discerner.mLength));
1841 
1842     if (discerner.mLength == 0)
1843     {
1844         // Clearing any previously set Joiner Discerner
1845         error = otJoinerSetDiscerner(mInstance, nullptr);
1846         ExitNow();
1847     }
1848 
1849     SuccessOrExit(error = mDecoder.ReadUint64(discerner.mValue));
1850     error = otJoinerSetDiscerner(mInstance, &discerner);
1851 
1852 exit:
1853     return error;
1854 }
1855 
1856 #endif // OPENTHREAD_CONFIG_JOINER_ENABLE
1857 
HandlePropertyGet(void)1858 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_IPV6_ML_PREFIX>(void)
1859 {
1860     otError                  error    = OT_ERROR_NONE;
1861     const otMeshLocalPrefix *mlPrefix = otThreadGetMeshLocalPrefix(mInstance);
1862     otIp6Address             addr;
1863 
1864     VerifyOrExit(mlPrefix != nullptr); // If `mlPrefix` is nullptr send empty response.
1865 
1866     memcpy(addr.mFields.m8, mlPrefix->m8, 8);
1867 
1868     // Zero out the last 8 bytes.
1869     memset(addr.mFields.m8 + 8, 0, 8);
1870 
1871     SuccessOrExit(error = mEncoder.WriteIp6Address(addr));             // Mesh local prefix
1872     SuccessOrExit(error = mEncoder.WriteUint8(OT_IP6_PREFIX_BITSIZE)); // Prefix length (in bits)
1873 
1874 exit:
1875     return error;
1876 }
1877 
HandlePropertySet(void)1878 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_IPV6_ML_PREFIX>(void)
1879 {
1880     otError             error = OT_ERROR_NONE;
1881     const otIp6Address *meshLocalPrefix;
1882     uint8_t             prefixLength;
1883 
1884     SuccessOrExit(error = mDecoder.ReadIp6Address(meshLocalPrefix));
1885     SuccessOrExit(error = mDecoder.ReadUint8(prefixLength));
1886     VerifyOrExit(prefixLength == OT_IP6_PREFIX_BITSIZE, error = OT_ERROR_INVALID_ARGS);
1887 
1888     error = otThreadSetMeshLocalPrefix(mInstance, reinterpret_cast<const otMeshLocalPrefix *>(meshLocalPrefix));
1889 
1890 exit:
1891     return error;
1892 }
1893 
HandlePropertyGet(void)1894 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_IPV6_ML_ADDR>(void)
1895 {
1896     otError             error = OT_ERROR_NONE;
1897     const otIp6Address *ml64  = otThreadGetMeshLocalEid(mInstance);
1898 
1899     VerifyOrExit(ml64 != nullptr);
1900     SuccessOrExit(error = mEncoder.WriteIp6Address(*ml64));
1901 
1902 exit:
1903     return error;
1904 }
1905 
HandlePropertyGet(void)1906 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_IPV6_LL_ADDR>(void)
1907 {
1908     otError             error   = OT_ERROR_NONE;
1909     const otIp6Address *address = otThreadGetLinkLocalIp6Address(mInstance);
1910 
1911     VerifyOrExit(address != nullptr);
1912     SuccessOrExit(error = mEncoder.WriteIp6Address(*address));
1913 
1914 exit:
1915     return error;
1916 }
1917 
HandlePropertyGet(void)1918 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_IPV6_ADDRESS_TABLE>(void)
1919 {
1920     otError error = OT_ERROR_NONE;
1921 
1922     for (const otNetifAddress *address = otIp6GetUnicastAddresses(mInstance); address; address = address->mNext)
1923     {
1924         SuccessOrExit(error = mEncoder.OpenStruct());
1925 
1926         SuccessOrExit(error = mEncoder.WriteIp6Address(address->mAddress));
1927         SuccessOrExit(error = mEncoder.WriteUint8(address->mPrefixLength));
1928         SuccessOrExit(error = mEncoder.WriteUint32(address->mPreferred ? 0xffffffff : 0));
1929         SuccessOrExit(error = mEncoder.WriteUint32(address->mValid ? 0xffffffff : 0));
1930 
1931         SuccessOrExit(error = mEncoder.CloseStruct());
1932     }
1933 
1934 exit:
1935     return error;
1936 }
1937 
HandlePropertyInsert(void)1938 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_IPV6_ADDRESS_TABLE>(void)
1939 {
1940     otError        error = OT_ERROR_NONE;
1941     otNetifAddress netifAddr;
1942     uint32_t       preferredLifetime;
1943     uint32_t       validLifetime;
1944 
1945     SuccessOrExit(error = mDecoder.ReadIp6Address(netifAddr.mAddress));
1946     SuccessOrExit(error = mDecoder.ReadUint8(netifAddr.mPrefixLength));
1947     SuccessOrExit(error = mDecoder.ReadUint32(preferredLifetime));
1948     SuccessOrExit(error = mDecoder.ReadUint32(validLifetime));
1949 
1950     netifAddr.mAddressOrigin = OT_ADDRESS_ORIGIN_MANUAL;
1951     netifAddr.mPreferred     = (preferredLifetime != 0);
1952     netifAddr.mValid         = (validLifetime != 0);
1953 
1954     error = otIp6AddUnicastAddress(mInstance, &netifAddr);
1955 
1956 exit:
1957     return error;
1958 }
1959 
HandlePropertyRemove(void)1960 template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_IPV6_ADDRESS_TABLE>(void)
1961 {
1962     otError             error = OT_ERROR_NONE;
1963     const otIp6Address *addrPtr;
1964 
1965     SuccessOrExit(error = mDecoder.ReadIp6Address(addrPtr));
1966 
1967     error = otIp6RemoveUnicastAddress(mInstance, addrPtr);
1968 
1969     // If address was not on the list, "remove" command is successful.
1970     if (error == OT_ERROR_NOT_FOUND)
1971     {
1972         error = OT_ERROR_NONE;
1973     }
1974 
1975 exit:
1976     return error;
1977 }
1978 
HandlePropertyGet(void)1979 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_IPV6_ROUTE_TABLE>(void)
1980 {
1981     // TODO: Implement get route table
1982     return mEncoder.OverwriteWithLastStatusError(SPINEL_STATUS_UNIMPLEMENTED);
1983 }
1984 
HandlePropertyGet(void)1985 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD>(void)
1986 {
1987     return mEncoder.WriteBool(otIcmp6GetEchoMode(mInstance) != OT_ICMP6_ECHO_HANDLER_DISABLED);
1988 }
1989 
HandlePropertySet(void)1990 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD>(void)
1991 {
1992     bool    enabled = false;
1993     otError error   = OT_ERROR_NONE;
1994 
1995     SuccessOrExit(error = mDecoder.ReadBool(enabled));
1996 
1997     otIcmp6SetEchoMode(mInstance, enabled ? OT_ICMP6_ECHO_HANDLER_ALL : OT_ICMP6_ECHO_HANDLER_DISABLED);
1998 
1999 exit:
2000     return error;
2001 }
2002 
HandlePropertyGet(void)2003 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE>(void)
2004 {
2005     otError                        error = OT_ERROR_NONE;
2006     const otNetifMulticastAddress *address;
2007 
2008     for (address = otIp6GetMulticastAddresses(mInstance); address; address = address->mNext)
2009     {
2010         SuccessOrExit(error = mEncoder.OpenStruct());
2011         SuccessOrExit(error = mEncoder.WriteIp6Address(address->mAddress));
2012         SuccessOrExit(error = mEncoder.CloseStruct());
2013     }
2014 
2015 exit:
2016     return error;
2017 }
2018 
HandlePropertyInsert(void)2019 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE>(void)
2020 {
2021     otError             error = OT_ERROR_NONE;
2022     const otIp6Address *addrPtr;
2023 
2024     SuccessOrExit(error = mDecoder.ReadIp6Address(addrPtr));
2025 
2026     error = otIp6SubscribeMulticastAddress(mInstance, addrPtr);
2027 
2028     if (error == OT_ERROR_ALREADY)
2029     {
2030         error = OT_ERROR_NONE;
2031     }
2032 
2033 exit:
2034     return error;
2035 }
2036 
HandlePropertyRemove(void)2037 template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE>(void)
2038 {
2039     otError             error = OT_ERROR_NONE;
2040     const otIp6Address *addrPtr;
2041 
2042     SuccessOrExit(error = mDecoder.ReadIp6Address(addrPtr));
2043 
2044     error = otIp6UnsubscribeMulticastAddress(mInstance, addrPtr);
2045 
2046     // If the address was not on the list, "remove" command is successful,
2047     // and we respond with a `SPINEL_STATUS_OK` status.
2048     if (error == OT_ERROR_NOT_FOUND)
2049     {
2050         error = OT_ERROR_NONE;
2051     }
2052 
2053 exit:
2054     return error;
2055 }
2056 
HandlePropertyGet(void)2057 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD_MODE>(void)
2058 {
2059     spinel_ipv6_icmp_ping_offload_mode_t mode = SPINEL_IPV6_ICMP_PING_OFFLOAD_DISABLED;
2060 
2061     switch (otIcmp6GetEchoMode(mInstance))
2062     {
2063     case OT_ICMP6_ECHO_HANDLER_DISABLED:
2064         mode = SPINEL_IPV6_ICMP_PING_OFFLOAD_DISABLED;
2065         break;
2066     case OT_ICMP6_ECHO_HANDLER_UNICAST_ONLY:
2067         mode = SPINEL_IPV6_ICMP_PING_OFFLOAD_UNICAST_ONLY;
2068         break;
2069     case OT_ICMP6_ECHO_HANDLER_MULTICAST_ONLY:
2070         mode = SPINEL_IPV6_ICMP_PING_OFFLOAD_MULTICAST_ONLY;
2071         break;
2072     case OT_ICMP6_ECHO_HANDLER_ALL:
2073         mode = SPINEL_IPV6_ICMP_PING_OFFLOAD_ALL;
2074         break;
2075     };
2076 
2077     return mEncoder.WriteUint8(mode);
2078 }
2079 
HandlePropertySet(void)2080 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD_MODE>(void)
2081 {
2082     otError         error = OT_ERROR_NONE;
2083     otIcmp6EchoMode mode  = OT_ICMP6_ECHO_HANDLER_DISABLED;
2084     uint8_t         spinelMode;
2085 
2086     SuccessOrExit(error = mDecoder.ReadUint8(spinelMode));
2087 
2088     switch (spinelMode)
2089     {
2090     case SPINEL_IPV6_ICMP_PING_OFFLOAD_DISABLED:
2091         mode = OT_ICMP6_ECHO_HANDLER_DISABLED;
2092         break;
2093     case SPINEL_IPV6_ICMP_PING_OFFLOAD_UNICAST_ONLY:
2094         mode = OT_ICMP6_ECHO_HANDLER_UNICAST_ONLY;
2095         break;
2096     case SPINEL_IPV6_ICMP_PING_OFFLOAD_MULTICAST_ONLY:
2097         mode = OT_ICMP6_ECHO_HANDLER_MULTICAST_ONLY;
2098         break;
2099     case SPINEL_IPV6_ICMP_PING_OFFLOAD_ALL:
2100         mode = OT_ICMP6_ECHO_HANDLER_ALL;
2101         break;
2102     };
2103 
2104     otIcmp6SetEchoMode(mInstance, mode);
2105 
2106 exit:
2107     return error;
2108 }
2109 
HandlePropertyGet(void)2110 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU>(void)
2111 {
2112     // Note reverse logic: passthru enabled = filter disabled
2113     return mEncoder.WriteBool(!otIp6IsReceiveFilterEnabled(mInstance));
2114 }
2115 
HandlePropertySet(void)2116 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU>(void)
2117 {
2118     bool    enabled = false;
2119     otError error   = OT_ERROR_NONE;
2120 
2121     SuccessOrExit(error = mDecoder.ReadBool(enabled));
2122 
2123     // Note reverse logic: passthru enabled = filter disabled
2124     otIp6SetReceiveFilterEnabled(mInstance, !enabled);
2125 
2126 exit:
2127     return error;
2128 }
2129 
HandlePropertyGet(void)2130 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_OFF_MESH_ROUTES>(void)
2131 {
2132     otError               error = OT_ERROR_NONE;
2133     otExternalRouteConfig routeConfig;
2134     otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT;
2135 
2136     while (otNetDataGetNextRoute(mInstance, &iter, &routeConfig) == OT_ERROR_NONE)
2137     {
2138         SuccessOrExit(error = mEncoder.OpenStruct());
2139 
2140         SuccessOrExit(error = mEncoder.WriteIp6Address(routeConfig.mPrefix.mPrefix));
2141         SuccessOrExit(error = mEncoder.WriteUint8(routeConfig.mPrefix.mLength));
2142         SuccessOrExit(error = mEncoder.WriteBool(routeConfig.mStable));
2143         SuccessOrExit(error = mEncoder.WriteUint8(ExternalRouteConfigToFlagByte(routeConfig)));
2144         SuccessOrExit(error = mEncoder.WriteBool(false)); // IsLocal
2145         SuccessOrExit(error = mEncoder.WriteBool(routeConfig.mNextHopIsThisDevice));
2146         SuccessOrExit(error = mEncoder.WriteUint16(routeConfig.mRloc16));
2147 
2148         SuccessOrExit(error = mEncoder.CloseStruct());
2149     }
2150 
2151 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
2152 
2153     iter = OT_NETWORK_DATA_ITERATOR_INIT;
2154 
2155     while (otBorderRouterGetNextRoute(mInstance, &iter, &routeConfig) == OT_ERROR_NONE)
2156     {
2157         SuccessOrExit(error = mEncoder.OpenStruct());
2158 
2159         SuccessOrExit(error = mEncoder.WriteIp6Address(routeConfig.mPrefix.mPrefix));
2160         SuccessOrExit(error = mEncoder.WriteUint8(routeConfig.mPrefix.mLength));
2161         SuccessOrExit(error = mEncoder.WriteBool(routeConfig.mStable));
2162         SuccessOrExit(error = mEncoder.WriteUint8(ExternalRouteConfigToFlagByte(routeConfig)));
2163         SuccessOrExit(error = mEncoder.WriteBool(true)); // IsLocal
2164         SuccessOrExit(error = mEncoder.WriteBool(routeConfig.mNextHopIsThisDevice));
2165         SuccessOrExit(error = mEncoder.WriteUint16(routeConfig.mRloc16));
2166 
2167         SuccessOrExit(error = mEncoder.CloseStruct());
2168     }
2169 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
2170 
2171 exit:
2172     return error;
2173 }
2174 
2175 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
FlagByteToExternalRoutePreference(uint8_t aFlags)2176 static int FlagByteToExternalRoutePreference(uint8_t aFlags)
2177 {
2178     int route_preference = 0;
2179 
2180     switch (aFlags & SPINEL_NET_FLAG_PREFERENCE_MASK)
2181     {
2182     case SPINEL_ROUTE_PREFERENCE_HIGH:
2183         route_preference = OT_ROUTE_PREFERENCE_HIGH;
2184         break;
2185 
2186     case SPINEL_ROUTE_PREFERENCE_MEDIUM:
2187         route_preference = OT_ROUTE_PREFERENCE_MED;
2188         break;
2189 
2190     case SPINEL_ROUTE_PREFERENCE_LOW:
2191         route_preference = OT_ROUTE_PREFERENCE_LOW;
2192         break;
2193     }
2194 
2195     return route_preference;
2196 }
2197 
HandlePropertyInsert(void)2198 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_THREAD_OFF_MESH_ROUTES>(void)
2199 {
2200     otError               error = OT_ERROR_NONE;
2201     otExternalRouteConfig routeConfig;
2202     bool                  stable = false;
2203     uint8_t               flags  = 0;
2204     uint8_t               prefixLength;
2205 
2206     memset(&routeConfig, 0, sizeof(otExternalRouteConfig));
2207 
2208     VerifyOrExit(mAllowLocalNetworkDataChange, error = OT_ERROR_INVALID_STATE);
2209 
2210     SuccessOrExit(error = mDecoder.ReadIp6Address(routeConfig.mPrefix.mPrefix));
2211     SuccessOrExit(error = mDecoder.ReadUint8(prefixLength));
2212     SuccessOrExit(error = mDecoder.ReadBool(stable));
2213     SuccessOrExit(error = mDecoder.ReadUint8(flags));
2214 
2215     routeConfig.mPrefix.mLength = prefixLength;
2216     routeConfig.mStable         = stable;
2217     routeConfig.mPreference     = FlagByteToExternalRoutePreference(flags);
2218     routeConfig.mNat64          = ((flags & SPINEL_ROUTE_FLAG_NAT64) != 0);
2219 
2220     error = otBorderRouterAddRoute(mInstance, &routeConfig);
2221 
2222 exit:
2223     return error;
2224 }
2225 
HandlePropertyRemove(void)2226 template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_THREAD_OFF_MESH_ROUTES>(void)
2227 {
2228     otError     error = OT_ERROR_NONE;
2229     otIp6Prefix ip6Prefix;
2230     uint8_t     prefixLength;
2231 
2232     memset(&ip6Prefix, 0, sizeof(otIp6Prefix));
2233 
2234     VerifyOrExit(mAllowLocalNetworkDataChange, error = OT_ERROR_INVALID_STATE);
2235 
2236     SuccessOrExit(error = mDecoder.ReadIp6Address(ip6Prefix.mPrefix));
2237     SuccessOrExit(error = mDecoder.ReadUint8(prefixLength));
2238 
2239     ip6Prefix.mLength = prefixLength;
2240 
2241     error = otBorderRouterRemoveRoute(mInstance, &ip6Prefix);
2242 
2243     // If the route prefix was not on the list, "remove" command is successful.
2244     if (error == OT_ERROR_NOT_FOUND)
2245     {
2246         error = OT_ERROR_NONE;
2247     }
2248 
2249 exit:
2250     return error;
2251 }
2252 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
2253 
HandlePropertySet(void)2254 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_STREAM_NET>(void)
2255 {
2256     const uint8_t *framePtr = nullptr;
2257     uint16_t       frameLen = 0;
2258     const uint8_t *metaPtr  = nullptr;
2259     uint16_t       metaLen  = 0;
2260     otMessage     *message  = nullptr;
2261     otError        error    = OT_ERROR_NONE;
2262 
2263     SuccessOrExit(error = mDecoder.ReadDataWithLen(framePtr, frameLen));
2264     SuccessOrExit(error = mDecoder.ReadData(metaPtr, metaLen));
2265 
2266     // We ignore metadata for now.
2267     // May later include TX power, allow retransmits, etc...
2268 
2269     // STREAM_NET requires layer 2 security.
2270     message = otIp6NewMessageFromBuffer(mInstance, framePtr, frameLen, nullptr);
2271     VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
2272 
2273     error = otIp6Send(mInstance, message);
2274 
2275 exit:
2276 
2277     if (error == OT_ERROR_NONE)
2278     {
2279         mInboundSecureIpFrameCounter++;
2280     }
2281     else
2282     {
2283         mDroppedInboundIpFrameCounter++;
2284     }
2285 
2286     return error;
2287 }
2288 
2289 #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE
2290 
HandlePropertyGet(void)2291 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_JAM_DETECT_ENABLE>(void)
2292 {
2293     return mEncoder.WriteBool(otJamDetectionIsEnabled(mInstance));
2294 }
2295 
HandlePropertyGet(void)2296 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_JAM_DETECTED>(void)
2297 {
2298     return mEncoder.WriteBool(otJamDetectionGetState(mInstance));
2299 }
2300 
HandlePropertyGet(void)2301 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD>(void)
2302 {
2303     return mEncoder.WriteInt8(otJamDetectionGetRssiThreshold(mInstance));
2304 }
2305 
HandlePropertyGet(void)2306 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_JAM_DETECT_WINDOW>(void)
2307 {
2308     return mEncoder.WriteUint8(otJamDetectionGetWindow(mInstance));
2309 }
2310 
HandlePropertyGet(void)2311 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_JAM_DETECT_BUSY>(void)
2312 {
2313     return mEncoder.WriteUint8(otJamDetectionGetBusyPeriod(mInstance));
2314 }
2315 
HandlePropertyGet(void)2316 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_JAM_DETECT_HISTORY_BITMAP>(void)
2317 {
2318     return mEncoder.WriteUint64(otJamDetectionGetHistoryBitmap(mInstance));
2319 }
2320 
HandlePropertySet(void)2321 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_JAM_DETECT_ENABLE>(void)
2322 {
2323     bool    enabled;
2324     otError error = OT_ERROR_NONE;
2325 
2326     SuccessOrExit(error = mDecoder.ReadBool(enabled));
2327 
2328     if (enabled)
2329     {
2330         IgnoreError(otJamDetectionStart(mInstance, &NcpBase::HandleJamStateChange_Jump, this));
2331     }
2332     else
2333     {
2334         IgnoreError(otJamDetectionStop(mInstance));
2335     }
2336 
2337 exit:
2338     return error;
2339 }
2340 
HandlePropertySet(void)2341 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD>(void)
2342 {
2343     int8_t  threshold = 0;
2344     otError error     = OT_ERROR_NONE;
2345 
2346     SuccessOrExit(error = mDecoder.ReadInt8(threshold));
2347 
2348     error = otJamDetectionSetRssiThreshold(mInstance, threshold);
2349 
2350 exit:
2351     return error;
2352 }
2353 
HandlePropertySet(void)2354 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_JAM_DETECT_WINDOW>(void)
2355 {
2356     uint8_t window = 0;
2357     otError error  = OT_ERROR_NONE;
2358 
2359     SuccessOrExit(error = mDecoder.ReadUint8(window));
2360 
2361     error = otJamDetectionSetWindow(mInstance, window);
2362 
2363 exit:
2364     return error;
2365 }
2366 
HandlePropertySet(void)2367 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_JAM_DETECT_BUSY>(void)
2368 {
2369     uint8_t busy  = 0;
2370     otError error = OT_ERROR_NONE;
2371 
2372     SuccessOrExit(error = mDecoder.ReadUint8(busy));
2373 
2374     error = otJamDetectionSetBusyPeriod(mInstance, busy);
2375 
2376 exit:
2377     return error;
2378 }
2379 
HandleJamStateChange_Jump(bool aJamState,void * aContext)2380 void NcpBase::HandleJamStateChange_Jump(bool aJamState, void *aContext)
2381 {
2382     static_cast<NcpBase *>(aContext)->HandleJamStateChange(aJamState);
2383 }
2384 
HandleJamStateChange(bool aJamState)2385 void NcpBase::HandleJamStateChange(bool aJamState)
2386 {
2387     OT_UNUSED_VARIABLE(aJamState);
2388 
2389     mChangedPropsSet.AddProperty(SPINEL_PROP_JAM_DETECTED);
2390     mUpdateChangedPropsTask.Post();
2391 }
2392 
2393 #endif // OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE
2394 
HandlePropertyGet(void)2395 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CHILD_SUPERVISION_CHECK_TIMEOUT>(void)
2396 {
2397     return mEncoder.WriteUint16(otChildSupervisionGetCheckTimeout(mInstance));
2398 }
2399 
HandlePropertySet(void)2400 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_CHILD_SUPERVISION_CHECK_TIMEOUT>(void)
2401 {
2402     otError  error = OT_ERROR_NONE;
2403     uint16_t timeout;
2404 
2405     SuccessOrExit(error = mDecoder.ReadUint16(timeout));
2406     otChildSupervisionSetCheckTimeout(mInstance, timeout);
2407 
2408 exit:
2409     return error;
2410 }
2411 
2412 #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
2413 
HandlePropertyGet(void)2414 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CHANNEL_MONITOR_SAMPLE_INTERVAL>(void)
2415 {
2416     return mEncoder.WriteUint32(otChannelMonitorGetSampleInterval(mInstance));
2417 }
2418 
HandlePropertyGet(void)2419 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CHANNEL_MONITOR_RSSI_THRESHOLD>(void)
2420 {
2421     return mEncoder.WriteInt8(otChannelMonitorGetRssiThreshold(mInstance));
2422 }
2423 
HandlePropertyGet(void)2424 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CHANNEL_MONITOR_SAMPLE_WINDOW>(void)
2425 {
2426     return mEncoder.WriteUint32(otChannelMonitorGetSampleWindow(mInstance));
2427 }
2428 
HandlePropertyGet(void)2429 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CHANNEL_MONITOR_SAMPLE_COUNT>(void)
2430 {
2431     return mEncoder.WriteUint32(otChannelMonitorGetSampleCount(mInstance));
2432 }
2433 
HandlePropertyGet(void)2434 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CHANNEL_MONITOR_CHANNEL_OCCUPANCY>(void)
2435 {
2436     otError  error       = OT_ERROR_NONE;
2437     uint32_t channelMask = otLinkGetSupportedChannelMask(mInstance);
2438     uint8_t  channelNum  = sizeof(channelMask) * kBitsPerByte;
2439 
2440     for (uint8_t channel = 0; channel < channelNum; channel++)
2441     {
2442         if (!((1UL << channel) & channelMask))
2443         {
2444             continue;
2445         }
2446 
2447         SuccessOrExit(error = mEncoder.OpenStruct());
2448 
2449         SuccessOrExit(error = mEncoder.WriteUint8(channel));
2450         SuccessOrExit(error = mEncoder.WriteUint16(otChannelMonitorGetChannelOccupancy(mInstance, channel)));
2451 
2452         SuccessOrExit(error = mEncoder.CloseStruct());
2453     }
2454 
2455 exit:
2456     return error;
2457 }
2458 
2459 #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
2460 
HandlePropertyGet(void)2461 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_CCA_FAILURE_RATE>(void)
2462 {
2463     return mEncoder.WriteUint16(otLinkGetCcaFailureRate(mInstance));
2464 }
2465 
HandlePropertyGet(void)2466 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_TOTAL>(void)
2467 {
2468     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxTotal);
2469 }
2470 
HandlePropertyGet(void)2471 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_ACK_REQ>(void)
2472 {
2473     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxAckRequested);
2474 }
2475 
HandlePropertyGet(void)2476 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_ACKED>(void)
2477 {
2478     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxAcked);
2479 }
2480 
HandlePropertyGet(void)2481 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_NO_ACK_REQ>(void)
2482 {
2483     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxNoAckRequested);
2484 }
2485 
HandlePropertyGet(void)2486 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_DATA>(void)
2487 {
2488     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxData);
2489 }
2490 
HandlePropertyGet(void)2491 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_DATA_POLL>(void)
2492 {
2493     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxDataPoll);
2494 }
2495 
HandlePropertyGet(void)2496 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_BEACON>(void)
2497 {
2498     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxBeacon);
2499 }
2500 
HandlePropertyGet(void)2501 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_BEACON_REQ>(void)
2502 {
2503     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxBeaconRequest);
2504 }
2505 
HandlePropertyGet(void)2506 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_OTHER>(void)
2507 {
2508     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxOther);
2509 }
2510 
HandlePropertyGet(void)2511 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_RETRY>(void)
2512 {
2513     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxRetry);
2514 }
2515 
HandlePropertyGet(void)2516 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_ERR_CCA>(void)
2517 {
2518     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxErrCca);
2519 }
2520 
HandlePropertyGet(void)2521 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_UNICAST>(void)
2522 {
2523     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxUnicast);
2524 }
2525 
HandlePropertyGet(void)2526 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_BROADCAST>(void)
2527 {
2528     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxBroadcast);
2529 }
2530 
HandlePropertyGet(void)2531 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_ERR_ABORT>(void)
2532 {
2533     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxErrAbort);
2534 }
2535 
HandlePropertyGet(void)2536 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_TOTAL>(void)
2537 {
2538     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxTotal);
2539 }
2540 
HandlePropertyGet(void)2541 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_DATA>(void)
2542 {
2543     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxData);
2544 }
2545 
HandlePropertyGet(void)2546 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_DATA_POLL>(void)
2547 {
2548     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxDataPoll);
2549 }
2550 
HandlePropertyGet(void)2551 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_BEACON>(void)
2552 {
2553     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxBeacon);
2554 }
2555 
HandlePropertyGet(void)2556 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_BEACON_REQ>(void)
2557 {
2558     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxBeaconRequest);
2559 }
2560 
HandlePropertyGet(void)2561 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_OTHER>(void)
2562 {
2563     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxOther);
2564 }
2565 
HandlePropertyGet(void)2566 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_FILT_WL>(void)
2567 {
2568     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxAddressFiltered);
2569 }
2570 
HandlePropertyGet(void)2571 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_FILT_DA>(void)
2572 {
2573     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxDestAddrFiltered);
2574 }
2575 
HandlePropertyGet(void)2576 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_DUP>(void)
2577 {
2578     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxDuplicated);
2579 }
2580 
HandlePropertyGet(void)2581 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_UNICAST>(void)
2582 {
2583     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxUnicast);
2584 }
2585 
HandlePropertyGet(void)2586 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_BROADCAST>(void)
2587 {
2588     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxBroadcast);
2589 }
2590 
HandlePropertyGet(void)2591 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_ERR_EMPTY>(void)
2592 {
2593     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrNoFrame);
2594 }
2595 
HandlePropertyGet(void)2596 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_ERR_UKWN_NBR>(void)
2597 {
2598     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrUnknownNeighbor);
2599 }
2600 
HandlePropertyGet(void)2601 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_ERR_NVLD_SADDR>(void)
2602 {
2603     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrInvalidSrcAddr);
2604 }
2605 
HandlePropertyGet(void)2606 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_ERR_SECURITY>(void)
2607 {
2608     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrSec);
2609 }
2610 
HandlePropertyGet(void)2611 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_ERR_BAD_FCS>(void)
2612 {
2613     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrFcs);
2614 }
2615 
HandlePropertyGet(void)2616 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_ERR_OTHER>(void)
2617 {
2618     return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrOther);
2619 }
2620 
HandlePropertyGet(void)2621 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_IP_SEC_TOTAL>(void)
2622 {
2623     return mEncoder.WriteUint32(mInboundSecureIpFrameCounter);
2624 }
2625 
HandlePropertyGet(void)2626 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_IP_INSEC_TOTAL>(void)
2627 {
2628     return mEncoder.WriteUint32(mInboundInsecureIpFrameCounter);
2629 }
2630 
HandlePropertyGet(void)2631 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_IP_DROPPED>(void)
2632 {
2633     return mEncoder.WriteUint32(mDroppedInboundIpFrameCounter);
2634 }
2635 
HandlePropertyGet(void)2636 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_IP_SEC_TOTAL>(void)
2637 {
2638     return mEncoder.WriteUint32(mOutboundSecureIpFrameCounter);
2639 }
2640 
HandlePropertyGet(void)2641 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_IP_INSEC_TOTAL>(void)
2642 {
2643     return mEncoder.WriteUint32(mOutboundInsecureIpFrameCounter);
2644 }
2645 
HandlePropertyGet(void)2646 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_IP_DROPPED>(void)
2647 {
2648     return mEncoder.WriteUint32(mDroppedOutboundIpFrameCounter);
2649 }
2650 
HandlePropertyGet(void)2651 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_SPINEL_TOTAL>(void)
2652 {
2653     return mEncoder.WriteUint32(mTxSpinelFrameCounter);
2654 }
2655 
HandlePropertyGet(void)2656 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_SPINEL_TOTAL>(void)
2657 {
2658     return mEncoder.WriteUint32(mRxSpinelFrameCounter);
2659 }
2660 
HandlePropertyGet(void)2661 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_SPINEL_OUT_OF_ORDER_TID>(void)
2662 {
2663     return mEncoder.WriteUint32(mRxSpinelOutOfOrderTidCounter);
2664 }
2665 
HandlePropertyGet(void)2666 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_SPINEL_ERR>(void)
2667 {
2668     return mEncoder.WriteUint32(mFramingErrorCounter);
2669 }
2670 
HandlePropertyGet(void)2671 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_IP_TX_SUCCESS>(void)
2672 {
2673     return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mTxSuccess);
2674 }
2675 
HandlePropertyGet(void)2676 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_IP_RX_SUCCESS>(void)
2677 {
2678     return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mRxSuccess);
2679 }
2680 
HandlePropertyGet(void)2681 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_IP_TX_FAILURE>(void)
2682 {
2683     return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mTxFailure);
2684 }
2685 
HandlePropertyGet(void)2686 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_IP_RX_FAILURE>(void)
2687 {
2688     return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mRxFailure);
2689 }
2690 
HandlePropertyGet(void)2691 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MSG_BUFFER_COUNTERS>(void)
2692 {
2693     otError      error = OT_ERROR_NONE;
2694     otBufferInfo bufferInfo;
2695 
2696     otMessageGetBufferInfo(mInstance, &bufferInfo);
2697 
2698     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mTotalBuffers));
2699     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mFreeBuffers));
2700 
2701     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loSendQueue.mNumMessages));
2702     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loSendQueue.mNumBuffers));
2703     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loReassemblyQueue.mNumMessages));
2704     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loReassemblyQueue.mNumBuffers));
2705     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mIp6Queue.mNumMessages));
2706     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mIp6Queue.mNumBuffers));
2707     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMplQueue.mNumMessages));
2708     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMplQueue.mNumBuffers));
2709     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMleQueue.mNumMessages));
2710     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMleQueue.mNumBuffers));
2711     SuccessOrExit(error = mEncoder.WriteUint16(0)); // Write zero for ARP for backward compatibility.
2712     SuccessOrExit(error = mEncoder.WriteUint16(0));
2713     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mCoapQueue.mNumMessages));
2714     SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mCoapQueue.mNumBuffers));
2715 
2716 exit:
2717     return error;
2718 }
2719 
HandlePropertyGet(void)2720 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_ALL_MAC_COUNTERS>(void)
2721 {
2722     otError              error    = OT_ERROR_NONE;
2723     const otMacCounters *counters = otLinkGetCounters(mInstance);
2724 
2725     // Encode Tx related counters
2726     SuccessOrExit(error = mEncoder.OpenStruct());
2727     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxTotal));
2728     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxUnicast));
2729     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxBroadcast));
2730     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxAckRequested));
2731     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxAcked));
2732     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxNoAckRequested));
2733     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxData));
2734     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxDataPoll));
2735     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxBeacon));
2736     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxBeaconRequest));
2737     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxOther));
2738     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxRetry));
2739     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxErrCca));
2740     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxErrAbort));
2741     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxErrBusyChannel));
2742     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxDirectMaxRetryExpiry));
2743     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxIndirectMaxRetryExpiry));
2744     SuccessOrExit(error = mEncoder.CloseStruct());
2745 
2746     // Encode Rx related counters
2747     SuccessOrExit(error = mEncoder.OpenStruct());
2748     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxTotal));
2749     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxUnicast));
2750     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxBroadcast));
2751     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxData));
2752     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxDataPoll));
2753     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxBeacon));
2754     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxBeaconRequest));
2755     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxOther));
2756     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxAddressFiltered));
2757     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxDestAddrFiltered));
2758     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxDuplicated));
2759     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxErrNoFrame));
2760     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxErrUnknownNeighbor));
2761     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxErrInvalidSrcAddr));
2762     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxErrSec));
2763     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxErrFcs));
2764     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxErrOther));
2765     SuccessOrExit(error = mEncoder.CloseStruct());
2766 
2767 exit:
2768     return error;
2769 }
2770 
HandlePropertySet(void)2771 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_CNTR_ALL_MAC_COUNTERS>(void)
2772 {
2773     otLinkResetCounters(mInstance);
2774 
2775     return OT_ERROR_NONE;
2776 }
2777 
HandlePropertyGet(void)2778 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_MLE_COUNTERS>(void)
2779 {
2780     otError              error    = OT_ERROR_NONE;
2781     const otMleCounters *counters = otThreadGetMleCounters(mInstance);
2782 
2783     OT_ASSERT(counters != nullptr);
2784 
2785     SuccessOrExit(error = mEncoder.WriteUint16(counters->mDisabledRole));
2786     SuccessOrExit(error = mEncoder.WriteUint16(counters->mDetachedRole));
2787     SuccessOrExit(error = mEncoder.WriteUint16(counters->mChildRole));
2788     SuccessOrExit(error = mEncoder.WriteUint16(counters->mRouterRole));
2789     SuccessOrExit(error = mEncoder.WriteUint16(counters->mLeaderRole));
2790     SuccessOrExit(error = mEncoder.WriteUint16(counters->mAttachAttempts));
2791     SuccessOrExit(error = mEncoder.WriteUint16(counters->mPartitionIdChanges));
2792     SuccessOrExit(error = mEncoder.WriteUint16(counters->mBetterPartitionAttachAttempts));
2793     SuccessOrExit(error = mEncoder.WriteUint16(counters->mParentChanges));
2794 
2795 exit:
2796     return error;
2797 }
2798 
HandlePropertySet(void)2799 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_CNTR_MLE_COUNTERS>(void)
2800 {
2801     otThreadResetMleCounters(mInstance);
2802 
2803     return OT_ERROR_NONE;
2804 }
2805 
HandlePropertyGet(void)2806 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_ALL_IP_COUNTERS>(void)
2807 {
2808     otError             error    = OT_ERROR_NONE;
2809     const otIpCounters *counters = otThreadGetIp6Counters(mInstance);
2810 
2811     OT_ASSERT(counters != nullptr);
2812 
2813     // Encode Tx related counters
2814     SuccessOrExit(error = mEncoder.OpenStruct());
2815     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxSuccess));
2816     SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxFailure));
2817     SuccessOrExit(error = mEncoder.CloseStruct());
2818 
2819     // Encode Rx related counters
2820     SuccessOrExit(error = mEncoder.OpenStruct());
2821     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxSuccess));
2822     SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxFailure));
2823     SuccessOrExit(error = mEncoder.CloseStruct());
2824 
2825 exit:
2826     return error;
2827 }
2828 
2829 #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
HandlePropertyGet(void)2830 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_MAC_RETRY_HISTOGRAM>(void)
2831 {
2832     otError         error = OT_ERROR_NONE;
2833     const uint32_t *histogramDirect;
2834     const uint32_t *histogramIndirect;
2835     uint8_t         histogramDirectEntries;
2836     uint8_t         histogramIndirectEntries;
2837 
2838     histogramDirect   = otLinkGetTxDirectRetrySuccessHistogram(mInstance, &histogramDirectEntries);
2839     histogramIndirect = otLinkGetTxIndirectRetrySuccessHistogram(mInstance, &histogramIndirectEntries);
2840 
2841     OT_ASSERT((histogramDirectEntries == 0) || (histogramDirect != nullptr));
2842     OT_ASSERT((histogramIndirectEntries == 0) || (histogramIndirect != nullptr));
2843 
2844     // Encode direct message retries histogram
2845     SuccessOrExit(error = mEncoder.OpenStruct());
2846     for (uint8_t i = 0; i < histogramDirectEntries; i++)
2847     {
2848         SuccessOrExit(error = mEncoder.WriteUint32(histogramDirect[i]));
2849     }
2850     SuccessOrExit(error = mEncoder.CloseStruct());
2851 
2852     // Encode indirect message retries histogram
2853     SuccessOrExit(error = mEncoder.OpenStruct());
2854     for (uint8_t i = 0; i < histogramIndirectEntries; i++)
2855     {
2856         SuccessOrExit(error = mEncoder.WriteUint32(histogramIndirect[i]));
2857     }
2858     SuccessOrExit(error = mEncoder.CloseStruct());
2859 
2860 exit:
2861     return error;
2862 }
2863 
HandlePropertySet(void)2864 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_CNTR_MAC_RETRY_HISTOGRAM>(void)
2865 {
2866     otLinkResetTxRetrySuccessHistogram(mInstance);
2867 
2868     return OT_ERROR_NONE;
2869 }
2870 #endif // OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
2871 
HandlePropertySet(void)2872 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_CNTR_ALL_IP_COUNTERS>(void)
2873 {
2874     otThreadResetIp6Counters(mInstance);
2875 
2876     return OT_ERROR_NONE;
2877 }
2878 
2879 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
2880 
HandlePropertyGet(void)2881 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_ALLOWLIST>(void)
2882 {
2883     otMacFilterEntry    entry;
2884     otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
2885     otError             error    = OT_ERROR_NONE;
2886 
2887     while (otLinkFilterGetNextAddress(mInstance, &iterator, &entry) == OT_ERROR_NONE)
2888     {
2889         SuccessOrExit(error = mEncoder.OpenStruct());
2890 
2891         SuccessOrExit(error = mEncoder.WriteEui64(entry.mExtAddress));
2892         SuccessOrExit(error = mEncoder.WriteInt8(entry.mRssIn));
2893 
2894         SuccessOrExit(error = mEncoder.CloseStruct());
2895     }
2896 
2897 exit:
2898     return error;
2899 }
2900 
HandlePropertyGet(void)2901 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_ALLOWLIST_ENABLED>(void)
2902 {
2903     return mEncoder.WriteBool(otLinkFilterGetAddressMode(mInstance) == OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST);
2904 }
2905 
HandlePropertyGet(void)2906 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_DENYLIST>(void)
2907 {
2908     otMacFilterEntry    entry;
2909     otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
2910     otError             error    = OT_ERROR_NONE;
2911 
2912     while (otLinkFilterGetNextAddress(mInstance, &iterator, &entry) == OT_ERROR_NONE)
2913     {
2914         SuccessOrExit(error = mEncoder.OpenStruct());
2915         SuccessOrExit(error = mEncoder.WriteEui64(entry.mExtAddress));
2916         SuccessOrExit(error = mEncoder.CloseStruct());
2917     }
2918 
2919 exit:
2920     return error;
2921 }
2922 
HandlePropertyGet(void)2923 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_DENYLIST_ENABLED>(void)
2924 {
2925     return mEncoder.WriteBool(otLinkFilterGetAddressMode(mInstance) == OT_MAC_FILTER_ADDRESS_MODE_DENYLIST);
2926 }
2927 
HandlePropertyGet(void)2928 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_FIXED_RSS>(void)
2929 {
2930     otMacFilterEntry    entry;
2931     otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
2932     otError             error    = OT_ERROR_NONE;
2933 
2934     while (otLinkFilterGetNextRssIn(mInstance, &iterator, &entry) == OT_ERROR_NONE)
2935     {
2936         SuccessOrExit(error = mEncoder.OpenStruct());
2937 
2938         SuccessOrExit(error = mEncoder.WriteEui64(entry.mExtAddress));
2939         SuccessOrExit(error = mEncoder.WriteInt8(entry.mRssIn));
2940 
2941         SuccessOrExit(error = mEncoder.CloseStruct());
2942     }
2943 
2944 exit:
2945     return error;
2946 }
2947 
HandlePropertySet(void)2948 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_ALLOWLIST>(void)
2949 {
2950     otError error = OT_ERROR_NONE;
2951 
2952     // First, clear the address filter entries.
2953     otLinkFilterClearAddresses(mInstance);
2954 
2955     while (mDecoder.GetRemainingLengthInStruct() > 0)
2956     {
2957         const otExtAddress *extAddress = nullptr;
2958         int8_t              rss;
2959 
2960         SuccessOrExit(error = mDecoder.OpenStruct());
2961         SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
2962 
2963         if (!mDecoder.IsAllReadInStruct())
2964         {
2965             SuccessOrExit(error = mDecoder.ReadInt8(rss));
2966         }
2967         else
2968         {
2969             rss = OT_MAC_FILTER_FIXED_RSS_DISABLED;
2970         }
2971 
2972         SuccessOrExit(error = mDecoder.CloseStruct());
2973 
2974         error = otLinkFilterAddAddress(mInstance, extAddress);
2975 
2976         if (error == OT_ERROR_ALREADY)
2977         {
2978             error = OT_ERROR_NONE;
2979         }
2980 
2981         SuccessOrExit(error);
2982 
2983         if (rss != OT_MAC_FILTER_FIXED_RSS_DISABLED)
2984         {
2985             SuccessOrExit(error = otLinkFilterAddRssIn(mInstance, extAddress, rss));
2986         }
2987     }
2988 
2989 exit:
2990     // If we had an error, we may have actually changed
2991     // the state of the allowlist, so we need to report
2992     // those incomplete changes via an asynchronous
2993     // change event.
2994 
2995     if (error != OT_ERROR_NONE)
2996     {
2997         IgnoreError(WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_MAC_ALLOWLIST));
2998     }
2999 
3000     return error;
3001 }
3002 
HandlePropertySet(void)3003 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_ALLOWLIST_ENABLED>(void)
3004 {
3005     bool                   enabled;
3006     otError                error = OT_ERROR_NONE;
3007     otMacFilterAddressMode mode  = OT_MAC_FILTER_ADDRESS_MODE_DISABLED;
3008 
3009     SuccessOrExit(error = mDecoder.ReadBool(enabled));
3010 
3011     if (enabled)
3012     {
3013         mode = OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST;
3014     }
3015 
3016     otLinkFilterSetAddressMode(mInstance, mode);
3017 
3018 exit:
3019     return error;
3020 }
3021 
HandlePropertySet(void)3022 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_DENYLIST>(void)
3023 {
3024     otError error = OT_ERROR_NONE;
3025 
3026     // First, clear the address filter entries.
3027     otLinkFilterClearAddresses(mInstance);
3028 
3029     while (mDecoder.GetRemainingLengthInStruct() > 0)
3030     {
3031         const otExtAddress *extAddress = nullptr;
3032 
3033         SuccessOrExit(error = mDecoder.OpenStruct());
3034         SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
3035         SuccessOrExit(error = mDecoder.CloseStruct());
3036 
3037         SuccessOrExit(error = otLinkFilterAddAddress(mInstance, extAddress));
3038     }
3039 
3040 exit:
3041     // If we had an error, we may have actually changed
3042     // the state of the denylist, so we need to report
3043     // those incomplete changes via an asynchronous
3044     // change event.
3045 
3046     if (error != OT_ERROR_NONE)
3047     {
3048         IgnoreError(WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_MAC_DENYLIST));
3049     }
3050 
3051     return error;
3052 }
3053 
HandlePropertySet(void)3054 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_DENYLIST_ENABLED>(void)
3055 {
3056     bool                   enabled;
3057     otError                error = OT_ERROR_NONE;
3058     otMacFilterAddressMode mode  = OT_MAC_FILTER_ADDRESS_MODE_DISABLED;
3059 
3060     SuccessOrExit(error = mDecoder.ReadBool(enabled));
3061 
3062     if (enabled)
3063     {
3064         mode = OT_MAC_FILTER_ADDRESS_MODE_DENYLIST;
3065     }
3066 
3067     otLinkFilterSetAddressMode(mInstance, mode);
3068 
3069 exit:
3070     return error;
3071 }
3072 
HandlePropertySet(void)3073 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_FIXED_RSS>(void)
3074 {
3075     otError error = OT_ERROR_NONE;
3076 
3077     // First, clear the address filter entries.
3078     otLinkFilterClearAllRssIn(mInstance);
3079 
3080     while (mDecoder.GetRemainingLengthInStruct() > 0)
3081     {
3082         const otExtAddress *extAddress;
3083         int8_t              rss;
3084 
3085         SuccessOrExit(error = mDecoder.OpenStruct());
3086 
3087         if (mDecoder.GetRemainingLengthInStruct() > sizeof(otExtAddress))
3088         {
3089             SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
3090         }
3091         else
3092         {
3093             extAddress = nullptr;
3094         }
3095 
3096         SuccessOrExit(error = mDecoder.ReadInt8(rss));
3097 
3098         SuccessOrExit(error = mDecoder.CloseStruct());
3099 
3100         if (extAddress != nullptr)
3101         {
3102             SuccessOrExit(error = otLinkFilterAddRssIn(mInstance, extAddress, rss));
3103         }
3104         else
3105         {
3106             otLinkFilterSetDefaultRssIn(mInstance, rss);
3107         }
3108     }
3109 
3110 exit:
3111     // If we had an error, we may have actually changed
3112     // the state of the RssIn filter, so we need to report
3113     // those incomplete changes via an asynchronous
3114     // change event.
3115 
3116     if (error != OT_ERROR_NONE)
3117     {
3118         IgnoreError(WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_MAC_FIXED_RSS));
3119     }
3120 
3121     return error;
3122 }
3123 #endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
3124 
3125 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
HandlePropertySet(void)3126 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_LINK_METRICS_QUERY>(void)
3127 {
3128     otError             error = OT_ERROR_NONE;
3129     struct otIp6Address address;
3130     uint8_t             seriesId;
3131     otLinkMetrics       linkMetrics = {false, false, false, false, false};
3132 
3133     SuccessOrExit(error = mDecoder.ReadIp6Address(address));
3134     SuccessOrExit(error = mDecoder.ReadUint8(seriesId));
3135     SuccessOrExit(error = DecodeLinkMetrics(&linkMetrics, /* aAllowPduCount */ true));
3136 
3137     error =
3138         otLinkMetricsQuery(mInstance, &address, seriesId, &linkMetrics, &NcpBase::HandleLinkMetricsReport_Jump, this);
3139 
3140 exit:
3141     return error;
3142 }
3143 
HandlePropertySet(void)3144 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_LINK_METRICS_PROBE>(void)
3145 {
3146     otError             error = OT_ERROR_NONE;
3147     struct otIp6Address address;
3148     uint8_t             seriesId;
3149     uint8_t             length;
3150 
3151     SuccessOrExit(error = mDecoder.ReadIp6Address(address));
3152     SuccessOrExit(error = mDecoder.ReadUint8(seriesId));
3153     SuccessOrExit(error = mDecoder.ReadUint8(length));
3154 
3155     error = otLinkMetricsSendLinkProbe(mInstance, &address, seriesId, length);
3156 
3157 exit:
3158     return error;
3159 }
3160 
HandlePropertySet(void)3161 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_LINK_METRICS_MGMT_ENH_ACK>(void)
3162 {
3163     otError             error = OT_ERROR_NONE;
3164     struct otIp6Address address;
3165     uint8_t             controlFlags;
3166     otLinkMetrics       linkMetrics = {false, false, false, false, false};
3167 
3168     SuccessOrExit(error = mDecoder.ReadIp6Address(address));
3169     SuccessOrExit(error = mDecoder.ReadUint8(controlFlags));
3170     SuccessOrExit(error = DecodeLinkMetrics(&linkMetrics, /* aAllowPduCount */ false));
3171 
3172     error = otLinkMetricsConfigEnhAckProbing(mInstance, &address, static_cast<otLinkMetricsEnhAckFlags>(controlFlags),
3173                                              controlFlags ? &linkMetrics : nullptr,
3174                                              &NcpBase::HandleLinkMetricsMgmtResponse_Jump, this,
3175                                              &NcpBase::HandleLinkMetricsEnhAckProbingIeReport_Jump, this);
3176 
3177 exit:
3178     return error;
3179 }
3180 
HandlePropertySet(void)3181 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_LINK_METRICS_MGMT_FORWARD>(void)
3182 {
3183     otError                  error = OT_ERROR_NONE;
3184     struct otIp6Address      address;
3185     uint8_t                  seriesId;
3186     uint8_t                  types;
3187     otLinkMetrics            linkMetrics = {false, false, false, false, false};
3188     otLinkMetricsSeriesFlags seriesFlags = {false, false, false, false};
3189 
3190     SuccessOrExit(error = mDecoder.ReadIp6Address(address));
3191     SuccessOrExit(error = mDecoder.ReadUint8(seriesId));
3192     SuccessOrExit(error = mDecoder.ReadUint8(types));
3193 
3194     SuccessOrExit(error = DecodeLinkMetrics(&linkMetrics, /* aAllowPduCount */ true));
3195 
3196     if (types & SPINEL_THREAD_FRAME_TYPE_MLE_LINK_PROBE)
3197     {
3198         seriesFlags.mLinkProbe = true;
3199     }
3200 
3201     if (types & SPINEL_THREAD_FRAME_TYPE_MAC_DATA)
3202     {
3203         seriesFlags.mMacData = true;
3204     }
3205 
3206     if (types & SPINEL_THREAD_FRAME_TYPE_MAC_DATA_REQUEST)
3207     {
3208         seriesFlags.mMacDataRequest = true;
3209     }
3210 
3211     if (types & SPINEL_THREAD_FRAME_TYPE_MAC_ACK)
3212     {
3213         seriesFlags.mMacAck = true;
3214     }
3215 
3216     error = otLinkMetricsConfigForwardTrackingSeries(mInstance, &address, seriesId, seriesFlags, &linkMetrics,
3217                                                      &NcpBase::HandleLinkMetricsMgmtResponse_Jump, this);
3218 
3219 exit:
3220     return error;
3221 }
3222 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
3223 
HandlePropertyGet(void)3224 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_MODE>(void)
3225 {
3226     uint8_t          numericMode;
3227     otLinkModeConfig modeConfig = otThreadGetLinkMode(mInstance);
3228 
3229     numericMode = LinkFlagsToFlagByte(modeConfig.mRxOnWhenIdle, modeConfig.mDeviceType, modeConfig.mNetworkData);
3230 
3231     return mEncoder.WriteUint8(numericMode);
3232 }
3233 
HandlePropertySet(void)3234 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_MODE>(void)
3235 {
3236     uint8_t          numericMode = 0;
3237     otLinkModeConfig modeConfig;
3238     otError          error = OT_ERROR_NONE;
3239 
3240     SuccessOrExit(error = mDecoder.ReadUint8(numericMode));
3241 
3242     modeConfig.mRxOnWhenIdle =
3243         ((numericMode & SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE) == SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE);
3244     modeConfig.mDeviceType = ((numericMode & SPINEL_THREAD_MODE_FULL_THREAD_DEV) == SPINEL_THREAD_MODE_FULL_THREAD_DEV);
3245     modeConfig.mNetworkData =
3246         ((numericMode & SPINEL_THREAD_MODE_FULL_NETWORK_DATA) == SPINEL_THREAD_MODE_FULL_NETWORK_DATA);
3247 
3248     error = otThreadSetLinkMode(mInstance, modeConfig);
3249 
3250 exit:
3251     return error;
3252 }
3253 
HandlePropertyGet(void)3254 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_CHILD_TIMEOUT>(void)
3255 {
3256     return mEncoder.WriteUint32(otThreadGetChildTimeout(mInstance));
3257 }
3258 
HandlePropertySet(void)3259 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_CHILD_TIMEOUT>(void)
3260 {
3261     uint32_t timeout = 0;
3262     otError  error   = OT_ERROR_NONE;
3263 
3264     SuccessOrExit(error = mDecoder.ReadUint32(timeout));
3265 
3266     otThreadSetChildTimeout(mInstance, timeout);
3267 
3268 exit:
3269     return error;
3270 }
3271 
HandlePropertyGet(void)3272 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_RLOC16>(void)
3273 {
3274     return mEncoder.WriteUint16(otThreadGetRloc16(mInstance));
3275 }
3276 
HandlePropertyGet(void)3277 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING>(void)
3278 {
3279     return mEncoder.WriteBool(mRequireJoinExistingNetwork);
3280 }
3281 
HandlePropertySet(void)3282 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING>(void)
3283 {
3284     return mDecoder.ReadBool(mRequireJoinExistingNetwork);
3285 }
3286 
HandlePropertySet(void)3287 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_STREAM_NET_INSECURE>(void)
3288 {
3289     const uint8_t    *framePtr    = nullptr;
3290     uint16_t          frameLen    = 0;
3291     const uint8_t    *metaPtr     = nullptr;
3292     uint16_t          metaLen     = 0;
3293     otMessage        *message     = nullptr;
3294     otError           error       = OT_ERROR_NONE;
3295     otMessageSettings msgSettings = {false, OT_MESSAGE_PRIORITY_NORMAL};
3296 
3297     SuccessOrExit(error = mDecoder.ReadDataWithLen(framePtr, frameLen));
3298     SuccessOrExit(error = mDecoder.ReadData(metaPtr, metaLen));
3299 
3300     // We ignore metadata for now.
3301     // May later include TX power, allow retransmits, etc...
3302 
3303     // STREAM_NET_INSECURE packets are not secured at layer 2.
3304     message = otIp6NewMessageFromBuffer(mInstance, framePtr, frameLen, &msgSettings);
3305     VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
3306 
3307     // Ensure the insecure message is forwarded using direct transmission.
3308     otMessageSetDirectTransmission(message, true);
3309 
3310     error = otIp6Send(mInstance, message);
3311 
3312 exit:
3313     if (error == OT_ERROR_NONE)
3314     {
3315         mInboundInsecureIpFrameCounter++;
3316     }
3317     else
3318     {
3319         mDroppedInboundIpFrameCounter++;
3320     }
3321 
3322     return error;
3323 }
3324 
HandlePropertySet(void)3325 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_CNTR_RESET>(void)
3326 {
3327     otLinkResetCounters(mInstance);
3328 #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
3329     otLinkResetTxRetrySuccessHistogram(mInstance);
3330 #endif
3331     otThreadResetIp6Counters(mInstance);
3332     otThreadResetMleCounters(mInstance);
3333     ResetCounters();
3334 
3335     return OT_ERROR_NONE;
3336 }
3337 
HandlePropertyInsert(void)3338 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_THREAD_ASSISTING_PORTS>(void)
3339 {
3340     otError  error = OT_ERROR_NONE;
3341     uint16_t port;
3342 
3343     SuccessOrExit(error = mDecoder.ReadUint16(port));
3344 
3345     error = otIp6AddUnsecurePort(mInstance, port);
3346 exit:
3347     return error;
3348 }
3349 
3350 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
3351 
HandlePropertyInsert(void)3352 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_MAC_ALLOWLIST>(void)
3353 {
3354     otError             error = OT_ERROR_NONE;
3355     const otExtAddress *extAddress;
3356     int8_t              rss = OT_MAC_FILTER_FIXED_RSS_DISABLED;
3357 
3358     SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
3359 
3360     if (!mDecoder.IsAllRead())
3361     {
3362         SuccessOrExit(error = mDecoder.ReadInt8(rss));
3363     }
3364 
3365     error = otLinkFilterAddAddress(mInstance, extAddress);
3366 
3367     if (error == OT_ERROR_ALREADY)
3368     {
3369         error = OT_ERROR_NONE;
3370     }
3371 
3372     SuccessOrExit(error);
3373 
3374     if (rss != OT_MAC_FILTER_FIXED_RSS_DISABLED)
3375     {
3376         error = otLinkFilterAddRssIn(mInstance, extAddress, rss);
3377     }
3378 
3379 exit:
3380     return error;
3381 }
3382 
HandlePropertyInsert(void)3383 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_MAC_DENYLIST>(void)
3384 {
3385     otError             error = OT_ERROR_NONE;
3386     const otExtAddress *extAddress;
3387 
3388     SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
3389 
3390     error = otLinkFilterAddAddress(mInstance, extAddress);
3391 
3392     if (error == OT_ERROR_ALREADY)
3393     {
3394         error = OT_ERROR_NONE;
3395     }
3396 
3397 exit:
3398     return error;
3399 }
3400 
HandlePropertyInsert(void)3401 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_MAC_FIXED_RSS>(void)
3402 {
3403     otError             error      = OT_ERROR_NONE;
3404     const otExtAddress *extAddress = nullptr;
3405     int8_t              rss        = OT_MAC_FILTER_FIXED_RSS_DISABLED;
3406 
3407     if (mDecoder.GetRemainingLength() > sizeof(int8_t))
3408     {
3409         SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
3410     }
3411 
3412     SuccessOrExit(error = mDecoder.ReadInt8(rss));
3413 
3414     if (extAddress != nullptr)
3415     {
3416         error = otLinkFilterAddRssIn(mInstance, extAddress, rss);
3417     }
3418     else
3419     {
3420         otLinkFilterSetDefaultRssIn(mInstance, rss);
3421     }
3422 
3423 exit:
3424     return error;
3425 }
3426 
3427 #endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
3428 
HandlePropertyRemove(void)3429 template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_THREAD_ASSISTING_PORTS>(void)
3430 {
3431     otError  error = OT_ERROR_NONE;
3432     uint16_t port;
3433 
3434     SuccessOrExit(error = mDecoder.ReadUint16(port));
3435 
3436     error = otIp6RemoveUnsecurePort(mInstance, port);
3437 
3438     // If unsecure port was not on the list, "remove" command is successful.
3439     if (error == OT_ERROR_NOT_FOUND)
3440     {
3441         error = OT_ERROR_NONE;
3442     }
3443 
3444 exit:
3445     return error;
3446 }
3447 
3448 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
3449 
HandlePropertyRemove(void)3450 template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_MAC_ALLOWLIST>(void)
3451 {
3452     otError             error      = OT_ERROR_NONE;
3453     const otExtAddress *extAddress = nullptr;
3454 
3455     SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
3456 
3457     otLinkFilterRemoveAddress(mInstance, extAddress);
3458 
3459 exit:
3460     return error;
3461 }
3462 
HandlePropertyRemove(void)3463 template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_MAC_DENYLIST>(void)
3464 {
3465     otError             error      = OT_ERROR_NONE;
3466     const otExtAddress *extAddress = nullptr;
3467 
3468     SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
3469 
3470     otLinkFilterRemoveAddress(mInstance, extAddress);
3471 
3472 exit:
3473     return error;
3474 }
3475 
HandlePropertyRemove(void)3476 template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_MAC_FIXED_RSS>(void)
3477 {
3478     otError             error      = OT_ERROR_NONE;
3479     const otExtAddress *extAddress = nullptr;
3480 
3481     if (mDecoder.GetRemainingLength() > 0)
3482     {
3483         SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
3484     }
3485 
3486     if (extAddress != nullptr)
3487     {
3488         otLinkFilterRemoveRssIn(mInstance, extAddress);
3489     }
3490     else
3491     {
3492         otLinkFilterClearDefaultRssIn(mInstance);
3493     }
3494 
3495 exit:
3496     return error;
3497 }
3498 
3499 #endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
3500 
3501 #if OPENTHREAD_PLATFORM_POSIX
3502 
HandlePropertyGet(void)3503 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_RCP_VERSION>(void)
3504 {
3505     return mEncoder.WriteUtf8(otGetRadioVersionString(mInstance));
3506 }
3507 
3508 #endif
3509 
3510 #if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
3511 
HandlePropertyGet(void)3512 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SLAAC_ENABLED>(void)
3513 {
3514     return mEncoder.WriteBool(otIp6IsSlaacEnabled(mInstance));
3515 }
3516 
HandlePropertySet(void)3517 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SLAAC_ENABLED>(void)
3518 {
3519     otError error = OT_ERROR_NONE;
3520     bool    enabled;
3521 
3522     SuccessOrExit(error = mDecoder.ReadBool(enabled));
3523     otIp6SetSlaacEnabled(mInstance, enabled);
3524 
3525 exit:
3526     return error;
3527 }
3528 
3529 #endif // OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
3530 
HandlePropertyGet(void)3531 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SUPPORTED_RADIO_LINKS>(void)
3532 {
3533     otError error;
3534 
3535 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
3536     SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_RADIO_LINK_IEEE_802_15_4));
3537 #endif
3538 
3539 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
3540     SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_RADIO_LINK_TREL_UDP6));
3541 #endif
3542 
3543 exit:
3544     return error;
3545 }
3546 
3547 #if OPENTHREAD_CONFIG_MULTI_RADIO
3548 
EncodeNeighborMultiRadioInfo(uint32_t aSpinelRadioLink,const otRadioLinkInfo & aInfo)3549 otError NcpBase::EncodeNeighborMultiRadioInfo(uint32_t aSpinelRadioLink, const otRadioLinkInfo &aInfo)
3550 {
3551     otError error;
3552 
3553     SuccessOrExit(error = mEncoder.OpenStruct());
3554     SuccessOrExit(error = mEncoder.WriteUintPacked(aSpinelRadioLink));
3555     SuccessOrExit(error = mEncoder.WriteUint8(aInfo.mPreference));
3556     SuccessOrExit(error = mEncoder.CloseStruct());
3557 
3558 exit:
3559     return error;
3560 }
3561 
HandlePropertyGet(void)3562 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NEIGHBOR_TABLE_MULTI_RADIO_INFO>(void)
3563 {
3564     otError                  error = OT_ERROR_NONE;
3565     otNeighborInfoIterator   iter  = OT_NEIGHBOR_INFO_ITERATOR_INIT;
3566     otNeighborInfo           neighInfo;
3567     otMultiRadioNeighborInfo multiRadioInfo;
3568 
3569     while (otThreadGetNextNeighborInfo(mInstance, &iter, &neighInfo) == OT_ERROR_NONE)
3570     {
3571         SuccessOrExit(error = mEncoder.OpenStruct());
3572 
3573         SuccessOrExit(error = mEncoder.WriteEui64(neighInfo.mExtAddress));
3574         SuccessOrExit(error = mEncoder.WriteUint16(neighInfo.mRloc16));
3575 
3576         if (otMultiRadioGetNeighborInfo(mInstance, &neighInfo.mExtAddress, &multiRadioInfo) == OT_ERROR_NONE)
3577         {
3578             if (multiRadioInfo.mSupportsIeee802154)
3579             {
3580                 SuccessOrExit(error = EncodeNeighborMultiRadioInfo(SPINEL_RADIO_LINK_IEEE_802_15_4,
3581                                                                    multiRadioInfo.mIeee802154Info));
3582             }
3583 
3584             if (multiRadioInfo.mSupportsTrelUdp6)
3585             {
3586                 SuccessOrExit(
3587                     error = EncodeNeighborMultiRadioInfo(SPINEL_RADIO_LINK_TREL_UDP6, multiRadioInfo.mTrelUdp6Info));
3588             }
3589         }
3590 
3591         SuccessOrExit(error = mEncoder.CloseStruct());
3592     }
3593 
3594 exit:
3595     return error;
3596 }
3597 #endif // OPENTHREAD_CONFIG_MULTI_RADIO
3598 
3599 // ----------------------------------------------------------------------------
3600 // SRP Client
3601 // ----------------------------------------------------------------------------
3602 
3603 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
3604 
HandlePropertySet(void)3605 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SRP_CLIENT_START>(void)
3606 {
3607     otError    error = OT_ERROR_NONE;
3608     bool       start;
3609     bool       callbackEnabled;
3610     otSockAddr serverAddr;
3611 
3612     SuccessOrExit(error = mDecoder.ReadBool(start));
3613 
3614     if (!start)
3615     {
3616         otSrpClientStop(mInstance);
3617         ExitNow();
3618     }
3619 
3620     SuccessOrExit(error = mDecoder.ReadIp6Address(serverAddr.mAddress));
3621     SuccessOrExit(error = mDecoder.ReadUint16(serverAddr.mPort));
3622     SuccessOrExit(error = mDecoder.ReadBool(callbackEnabled));
3623 
3624     SuccessOrExit(error = otSrpClientStart(mInstance, &serverAddr));
3625     mSrpClientCallbackEnabled = callbackEnabled;
3626 
3627 exit:
3628     return error;
3629 }
3630 
HandlePropertyGet(void)3631 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SRP_CLIENT_LEASE_INTERVAL>(void)
3632 {
3633     return mEncoder.WriteUint32(otSrpClientGetLeaseInterval(mInstance));
3634 }
3635 
HandlePropertySet(void)3636 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SRP_CLIENT_LEASE_INTERVAL>(void)
3637 {
3638     otError  error;
3639     uint32_t interval;
3640 
3641     SuccessOrExit(error = mDecoder.ReadUint32(interval));
3642     otSrpClientSetLeaseInterval(mInstance, interval);
3643 
3644 exit:
3645     return error;
3646 }
3647 
HandlePropertyGet(void)3648 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SRP_CLIENT_KEY_LEASE_INTERVAL>(void)
3649 {
3650     return mEncoder.WriteUint32(otSrpClientGetKeyLeaseInterval(mInstance));
3651 }
3652 
HandlePropertySet(void)3653 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SRP_CLIENT_KEY_LEASE_INTERVAL>(void)
3654 {
3655     otError  error;
3656     uint32_t interval;
3657 
3658     SuccessOrExit(error = mDecoder.ReadUint32(interval));
3659     otSrpClientSetKeyLeaseInterval(mInstance, interval);
3660 
3661 exit:
3662     return error;
3663 }
3664 
SrpClientItemStateToSpinel(otSrpClientItemState aItemState)3665 static spinel_srp_client_item_state_t SrpClientItemStateToSpinel(otSrpClientItemState aItemState)
3666 {
3667     spinel_srp_client_item_state_t state = SPINEL_SRP_CLIENT_ITEM_STATE_REMOVED;
3668 
3669     switch (aItemState)
3670     {
3671     case OT_SRP_CLIENT_ITEM_STATE_TO_ADD:
3672         state = SPINEL_SRP_CLIENT_ITEM_STATE_TO_ADD;
3673         break;
3674     case OT_SRP_CLIENT_ITEM_STATE_ADDING:
3675         state = SPINEL_SRP_CLIENT_ITEM_STATE_ADDING;
3676         break;
3677     case OT_SRP_CLIENT_ITEM_STATE_TO_REFRESH:
3678         state = SPINEL_SRP_CLIENT_ITEM_STATE_TO_REFRESH;
3679         break;
3680     case OT_SRP_CLIENT_ITEM_STATE_REFRESHING:
3681         state = SPINEL_SRP_CLIENT_ITEM_STATE_REFRESHING;
3682         break;
3683     case OT_SRP_CLIENT_ITEM_STATE_TO_REMOVE:
3684         state = SPINEL_SRP_CLIENT_ITEM_STATE_TO_REMOVE;
3685         break;
3686     case OT_SRP_CLIENT_ITEM_STATE_REMOVING:
3687         state = SPINEL_SRP_CLIENT_ITEM_STATE_REMOVING;
3688         break;
3689     case OT_SRP_CLIENT_ITEM_STATE_REGISTERED:
3690         state = SPINEL_SRP_CLIENT_ITEM_STATE_REGISTERED;
3691         break;
3692     case OT_SRP_CLIENT_ITEM_STATE_REMOVED:
3693         state = SPINEL_SRP_CLIENT_ITEM_STATE_REMOVED;
3694         break;
3695     }
3696 
3697     return state;
3698 }
3699 
EncodeSrpClientHostInfo(const otSrpClientHostInfo & aHostInfo)3700 otError NcpBase::EncodeSrpClientHostInfo(const otSrpClientHostInfo &aHostInfo)
3701 {
3702     otError error;
3703 
3704     SuccessOrExit(error = mEncoder.WriteUtf8(aHostInfo.mName != nullptr ? aHostInfo.mName : ""));
3705     SuccessOrExit(error = mEncoder.WriteUint8(SrpClientItemStateToSpinel(aHostInfo.mState)));
3706 
3707     SuccessOrExit(error = mEncoder.OpenStruct());
3708 
3709     for (uint8_t index = 0; index < aHostInfo.mNumAddresses; index++)
3710     {
3711         SuccessOrExit(error = mEncoder.WriteIp6Address(aHostInfo.mAddresses[index]));
3712     }
3713 
3714     SuccessOrExit(error = mEncoder.CloseStruct());
3715 
3716 exit:
3717     return error;
3718 }
3719 
HandlePropertyGet(void)3720 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SRP_CLIENT_HOST_INFO>(void)
3721 {
3722     return EncodeSrpClientHostInfo(*otSrpClientGetHostInfo(mInstance));
3723 }
3724 
HandlePropertyGet(void)3725 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SRP_CLIENT_HOST_NAME>(void)
3726 {
3727     const char *name = otSrpClientGetHostInfo(mInstance)->mName;
3728 
3729     return mEncoder.WriteUtf8(name != nullptr ? name : "");
3730 }
3731 
HandlePropertySet(void)3732 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SRP_CLIENT_HOST_NAME>(void)
3733 {
3734     otError     error;
3735     const char *name;
3736     uint16_t    size;
3737     char       *hostNameBuffer;
3738 
3739     SuccessOrExit(error = mDecoder.ReadUtf8(name));
3740 
3741     hostNameBuffer = otSrpClientBuffersGetHostNameString(mInstance, &size);
3742 
3743     VerifyOrExit(StringLength(name, size) < size, error = OT_ERROR_INVALID_ARGS);
3744 
3745     // We first make sure we can set the name, and if so
3746     // we copy it to the persisted buffer and set
3747     // the host name again now with the persisted buffer.
3748     // This ensures that we do not overwrite a previous
3749     // buffer with a host name that cannot be set.
3750 
3751     SuccessOrExit(error = otSrpClientSetHostName(mInstance, name));
3752 
3753     strcpy(hostNameBuffer, name);
3754     SuccessOrAssert(error = otSrpClientSetHostName(mInstance, hostNameBuffer));
3755 
3756 exit:
3757     return error;
3758 }
3759 
HandlePropertyGet(void)3760 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SRP_CLIENT_HOST_ADDRESSES>(void)
3761 {
3762     otError                    error    = OT_ERROR_NONE;
3763     const otSrpClientHostInfo *hostInfo = otSrpClientGetHostInfo(mInstance);
3764 
3765     for (uint8_t index = 0; index < hostInfo->mNumAddresses; index++)
3766     {
3767         SuccessOrExit(error = mEncoder.WriteIp6Address(hostInfo->mAddresses[index]));
3768     }
3769 
3770 exit:
3771     return error;
3772 }
3773 
HandlePropertySet(void)3774 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SRP_CLIENT_HOST_ADDRESSES>(void)
3775 {
3776     otError       error;
3777     otIp6Address  addresses[kSrpClientMaxHostAddresses];
3778     uint8_t       numAddresses = 0;
3779     otIp6Address *hostAddressArray;
3780     uint8_t       hostAddressArrayLength;
3781 
3782     hostAddressArray = otSrpClientBuffersGetHostAddressesArray(mInstance, &hostAddressArrayLength);
3783     OT_ASSERT(hostAddressArrayLength <= kSrpClientMaxHostAddresses);
3784 
3785     while (!mDecoder.IsAllReadInStruct())
3786     {
3787         VerifyOrExit(numAddresses < kSrpClientMaxHostAddresses, error = OT_ERROR_NO_BUFS);
3788 
3789         SuccessOrExit(error = mDecoder.ReadIp6Address(addresses[numAddresses]));
3790         numAddresses++;
3791     }
3792 
3793     // We first make sure we can set the addresses, and if so we copy
3794     // the address list into `hostAddressArray` and set it again. This
3795     // ensures that we do not overwrite a previous list before we know
3796     // it is safe to set/change the address list.
3797 
3798     SuccessOrExit(error = otSrpClientSetHostAddresses(mInstance, addresses, numAddresses));
3799 
3800     memcpy(hostAddressArray, addresses, sizeof(addresses));
3801 
3802     SuccessOrAssert(error = otSrpClientSetHostAddresses(mInstance, hostAddressArray, numAddresses));
3803 
3804 exit:
3805     return error;
3806 }
3807 
EncodeSrpClientServices(const otSrpClientService * aServices)3808 otError NcpBase::EncodeSrpClientServices(const otSrpClientService *aServices)
3809 {
3810     otError error = OT_ERROR_NONE;
3811 
3812     for (; aServices != nullptr; aServices = aServices->mNext)
3813     {
3814         SuccessOrExit(error = mEncoder.OpenStruct());
3815 
3816         SuccessOrExit(error = mEncoder.WriteUtf8(aServices->mName));
3817         SuccessOrExit(error = mEncoder.WriteUtf8(aServices->mInstanceName));
3818         SuccessOrExit(error = mEncoder.WriteUint16(aServices->mPort));
3819         SuccessOrExit(error = mEncoder.WriteUint16(aServices->mPriority));
3820         SuccessOrExit(error = mEncoder.WriteUint16(aServices->mWeight));
3821 
3822         SuccessOrExit(error = mEncoder.CloseStruct());
3823     }
3824 
3825 exit:
3826     return error;
3827 }
3828 
HandlePropertyGet(void)3829 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SRP_CLIENT_SERVICES>(void)
3830 {
3831     return EncodeSrpClientServices(otSrpClientGetServices(mInstance));
3832 }
3833 
HandlePropertyInsert(void)3834 template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_SRP_CLIENT_SERVICES>(void)
3835 {
3836     otError                         error = OT_ERROR_NONE;
3837     otSrpClientBuffersServiceEntry *entry = nullptr;
3838     const char                     *serviceName;
3839     const char                     *instanceName;
3840     char                           *stringBuffer;
3841     uint16_t                        size;
3842 
3843     entry = otSrpClientBuffersAllocateService(mInstance);
3844     VerifyOrExit(entry != nullptr, error = OT_ERROR_NO_BUFS);
3845 
3846     stringBuffer = otSrpClientBuffersGetServiceEntryServiceNameString(entry, &size);
3847     SuccessOrExit(error = mDecoder.ReadUtf8(serviceName));
3848     VerifyOrExit(StringLength(serviceName, size) < size, error = OT_ERROR_INVALID_ARGS);
3849     strcpy(stringBuffer, serviceName);
3850 
3851     stringBuffer = otSrpClientBuffersGetServiceEntryInstanceNameString(entry, &size);
3852     SuccessOrExit(error = mDecoder.ReadUtf8(instanceName));
3853     VerifyOrExit(StringLength(instanceName, size) < size, error = OT_ERROR_INVALID_ARGS);
3854     strcpy(stringBuffer, instanceName);
3855 
3856     SuccessOrExit(error = mDecoder.ReadUint16(entry->mService.mPort));
3857     SuccessOrExit(error = mDecoder.ReadUint16(entry->mService.mPriority));
3858     SuccessOrExit(error = mDecoder.ReadUint16(entry->mService.mWeight));
3859 
3860     SuccessOrExit(error = otSrpClientAddService(mInstance, &entry->mService));
3861     entry = nullptr;
3862 
3863 exit:
3864     if (entry != nullptr)
3865     {
3866         otSrpClientBuffersFreeService(mInstance, entry);
3867     }
3868 
3869     return error;
3870 }
3871 
HandlePropertyRemove(void)3872 template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_SRP_CLIENT_SERVICES>(void)
3873 {
3874     otError                   error = OT_ERROR_NONE;
3875     const char               *serviceName;
3876     const char               *instanceName;
3877     bool                      toClear = false;
3878     const otSrpClientService *service;
3879 
3880     SuccessOrExit(error = mDecoder.ReadUtf8(serviceName));
3881     SuccessOrExit(error = mDecoder.ReadUtf8(instanceName));
3882 
3883     if (!mDecoder.IsAllReadInStruct())
3884     {
3885         SuccessOrExit(error = mDecoder.ReadBool(toClear));
3886     }
3887 
3888     for (service = otSrpClientGetServices(mInstance); service != nullptr; service = service->mNext)
3889     {
3890         if ((strcmp(serviceName, service->mName) == 0) || (strcmp(instanceName, service->mInstanceName) == 0))
3891         {
3892             break;
3893         }
3894     }
3895 
3896     VerifyOrExit(service != nullptr, error = OT_ERROR_NOT_FOUND);
3897 
3898     if (toClear)
3899     {
3900         SuccessOrExit(error = otSrpClientClearService(mInstance, const_cast<otSrpClientService *>(service)));
3901         otSrpClientBuffersFreeService(
3902             mInstance, reinterpret_cast<otSrpClientBuffersServiceEntry *>(const_cast<otSrpClientService *>(service)));
3903     }
3904     else
3905     {
3906         error = otSrpClientRemoveService(mInstance, const_cast<otSrpClientService *>(service));
3907     }
3908 
3909 exit:
3910     return error;
3911 }
3912 
HandlePropertySet(void)3913 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SRP_CLIENT_HOST_SERVICES_REMOVE>(void)
3914 {
3915     otError error = OT_ERROR_NONE;
3916     bool    removeKeyLease;
3917     bool    sendUnregToServer;
3918 
3919     SuccessOrExit(error = mDecoder.ReadBool(removeKeyLease));
3920     SuccessOrExit(error = mDecoder.ReadBool(sendUnregToServer));
3921 
3922     error = otSrpClientRemoveHostAndServices(mInstance, removeKeyLease, sendUnregToServer);
3923 
3924 exit:
3925     return error;
3926 }
3927 
HandlePropertySet(void)3928 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SRP_CLIENT_HOST_SERVICES_CLEAR>(void)
3929 {
3930     otSrpClientClearHostAndServices(mInstance);
3931 
3932     return OT_ERROR_NONE;
3933 }
3934 
SrpClientErrorToSpinelError(otError aError)3935 static spinel_srp_client_error_t SrpClientErrorToSpinelError(otError aError)
3936 {
3937     spinel_srp_client_error_t error = SPINEL_SRP_CLIENT_ERROR_FAILED;
3938 
3939     switch (aError)
3940     {
3941     case OT_ERROR_NONE:
3942         error = SPINEL_SRP_CLIENT_ERROR_NONE;
3943         break;
3944     case OT_ERROR_PARSE:
3945         error = SPINEL_SRP_CLIENT_ERROR_PARSE;
3946         break;
3947     case OT_ERROR_NOT_FOUND:
3948         error = SPINEL_SRP_CLIENT_ERROR_NOT_FOUND;
3949         break;
3950     case OT_ERROR_NOT_IMPLEMENTED:
3951         error = SPINEL_SRP_CLIENT_ERROR_NOT_IMPLEMENTED;
3952         break;
3953     case OT_ERROR_SECURITY:
3954         error = SPINEL_SRP_CLIENT_ERROR_SECURITY;
3955         break;
3956     case OT_ERROR_DUPLICATED:
3957         error = SPINEL_SRP_CLIENT_ERROR_DUPLICATED;
3958         break;
3959     case OT_ERROR_RESPONSE_TIMEOUT:
3960         error = SPINEL_SRP_CLIENT_ERROR_RESPONSE_TIMEOUT;
3961         break;
3962     case OT_ERROR_INVALID_ARGS:
3963         error = SPINEL_SRP_CLIENT_ERROR_INVALID_ARGS;
3964         break;
3965     case OT_ERROR_NO_BUFS:
3966         error = SPINEL_SRP_CLIENT_ERROR_NO_BUFS;
3967         break;
3968     case OT_ERROR_FAILED:
3969     default:
3970         error = SPINEL_SRP_CLIENT_ERROR_FAILED;
3971         break;
3972     }
3973 
3974     return error;
3975 }
3976 
HandleSrpClientCallback(otError aError,const otSrpClientHostInfo * aHostInfo,const otSrpClientService * aServices,const otSrpClientService * aRemovedServices,void * aContext)3977 void NcpBase::HandleSrpClientCallback(otError                    aError,
3978                                       const otSrpClientHostInfo *aHostInfo,
3979                                       const otSrpClientService  *aServices,
3980                                       const otSrpClientService  *aRemovedServices,
3981                                       void                      *aContext)
3982 {
3983     static_cast<NcpBase *>(aContext)->HandleSrpClientCallback(aError, aHostInfo, aServices, aRemovedServices);
3984 }
3985 
HandleSrpClientCallback(otError aError,const otSrpClientHostInfo * aHostInfo,const otSrpClientService * aServices,const otSrpClientService * aRemovedServices)3986 void NcpBase::HandleSrpClientCallback(otError                    aError,
3987                                       const otSrpClientHostInfo *aHostInfo,
3988                                       const otSrpClientService  *aServices,
3989                                       const otSrpClientService  *aRemovedServices)
3990 {
3991     otError                   error = OT_ERROR_NONE;
3992     const otSrpClientService *service;
3993     const otSrpClientService *next;
3994 
3995     VerifyOrExit(mSrpClientCallbackEnabled);
3996 
3997     SuccessOrExit(error = mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS,
3998                                               SPINEL_PROP_SRP_CLIENT_EVENT));
3999 
4000     SuccessOrExit(error = mEncoder.WriteUint16(SrpClientErrorToSpinelError(aError)));
4001 
4002     SuccessOrExit(error = mEncoder.OpenStruct());
4003     SuccessOrExit(error = EncodeSrpClientHostInfo(*aHostInfo));
4004     SuccessOrExit(error = mEncoder.CloseStruct());
4005 
4006     SuccessOrExit(error = mEncoder.OpenStruct());
4007     SuccessOrExit(error = EncodeSrpClientServices(aServices));
4008     SuccessOrExit(error = mEncoder.CloseStruct());
4009 
4010     SuccessOrExit(error = mEncoder.OpenStruct());
4011     SuccessOrExit(error = EncodeSrpClientServices(aRemovedServices));
4012     SuccessOrExit(error = mEncoder.CloseStruct());
4013 
4014     SuccessOrExit(error = mEncoder.EndFrame());
4015 
4016 exit:
4017 
4018     if (error != OT_ERROR_NONE)
4019     {
4020         // Emit a NONMEM status if we fail to send the event.
4021         mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM);
4022         mUpdateChangedPropsTask.Post();
4023     }
4024 
4025     for (service = aRemovedServices; service != nullptr; service = next)
4026     {
4027         next = service->mNext;
4028 
4029         otSrpClientBuffersFreeService(
4030             mInstance, reinterpret_cast<otSrpClientBuffersServiceEntry *>(const_cast<otSrpClientService *>(service)));
4031     }
4032 }
4033 
4034 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
HandlePropertyGet(void)4035 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SRP_CLIENT_SERVICE_KEY_ENABLED>(void)
4036 {
4037     return mEncoder.WriteBool(otSrpClientIsServiceKeyRecordEnabled(mInstance));
4038 }
4039 
HandlePropertySet(void)4040 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SRP_CLIENT_SERVICE_KEY_ENABLED>(void)
4041 {
4042     otError error = OT_ERROR_NONE;
4043     bool    enabled;
4044 
4045     SuccessOrExit(error = mDecoder.ReadBool(enabled));
4046     otSrpClientSetServiceKeyRecordEnabled(mInstance, enabled);
4047 
4048 exit:
4049     return error;
4050 }
4051 #endif
4052 
4053 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
4054 
4055 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
HandlePropertyGet(void)4056 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_DEBUG_TREL_TEST_MODE_ENABLE>(void)
4057 {
4058     return mEncoder.WriteBool(!otTrelIsFilterEnabled(mInstance));
4059 }
4060 
HandlePropertySet(void)4061 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_DEBUG_TREL_TEST_MODE_ENABLE>(void)
4062 {
4063     otError error = OT_ERROR_NONE;
4064     bool    testMode;
4065 
4066     SuccessOrExit(error = mDecoder.ReadBool(testMode));
4067 
4068     // Note that `TEST_MODE` being `true` indicates that the TREL
4069     // interface should be enabled and functional, so filtering
4070     // should be disabled.
4071 
4072     otTrelSetFilterEnabled(mInstance, !testMode);
4073 
4074 exit:
4075     return error;
4076 }
4077 #endif
4078 
4079 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
HandlePropertyGet(void)4080 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_NETWORK_TIME>(void)
4081 {
4082     otError             error = OT_ERROR_NONE;
4083     otNetworkTimeStatus networkTimeStatus;
4084     uint64_t            time;
4085 
4086     networkTimeStatus = otNetworkTimeGet(mInstance, &time);
4087 
4088     SuccessOrExit(error = mEncoder.WriteUint64(time));
4089     SuccessOrExit(error = mEncoder.WriteInt8((int8_t)networkTimeStatus));
4090 
4091 exit:
4092     return error;
4093 }
4094 
HandleTimeSyncUpdate(void * aContext)4095 void NcpBase::HandleTimeSyncUpdate(void *aContext) { static_cast<NcpBase *>(aContext)->HandleTimeSyncUpdate(); }
4096 
HandleTimeSyncUpdate(void)4097 void NcpBase::HandleTimeSyncUpdate(void)
4098 {
4099     mChangedPropsSet.AddProperty(SPINEL_PROP_THREAD_NETWORK_TIME);
4100     mUpdateChangedPropsTask.Post();
4101 }
4102 #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
4103 
HandleActiveScanResult_Jump(otActiveScanResult * aResult,void * aContext)4104 void NcpBase::HandleActiveScanResult_Jump(otActiveScanResult *aResult, void *aContext)
4105 {
4106     static_cast<NcpBase *>(aContext)->HandleActiveScanResult(aResult);
4107 }
4108 
4109 // ----------------------------------------------------------------------------
4110 // MARK: Scan Results Glue
4111 // ----------------------------------------------------------------------------
4112 
HandleActiveScanResult(otActiveScanResult * aResult)4113 void NcpBase::HandleActiveScanResult(otActiveScanResult *aResult)
4114 {
4115     otError error = OT_ERROR_NONE;
4116 
4117     if (aResult)
4118     {
4119         uint8_t flags = static_cast<uint8_t>(aResult->mVersion << SPINEL_BEACON_THREAD_FLAG_VERSION_SHIFT);
4120 
4121         if (aResult->mIsNative)
4122         {
4123             flags |= SPINEL_BEACON_THREAD_FLAG_NATIVE;
4124         }
4125 
4126         SuccessOrExit(error = mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
4127                                                   SPINEL_CMD_PROP_VALUE_INSERTED, SPINEL_PROP_MAC_SCAN_BEACON));
4128         SuccessOrExit(error = mEncoder.WriteUint8(aResult->mChannel));
4129         SuccessOrExit(error = mEncoder.WriteInt8(aResult->mRssi));
4130 
4131         SuccessOrExit(error = mEncoder.OpenStruct()); // "mac-layer data"
4132         SuccessOrExit(error = mEncoder.WriteEui64(aResult->mExtAddress));
4133         SuccessOrExit(error = mEncoder.WriteUint16(0xffff)); // short address, not given
4134         SuccessOrExit(error = mEncoder.WriteUint16(aResult->mPanId));
4135         SuccessOrExit(error = mEncoder.WriteUint8(aResult->mLqi));
4136         SuccessOrExit(error = mEncoder.CloseStruct());
4137 
4138         SuccessOrExit(error = mEncoder.OpenStruct());                                 // "net-layer data"
4139         SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROTOCOL_TYPE_THREAD)); // type
4140         SuccessOrExit(error = mEncoder.WriteUint8(flags));
4141         SuccessOrExit(error = mEncoder.WriteUtf8(aResult->mNetworkName.m8));
4142         SuccessOrExit(error = mEncoder.WriteDataWithLen(aResult->mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE));
4143         SuccessOrExit(error = mEncoder.WriteDataWithLen(aResult->mSteeringData.m8, aResult->mSteeringData.mLength));
4144         SuccessOrExit(error = mEncoder.CloseStruct());
4145 
4146         SuccessOrExit(error = mEncoder.EndFrame());
4147     }
4148     else
4149     {
4150         // We are finished with the scan, send an unsolicited
4151         // scan state update.
4152         mChangedPropsSet.AddProperty(SPINEL_PROP_MAC_SCAN_STATE);
4153         mUpdateChangedPropsTask.Post();
4154     }
4155 
4156 exit:
4157 
4158     if (error != OT_ERROR_NONE)
4159     {
4160         // We ran out of buffer adding a scan result so remember to send
4161         // an async `LAST_STATUS(NOMEM)` when buffer space becomes
4162         // available.
4163         mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM);
4164         mUpdateChangedPropsTask.Post();
4165     }
4166 }
4167 
HandleEnergyScanResult_Jump(otEnergyScanResult * aResult,void * aContext)4168 void NcpBase::HandleEnergyScanResult_Jump(otEnergyScanResult *aResult, void *aContext)
4169 {
4170     static_cast<NcpBase *>(aContext)->HandleEnergyScanResult(aResult);
4171 }
4172 
HandleEnergyScanResult(otEnergyScanResult * aResult)4173 void NcpBase::HandleEnergyScanResult(otEnergyScanResult *aResult)
4174 {
4175     otError error = OT_ERROR_NONE;
4176 
4177     if (aResult)
4178     {
4179         SuccessOrExit(error = mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
4180                                                   SPINEL_CMD_PROP_VALUE_INSERTED, SPINEL_PROP_MAC_ENERGY_SCAN_RESULT));
4181         SuccessOrExit(error = mEncoder.WriteUint8(aResult->mChannel));
4182         SuccessOrExit(error = mEncoder.WriteInt8(aResult->mMaxRssi));
4183         SuccessOrExit(error = mEncoder.EndFrame());
4184     }
4185     else
4186     {
4187         // We are finished with the scan, send an unsolicited
4188         // scan state update.
4189         mChangedPropsSet.AddProperty(SPINEL_PROP_MAC_SCAN_STATE);
4190         mUpdateChangedPropsTask.Post();
4191     }
4192 
4193 exit:
4194 
4195     if (error != OT_ERROR_NONE)
4196     {
4197         mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM);
4198         mUpdateChangedPropsTask.Post();
4199     }
4200 }
4201 
4202 #if OPENTHREAD_CONFIG_JOINER_ENABLE
HandleJoinerCallback_Jump(otError aError,void * aContext)4203 void NcpBase::HandleJoinerCallback_Jump(otError aError, void *aContext)
4204 {
4205     static_cast<NcpBase *>(aContext)->HandleJoinerCallback(aError);
4206 }
4207 
HandleJoinerCallback(otError aError)4208 void NcpBase::HandleJoinerCallback(otError aError)
4209 {
4210     switch (aError)
4211     {
4212     case OT_ERROR_NONE:
4213         mChangedPropsSet.AddLastStatus(SPINEL_STATUS_JOIN_SUCCESS);
4214         break;
4215     case OT_ERROR_SECURITY:
4216         mChangedPropsSet.AddLastStatus(SPINEL_STATUS_JOIN_SECURITY);
4217         break;
4218     case OT_ERROR_NOT_FOUND:
4219         mChangedPropsSet.AddLastStatus(SPINEL_STATUS_JOIN_NO_PEERS);
4220         break;
4221     case OT_ERROR_RESPONSE_TIMEOUT:
4222         mChangedPropsSet.AddLastStatus(SPINEL_STATUS_JOIN_RSP_TIMEOUT);
4223         break;
4224     default:
4225         mChangedPropsSet.AddLastStatus(SPINEL_STATUS_JOIN_FAILURE);
4226         break;
4227     }
4228 
4229     mUpdateChangedPropsTask.Post();
4230 }
4231 #endif
4232 
4233 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
HandleLinkMetricsReport_Jump(const otIp6Address * aSource,const otLinkMetricsValues * aMetricsValues,otLinkMetricsStatus aStatus,void * aContext)4234 void NcpBase::HandleLinkMetricsReport_Jump(const otIp6Address        *aSource,
4235                                            const otLinkMetricsValues *aMetricsValues,
4236                                            otLinkMetricsStatus        aStatus,
4237                                            void                      *aContext)
4238 {
4239     static_cast<NcpBase *>(aContext)->HandleLinkMetricsReport(aSource, aMetricsValues, aStatus);
4240 }
4241 
HandleLinkMetricsReport(const otIp6Address * aSource,const otLinkMetricsValues * aMetricsValues,otLinkMetricsStatus aStatus)4242 void NcpBase::HandleLinkMetricsReport(const otIp6Address        *aSource,
4243                                       const otLinkMetricsValues *aMetricsValues,
4244                                       otLinkMetricsStatus        aStatus)
4245 {
4246     SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS,
4247                                       SPINEL_PROP_THREAD_LINK_METRICS_QUERY_RESULT));
4248 
4249     SuccessOrExit(mEncoder.WriteIp6Address(*aSource));
4250     SuccessOrExit(mEncoder.WriteUint8(aStatus));
4251     SuccessOrExit(EncodeLinkMetricsValues(aMetricsValues));
4252 
4253     SuccessOrExit(mEncoder.EndFrame());
4254 
4255 exit:
4256     return;
4257 }
4258 
HandleLinkMetricsMgmtResponse_Jump(const otIp6Address * aSource,otLinkMetricsStatus aStatus,void * aContext)4259 void NcpBase::HandleLinkMetricsMgmtResponse_Jump(const otIp6Address *aSource,
4260                                                  otLinkMetricsStatus aStatus,
4261                                                  void               *aContext)
4262 {
4263     static_cast<NcpBase *>(aContext)->HandleLinkMetricsMgmtResponse(aSource, aStatus);
4264 }
4265 
HandleLinkMetricsMgmtResponse(const otIp6Address * aSource,otLinkMetricsStatus aStatus)4266 void NcpBase::HandleLinkMetricsMgmtResponse(const otIp6Address *aSource, otLinkMetricsStatus aStatus)
4267 {
4268     SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS,
4269                                       SPINEL_PROP_THREAD_LINK_METRICS_MGMT_RESPONSE));
4270 
4271     SuccessOrExit(mEncoder.WriteIp6Address(*aSource));
4272     SuccessOrExit(mEncoder.WriteUint8(aStatus));
4273 
4274     SuccessOrExit(mEncoder.EndFrame());
4275 
4276 exit:
4277     return;
4278 }
4279 
HandleLinkMetricsEnhAckProbingIeReport_Jump(otShortAddress aShortAddress,const otExtAddress * aExtAddress,const otLinkMetricsValues * aMetricsValues,void * aContext)4280 void NcpBase::HandleLinkMetricsEnhAckProbingIeReport_Jump(otShortAddress             aShortAddress,
4281                                                           const otExtAddress        *aExtAddress,
4282                                                           const otLinkMetricsValues *aMetricsValues,
4283                                                           void                      *aContext)
4284 {
4285     static_cast<NcpBase *>(aContext)->HandleLinkMetricsEnhAckProbingIeReport(aShortAddress, aExtAddress,
4286                                                                              aMetricsValues);
4287 }
4288 
HandleLinkMetricsEnhAckProbingIeReport(otShortAddress aShortAddress,const otExtAddress * aExtAddress,const otLinkMetricsValues * aMetricsValues)4289 void NcpBase::HandleLinkMetricsEnhAckProbingIeReport(otShortAddress             aShortAddress,
4290                                                      const otExtAddress        *aExtAddress,
4291                                                      const otLinkMetricsValues *aMetricsValues)
4292 {
4293     SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS,
4294                                       SPINEL_PROP_THREAD_LINK_METRICS_MGMT_ENH_ACK_IE));
4295 
4296     SuccessOrExit(mEncoder.WriteUint16(aShortAddress));
4297     SuccessOrExit(mEncoder.WriteEui64(*aExtAddress));
4298     SuccessOrExit(EncodeLinkMetricsValues(aMetricsValues));
4299 
4300     SuccessOrExit(mEncoder.EndFrame());
4301 
4302 exit:
4303     return;
4304 }
4305 #endif
4306 
4307 // ----------------------------------------------------------------------------
4308 // MARK: Outbound Datagram Handling
4309 // ----------------------------------------------------------------------------
4310 
HandleDatagramFromStack(otMessage * aMessage,void * aContext)4311 void NcpBase::HandleDatagramFromStack(otMessage *aMessage, void *aContext)
4312 {
4313     static_cast<NcpBase *>(aContext)->HandleDatagramFromStack(aMessage);
4314 }
4315 
HandleDatagramFromStack(otMessage * aMessage)4316 void NcpBase::HandleDatagramFromStack(otMessage *aMessage)
4317 {
4318     VerifyOrExit(aMessage != nullptr);
4319 
4320     // Do not forward frames larger than SPINEL payload size.
4321     VerifyOrExit(otMessageGetLength(aMessage) <= SPINEL_FRAME_MAX_COMMAND_PAYLOAD_SIZE, otMessageFree(aMessage));
4322 
4323     otMessageQueueEnqueue(&mMessageQueue, aMessage);
4324 
4325     // If there is no queued spinel command response, try to write/send
4326     // the datagram message immediately. If there is a queued response
4327     // or if currently out of buffer space, the IPv6 datagram message
4328     // will be sent from `HandleFrameRemovedFromNcpBuffer()` when buffer
4329     //  space becomes available and after any pending spinel command
4330     // response.
4331 
4332     if (IsResponseQueueEmpty())
4333     {
4334         IgnoreError(SendQueuedDatagramMessages());
4335     }
4336 
4337 exit:
4338     return;
4339 }
4340 
SendDatagramMessage(otMessage * aMessage)4341 otError NcpBase::SendDatagramMessage(otMessage *aMessage)
4342 {
4343     otError           error    = OT_ERROR_NONE;
4344     uint8_t           header   = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
4345     bool              isSecure = otMessageIsLinkSecurityEnabled(aMessage);
4346     spinel_prop_key_t propKey  = isSecure ? SPINEL_PROP_STREAM_NET : SPINEL_PROP_STREAM_NET_INSECURE;
4347 
4348     SuccessOrExit(error = mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, propKey));
4349     SuccessOrExit(error = mEncoder.WriteUint16(otMessageGetLength(aMessage)));
4350     SuccessOrExit(error = mEncoder.WriteMessage(aMessage));
4351 
4352     // Append any metadata (rssi, lqi, channel, etc) here!
4353 
4354     SuccessOrExit(error = mEncoder.EndFrame());
4355 
4356     if (isSecure)
4357     {
4358         mOutboundSecureIpFrameCounter++;
4359     }
4360     else
4361     {
4362         mOutboundInsecureIpFrameCounter++;
4363     }
4364 
4365 exit:
4366     return error;
4367 }
4368 
SendQueuedDatagramMessages(void)4369 otError NcpBase::SendQueuedDatagramMessages(void)
4370 {
4371     otError    error = OT_ERROR_NONE;
4372     otMessage *message;
4373 
4374     while ((message = otMessageQueueGetHead(&mMessageQueue)) != nullptr)
4375     {
4376         // Since an `otMessage` instance can be in one queue at a time,
4377         // it is first dequeued from `mMessageQueue` before attempting
4378         // to include it in a spinel frame by calling `SendDatagramMessage()`
4379         // If forming of the spinel frame fails, the message is enqueued
4380         // back at the front of `mMessageQueue`.
4381 
4382         otMessageQueueDequeue(&mMessageQueue, message);
4383 
4384         error = SendDatagramMessage(message);
4385 
4386         if (error != OT_ERROR_NONE)
4387         {
4388             otMessageQueueEnqueueAtHead(&mMessageQueue, message);
4389         }
4390 
4391         SuccessOrExit(error);
4392     }
4393 
4394 exit:
4395     return error;
4396 }
4397 
4398 #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE
HandlePropertySet(void)4399 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_UDP_FORWARD_STREAM>(void)
4400 {
4401     const uint8_t      *framePtr = nullptr;
4402     uint16_t            frameLen = 0;
4403     const otIp6Address *peerAddr;
4404     uint16_t            peerPort;
4405     uint16_t            sockPort;
4406     otMessage          *message;
4407     otError             error       = OT_ERROR_NONE;
4408     otMessageSettings   msgSettings = {false, OT_MESSAGE_PRIORITY_NORMAL};
4409 
4410     message = otIp6NewMessage(mInstance, &msgSettings);
4411     VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
4412 
4413     SuccessOrExit(error = mDecoder.ReadDataWithLen(framePtr, frameLen));
4414     SuccessOrExit(error = mDecoder.ReadUint16(peerPort));
4415     SuccessOrExit(error = mDecoder.ReadIp6Address(peerAddr));
4416     SuccessOrExit(error = mDecoder.ReadUint16(sockPort));
4417 
4418     SuccessOrExit(error = otMessageAppend(message, framePtr, static_cast<uint16_t>(frameLen)));
4419 
4420     otUdpForwardReceive(mInstance, message, peerPort, peerAddr, sockPort);
4421 
4422     // `otUdpForwardReceive()` takes ownership of `message` (in both success
4423     // or failure cases). `message` is set to nullptr so it is not freed at
4424     // exit.
4425     message = nullptr;
4426 
4427 exit:
4428     if (message != nullptr)
4429     {
4430         otMessageFree(message);
4431     }
4432 
4433     return error;
4434 }
4435 
HandleUdpForwardStream(otMessage * aMessage,uint16_t aPeerPort,otIp6Address * aPeerAddr,uint16_t aSockPort,void * aContext)4436 void NcpBase::HandleUdpForwardStream(otMessage    *aMessage,
4437                                      uint16_t      aPeerPort,
4438                                      otIp6Address *aPeerAddr,
4439                                      uint16_t      aSockPort,
4440                                      void         *aContext)
4441 {
4442     static_cast<NcpBase *>(aContext)->HandleUdpForwardStream(aMessage, aPeerPort, *aPeerAddr, aSockPort);
4443 }
4444 
HandleUdpForwardStream(otMessage * aMessage,uint16_t aPeerPort,otIp6Address & aPeerAddr,uint16_t aPort)4445 void NcpBase::HandleUdpForwardStream(otMessage *aMessage, uint16_t aPeerPort, otIp6Address &aPeerAddr, uint16_t aPort)
4446 {
4447     uint16_t length = otMessageGetLength(aMessage);
4448     uint8_t  header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
4449 
4450     SuccessOrExit(mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_THREAD_UDP_FORWARD_STREAM));
4451     SuccessOrExit(mEncoder.WriteUint16(length));
4452     SuccessOrExit(mEncoder.WriteMessage(aMessage));
4453 
4454     SuccessOrExit(mEncoder.WriteUint16(aPeerPort));
4455     SuccessOrExit(mEncoder.WriteIp6Address(aPeerAddr));
4456     SuccessOrExit(mEncoder.WriteUint16(aPort));
4457     SuccessOrExit(mEncoder.EndFrame());
4458 
4459     // The `aMessage` is owned by the outbound frame and NCP buffer
4460     // after frame was finished/ended successfully. It will be freed
4461     // when the frame is successfully sent and removed.
4462 
4463     aMessage = nullptr;
4464 
4465 exit:
4466 
4467     if (aMessage != nullptr)
4468     {
4469         otMessageFree(aMessage);
4470     }
4471 }
4472 #endif // OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE
4473 
4474 // ----------------------------------------------------------------------------
4475 // MARK: Pcap frame handling
4476 // ----------------------------------------------------------------------------
4477 
HandlePcapFrame(const otRadioFrame * aFrame,bool aIsTx,void * aContext)4478 void NcpBase::HandlePcapFrame(const otRadioFrame *aFrame, bool aIsTx, void *aContext)
4479 {
4480     static_cast<NcpBase *>(aContext)->HandlePcapFrame(aFrame, aIsTx);
4481 }
4482 
HandlePcapFrame(const otRadioFrame * aFrame,bool aIsTx)4483 void NcpBase::HandlePcapFrame(const otRadioFrame *aFrame, bool aIsTx)
4484 {
4485     uint16_t flags  = 0;
4486     uint8_t  header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
4487 
4488     VerifyOrExit(mPcapEnabled);
4489 
4490     if (aIsTx)
4491     {
4492         flags |= SPINEL_MD_FLAG_TX;
4493     }
4494 
4495     SuccessOrExit(mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_STREAM_RAW));
4496     SuccessOrExit(mEncoder.WriteUint16(aFrame->mLength));
4497 
4498     SuccessOrExit(mEncoder.WriteData(aFrame->mPsdu, aFrame->mLength));
4499 
4500     // Append metadata (rssi, etc)
4501     SuccessOrExit(mEncoder.WriteInt8(aFrame->mInfo.mRxInfo.mRssi)); // RSSI
4502     SuccessOrExit(mEncoder.WriteInt8(-128));                        // Noise floor (Currently unused)
4503     SuccessOrExit(mEncoder.WriteUint16(flags));                     // Flags
4504 
4505     SuccessOrExit(mEncoder.OpenStruct()); // PHY-data
4506     // Empty for now
4507     SuccessOrExit(mEncoder.CloseStruct());
4508 
4509     SuccessOrExit(mEncoder.OpenStruct()); // Vendor-data
4510     // Empty for now
4511     SuccessOrExit(mEncoder.CloseStruct());
4512 
4513     SuccessOrExit(mEncoder.EndFrame());
4514 
4515 exit:
4516     return;
4517 }
4518 
HandlePropertyGet(void)4519 template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_PHY_PCAP_ENABLED>(void)
4520 {
4521     return mEncoder.WriteBool(mPcapEnabled);
4522 }
4523 
HandlePropertySet(void)4524 template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_PCAP_ENABLED>(void)
4525 {
4526     otError error = OT_ERROR_NONE;
4527     bool    enabled;
4528 
4529     SuccessOrExit(error = mDecoder.ReadBool(enabled));
4530     VerifyOrExit(enabled != mPcapEnabled);
4531 
4532     mPcapEnabled = enabled;
4533 
4534     if (mPcapEnabled)
4535     {
4536         otLinkSetPcapCallback(mInstance, &NcpBase::HandlePcapFrame, static_cast<void *>(this));
4537     }
4538     else
4539     {
4540         otLinkSetPcapCallback(mInstance, nullptr, nullptr);
4541     }
4542 
4543 exit:
4544     return error;
4545 }
4546 
4547 // ----------------------------------------------------------------------------
4548 // MARK: Property/Status Changed
4549 // ----------------------------------------------------------------------------
4550 
HandleStateChanged(otChangedFlags aFlags,void * aContext)4551 void NcpBase::HandleStateChanged(otChangedFlags aFlags, void *aContext)
4552 {
4553     NcpBase *ncp = static_cast<NcpBase *>(aContext);
4554 
4555     ncp->mThreadChangedFlags |= aFlags;
4556     ncp->mUpdateChangedPropsTask.Post();
4557 }
4558 
ProcessThreadChangedFlags(void)4559 void NcpBase::ProcessThreadChangedFlags(void)
4560 {
4561     static const struct
4562     {
4563         otChangedFlags    mThreadFlag;
4564         spinel_prop_key_t mPropKey;
4565     } kFlags[] = {
4566         {OT_CHANGED_IP6_ADDRESS_ADDED, SPINEL_PROP_IPV6_ADDRESS_TABLE},
4567         {OT_CHANGED_IP6_ADDRESS_REMOVED, SPINEL_PROP_IPV6_ADDRESS_TABLE},
4568         {OT_CHANGED_THREAD_ROLE, SPINEL_PROP_NET_ROLE},
4569         {OT_CHANGED_THREAD_LL_ADDR, SPINEL_PROP_IPV6_LL_ADDR},
4570         {OT_CHANGED_THREAD_ML_ADDR, SPINEL_PROP_IPV6_ML_ADDR},
4571         {OT_CHANGED_THREAD_PARTITION_ID, SPINEL_PROP_NET_PARTITION_ID},
4572         {OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER, SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER},
4573         {OT_CHANGED_THREAD_NETDATA, SPINEL_PROP_THREAD_LEADER_NETWORK_DATA},
4574         {OT_CHANGED_THREAD_CHILD_ADDED, SPINEL_PROP_THREAD_CHILD_TABLE},
4575         {OT_CHANGED_THREAD_CHILD_REMOVED, SPINEL_PROP_THREAD_CHILD_TABLE},
4576         {OT_CHANGED_IP6_MULTICAST_SUBSCRIBED, SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE},
4577         {OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED, SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE},
4578         {OT_CHANGED_THREAD_CHANNEL, SPINEL_PROP_PHY_CHAN},
4579         {OT_CHANGED_THREAD_PANID, SPINEL_PROP_MAC_15_4_PANID},
4580         {OT_CHANGED_THREAD_NETWORK_NAME, SPINEL_PROP_NET_NETWORK_NAME},
4581         {OT_CHANGED_THREAD_EXT_PANID, SPINEL_PROP_NET_XPANID},
4582         {OT_CHANGED_THREAD_RLOC_ADDED, SPINEL_PROP_IPV6_ADDRESS_TABLE},
4583         {OT_CHANGED_THREAD_RLOC_REMOVED, SPINEL_PROP_IPV6_ADDRESS_TABLE},
4584         {OT_CHANGED_NETWORK_KEY, SPINEL_PROP_NET_NETWORK_KEY},
4585         {OT_CHANGED_PSKC, SPINEL_PROP_NET_PSKC},
4586         {OT_CHANGED_CHANNEL_MANAGER_NEW_CHANNEL, SPINEL_PROP_CHANNEL_MANAGER_NEW_CHANNEL},
4587         {OT_CHANGED_SUPPORTED_CHANNEL_MASK, SPINEL_PROP_PHY_CHAN_SUPPORTED},
4588     };
4589 
4590     VerifyOrExit(mThreadChangedFlags != 0);
4591 
4592     // If thread role has changed, check for possible "join" error.
4593 
4594     if ((mThreadChangedFlags & OT_CHANGED_THREAD_ROLE) != 0)
4595     {
4596         if (mRequireJoinExistingNetwork)
4597         {
4598             switch (otThreadGetDeviceRole(mInstance))
4599             {
4600             case OT_DEVICE_ROLE_DETACHED:
4601             case OT_DEVICE_ROLE_DISABLED:
4602                 break;
4603 
4604             default:
4605                 mRequireJoinExistingNetwork = false;
4606                 mChangedPropsSet.AddProperty(SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING);
4607                 break;
4608             }
4609 
4610             if ((otThreadGetDeviceRole(mInstance) == OT_DEVICE_ROLE_LEADER) && otThreadIsSingleton(mInstance))
4611             {
4612                 mThreadChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_PARTITION_ID);
4613                 IgnoreError(otThreadSetEnabled(mInstance, false));
4614 
4615                 mChangedPropsSet.AddProperty(SPINEL_PROP_NET_STACK_UP);
4616                 mChangedPropsSet.AddLastStatus(SPINEL_STATUS_JOIN_FAILURE);
4617             }
4618         }
4619     }
4620 
4621     // Convert OT_CHANGED flags to corresponding NCP property update.
4622 
4623     for (auto &flag : kFlags)
4624     {
4625         uint32_t threadFlag = flag.mThreadFlag;
4626 
4627         if (mThreadChangedFlags & threadFlag)
4628         {
4629             spinel_prop_key_t propKey           = flag.mPropKey;
4630             bool              shouldAddProperty = true;
4631 
4632             // Child table changes are reported using the `HandleChildAdded()` and
4633             // `HandleChildRemoved()` callbacks emitting spinel `VALUE_INSERTED` and
4634             // `VALUE_REMOVED` async spinel frames. If the spinel frames could not be
4635             // added (e.g., out of NCP buffer) from the above callbacks, the flag
4636             // `mShouldEmitChildTableUpdate` is set to `true` so that the entire
4637             // child table is emitted as an unsolicited `VALUE_IS` update.
4638 
4639             if (propKey == SPINEL_PROP_THREAD_CHILD_TABLE)
4640             {
4641                 shouldAddProperty           = mShouldEmitChildTableUpdate;
4642                 mShouldEmitChildTableUpdate = false;
4643             }
4644 
4645             if (shouldAddProperty)
4646             {
4647                 mChangedPropsSet.AddProperty(propKey);
4648             }
4649 
4650             if (threadFlag == OT_CHANGED_THREAD_NETDATA)
4651             {
4652                 mChangedPropsSet.AddProperty(SPINEL_PROP_THREAD_ON_MESH_NETS);
4653                 mChangedPropsSet.AddProperty(SPINEL_PROP_THREAD_OFF_MESH_ROUTES);
4654             }
4655 
4656             mThreadChangedFlags &= ~threadFlag;
4657             VerifyOrExit(mThreadChangedFlags != 0);
4658         }
4659     }
4660 
4661     // Clear any remaining ThreadFlag that has no matching
4662     // NCP property update (e.g., OT_CHANGED_SECURITY_POLICY)
4663 
4664     mThreadChangedFlags = 0;
4665 
4666 exit:
4667     return;
4668 }
4669 
4670 } // namespace Ncp
4671 } // namespace ot
4672 
4673 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
4674