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