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