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