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