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