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