1 /*
2  *    Copyright (c) 2016, 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 contains definitions a spinel interface to the OpenThread stack.
31  */
32 
33 #ifndef NCP_BASE_HPP_
34 #define NCP_BASE_HPP_
35 
36 #include "openthread-core-config.h"
37 
38 #include "ncp/ncp_config.h"
39 
40 #if OPENTHREAD_MTD || OPENTHREAD_FTD
41 #include <openthread/ip6.h>
42 #else
43 #include <openthread/platform/radio.h>
44 #endif
45 #if OPENTHREAD_FTD
46 #include <openthread/thread_ftd.h>
47 #endif
48 #include <openthread/message.h>
49 #include <openthread/ncp.h>
50 #if OPENTHREAD_CONFIG_MULTI_RADIO
51 #include <openthread/multi_radio.h>
52 #endif
53 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
54 #include <openthread/srp_client.h>
55 #endif
56 
57 #include "changed_props_set.hpp"
58 #include "common/tasklet.hpp"
59 #include "instance/instance.hpp"
60 #include "lib/spinel/spinel.h"
61 #include "lib/spinel/spinel_buffer.hpp"
62 #include "lib/spinel/spinel_decoder.hpp"
63 #include "lib/spinel/spinel_encoder.hpp"
64 
65 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
66 #define SPINEL_HEADER_IID_BROADCAST OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID
67 #else
68 #define SPINEL_HEADER_IID_BROADCAST SPINEL_HEADER_IID_0
69 #endif
70 
71 // In case of host<->ncp<->rcp configuration, notifications shall be
72 // received on broadcast iid on ncp, but transmitted on IID 0 to host.
73 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
74 #define SPINEL_HEADER_TX_NOTIFICATION_IID SPINEL_HEADER_IID_BROADCAST
75 #else
76 #define SPINEL_HEADER_TX_NOTIFICATION_IID SPINEL_HEADER_IID_0
77 #endif
78 
79 namespace ot {
80 namespace Ncp {
81 
82 class NcpBase
83 {
84 public:
85     enum
86     {
87         kSpinelCmdHeaderSize = 2, ///< Size of spinel command header (in bytes).
88         kSpinelPropIdSize    = 3, ///< Size of spinel property identifier (in bytes).
89 #if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && OPENTHREAD_RADIO
90         kSpinelInterfaceCount = SPINEL_HEADER_IID_MAX + 1, // Number of supported spinel interfaces
91 #else
92         kSpinelInterfaceCount = 1, // Only one interface supported in single instance configuration
93 #endif
94     };
95 
96     /**
97      * Creates and initializes an NcpBase instance.
98      *
99      * @param[in]  aInstance  The OpenThread instance structure.
100      *
101      */
102     explicit NcpBase(Instance *aInstance);
103 
104 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
105     /**
106      * Creates and initializes an NcpBase instance.
107      *
108      * @param[in]  aInstances  The OpenThread instances structure pointer array.
109      * @param[in]  aCount      Number of the instances in the array.
110      *
111      */
112     explicit NcpBase(Instance **aInstances, uint8_t aCount);
113 
114 #endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO
115 
116     /**
117      * Returns the pointer to the single NCP instance.
118      *
119      * @returns Pointer to the single NCP instance.
120      *
121      */
122     static NcpBase *GetNcpInstance(void);
123 
124     /**
125      * Returns an IID for the given instance
126      *
127      * Returned IID is an integer value that must be shifted by SPINEL_HEADER_IID_SHIFT before putting into spinel
128      * header. If multipan interface is not enabled or build is not for RCP IID=0 is returned. If nullptr is passed it
129      * matches broadcast IID in current implementation. Broadcast IID is also returned in case no match was found.
130      *
131      * @param[in] aInstance  Instance pointer to match with IID
132      *
133      * @returns Spinel Interface Identifier to use for communication for this instance
134      *
135      */
136     uint8_t InstanceToIid(Instance *aInstance);
137 
138     /**
139      * Returns an OT instance for the given IID
140      *
141      * Returns an OpenThread instance object associated to the given IID.
142      * If multipan interface is not enabled or build is not for RCP returned value is the same instance object
143      * regardless of the aIid parameter In current implementation nullptr is returned for broadcast IID and values
144      * exceeding the instances count but lower than kSpinelInterfaceCount.
145      *
146      * @param[in] aIid  IID used in the Spinel communication
147      *
148      * @returns OpenThread instance object associated with the given IID
149      *
150      */
151     Instance *IidToInstance(uint8_t aIid);
152 
153 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
154     /**
155      * Called to send notification to host about switchower results.
156      */
157     void NotifySwitchoverDone(otInstance *aInstance, bool aSuccess);
158 #endif
159 
160     /**
161      * This method returns the IID of the current spinel command.
162      *
163      * @returns IID.
164      *
165      */
166     spinel_iid_t GetCurCommandIid(void) const;
167 
168     /**
169      * Sends data to host via specific stream.
170      *
171      *
172      * @param[in]  aStreamId  A numeric identifier for the stream to write to.
173      *                        If set to '0', will default to the debug stream.
174      * @param[in]  aDataPtr   A pointer to the data to send on the stream.
175      *                        If aDataLen is non-zero, this param MUST NOT be nullptr.
176      * @param[in]  aDataLen   The number of bytes of data from aDataPtr to send.
177      *
178      * @retval OT_ERROR_NONE         The data was queued for delivery to the host.
179      * @retval OT_ERROR_BUSY         There are not enough resources to complete this
180      *                               request. This is usually a temporary condition.
181      * @retval OT_ERROR_INVALID_ARGS The given aStreamId was invalid.
182      *
183      */
184     otError StreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen);
185 
186     /**
187      * Send an OpenThread log message to host via `SPINEL_PROP_STREAM_LOG` property.
188      *
189      * @param[in] aLogLevel   The log level
190      * @param[in] aLogRegion  The log region
191      * @param[in] aLogString  The log string
192      *
193      */
194     void Log(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogString);
195 
196 #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
197     /**
198      * Registers peek/poke delegate functions with NCP module.
199      *
200      * @param[in] aAllowPeekDelegate      Delegate function pointer for peek operation.
201      * @param[in] aAllowPokeDelegate      Delegate function pointer for poke operation.
202      *
203      */
204     void RegisterPeekPokeDelegates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate,
205                                    otNcpDelegateAllowPeekPoke aAllowPokeDelegate);
206 #endif
207 
208     /**
209      * Is called by the framer whenever a framing error is detected.
210      */
211     void IncrementFrameErrorCounter(void);
212 
213     /**
214      * Called by the subclass to indicate when a frame has been received.
215      */
216     void HandleReceive(const uint8_t *aBuf, uint16_t aBufLength);
217 
218     /**
219      * Called by the subclass to learn when the host wake operation must be issued.
220      */
221     bool ShouldWakeHost(void);
222 
223     /**
224      * Called by the subclass to learn when the transfer to the host should be deferred.
225      */
226     bool ShouldDeferHostSend(void);
227 
228 protected:
229     static constexpr uint8_t kBitsPerByte = 8; ///< Number of bits in a byte.
230 
231     typedef otError (NcpBase::*PropertyHandler)(void);
232 
233     /**
234      * Represents the `ResponseEntry` type.
235      *
236      */
237     enum ResponseType
238     {
239         kResponseTypeGet = 0,    ///< Response entry is for a `VALUE_GET` command.
240         kResponseTypeSet,        ///< Response entry is for a `VALUE_SET` command.
241         kResponseTypeLastStatus, ///< Response entry is a `VALUE_IS(LAST_STATUS)`.
242     };
243 
244     /**
245      * Represents a spinel response entry.
246      *
247      */
248     struct ResponseEntry
249     {
250         uint8_t      mIid : 2;              ///< Spinel interface id.
251         uint8_t      mTid : 4;              ///< Spinel transaction id.
252         bool         mIsInUse : 1;          ///< `true` if this entry is in use, `false` otherwise.
253         ResponseType mType : 2;             ///< Response type.
254         uint32_t     mPropKeyOrStatus : 24; ///< 3 bytes for either property key or spinel status.
255     };
256 
257     struct HandlerEntry
258     {
259         spinel_prop_key_t        mKey;
260         NcpBase::PropertyHandler mHandler;
261     };
262 
263     Spinel::Buffer::FrameTag GetLastOutboundFrameTag(void);
264 
265     otError HandleCommand(uint8_t aHeader);
266 
267 #if __cplusplus >= 201103L
268     static constexpr bool AreHandlerEntriesSorted(const HandlerEntry *aHandlerEntries, size_t aSize);
269 #endif
270 
271     static PropertyHandler FindPropertyHandler(const HandlerEntry *aHandlerEntries,
272                                                size_t              aSize,
273                                                spinel_prop_key_t   aKey);
274     static PropertyHandler FindGetPropertyHandler(spinel_prop_key_t aKey);
275     static PropertyHandler FindSetPropertyHandler(spinel_prop_key_t aKey);
276     static PropertyHandler FindInsertPropertyHandler(spinel_prop_key_t aKey);
277     static PropertyHandler FindRemovePropertyHandler(spinel_prop_key_t aKey);
278 
279     bool    HandlePropertySetForSpecialProperties(uint8_t aHeader, spinel_prop_key_t aKey, otError &aError);
280     otError HandleCommandPropertySet(uint8_t aHeader, spinel_prop_key_t aKey);
281     otError HandleCommandPropertyInsertRemove(uint8_t aHeader, spinel_prop_key_t aKey, unsigned int aCommand);
282 
283     otError WriteLastStatusFrame(uint8_t aHeader, spinel_status_t aLastStatus);
284     otError WritePropertyValueIsFrame(uint8_t aHeader, spinel_prop_key_t aPropKey, bool aIsGetResponse = true);
285     otError WritePropertyValueInsertedRemovedFrame(uint8_t           aHeader,
286                                                    unsigned int      aResponseCommand,
287                                                    spinel_prop_key_t aPropKey,
288                                                    const uint8_t    *aValuePtr,
289                                                    uint16_t          aValueLen);
290 
291     otError SendQueuedResponses(void);
IsResponseQueueEmpty(void) const292     bool    IsResponseQueueEmpty(void) const { return (mResponseQueueHead == mResponseQueueTail); }
293     otError EnqueueResponse(uint8_t aHeader, ResponseType aType, unsigned int aPropKeyOrStatus);
294 
PrepareGetResponse(uint8_t aHeader,spinel_prop_key_t aPropKey)295     otError PrepareGetResponse(uint8_t aHeader, spinel_prop_key_t aPropKey)
296     {
297         return EnqueueResponse(aHeader, kResponseTypeGet, aPropKey);
298     }
PrepareSetResponse(uint8_t aHeader,spinel_prop_key_t aPropKey)299     otError PrepareSetResponse(uint8_t aHeader, spinel_prop_key_t aPropKey)
300     {
301         return EnqueueResponse(aHeader, kResponseTypeSet, aPropKey);
302     }
PrepareLastStatusResponse(uint8_t aHeader,spinel_status_t aStatus)303     otError PrepareLastStatusResponse(uint8_t aHeader, spinel_status_t aStatus)
304     {
305         return EnqueueResponse(aHeader, kResponseTypeLastStatus, aStatus);
306     }
307 
308     static uint8_t GetWrappedResponseQueueIndex(uint8_t aPosition);
309 
310     static void UpdateChangedProps(Tasklet &aTasklet);
311     void        UpdateChangedProps(void);
312 
313     static void HandleFrameRemovedFromNcpBuffer(void                    *aContext,
314                                                 Spinel::Buffer::FrameTag aFrameTag,
315                                                 Spinel::Buffer::Priority aPriority,
316                                                 Spinel::Buffer          *aNcpBuffer);
317     void        HandleFrameRemovedFromNcpBuffer(Spinel::Buffer::FrameTag aFrameTag);
318 
319     otError EncodeChannelMask(uint32_t aChannelMask);
320     otError DecodeChannelMask(uint32_t &aChannelMask);
321 
322 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
323     otError PackRadioFrame(otRadioFrame *aFrame, otError aError);
324 
325 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
326     void NotifySwitchoverDone(bool aSuccess);
327 #endif
328 
329     static void LinkRawReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError);
330     void        LinkRawReceiveDone(uint8_t aIid, otRadioFrame *aFrame, otError aError);
331 
332     static void LinkRawTransmitDone(otInstance   *aInstance,
333                                     otRadioFrame *aFrame,
334                                     otRadioFrame *aAckFrame,
335                                     otError       aError);
336     void        LinkRawTransmitDone(uint8_t aIid, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError);
337 
338     static void LinkRawEnergyScanDone(otInstance *aInstance, int8_t aEnergyScanMaxRssi);
339     void        LinkRawEnergyScanDone(uint8_t aIid, int8_t aEnergyScanMaxRssi);
340 
GetNcpBaseIid(otInstance * aInstance)341     static inline uint8_t GetNcpBaseIid(otInstance *aInstance)
342     {
343         return sNcpInstance->InstanceToIid(static_cast<Instance *>(aInstance));
344     }
345 
346 #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
347 
348 #if OPENTHREAD_MTD || OPENTHREAD_FTD
349     static void HandleStateChanged(otChangedFlags aFlags, void *aContext);
350     void        ProcessThreadChangedFlags(void);
351 
352     static void HandlePcapFrame(const otRadioFrame *aFrame, bool aIsTx, void *aContext);
353     void        HandlePcapFrame(const otRadioFrame *aFrame, bool aIsTx);
354 
355     static void HandleTimeSyncUpdate(void *aContext);
356     void        HandleTimeSyncUpdate(void);
357 
358 #if OPENTHREAD_FTD
359     static void HandleNeighborTableChanged(otNeighborTableEvent aEvent, const otNeighborTableEntryInfo *aEntry);
360     void        HandleNeighborTableChanged(otNeighborTableEvent aEvent, const otNeighborTableEntryInfo &aEntry);
361 
362 #if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE
363     static void HandleParentResponseInfo(otThreadParentResponseInfo *aInfo, void *aContext);
364     void        HandleParentResponseInfo(const otThreadParentResponseInfo &aInfo);
365 #endif
366 #endif
367 
368     static void HandleDatagramFromStack(otMessage *aMessage, void *aContext);
369     void        HandleDatagramFromStack(otMessage *aMessage);
370 
371     otError SendQueuedDatagramMessages(void);
372     otError SendDatagramMessage(otMessage *aMessage);
373 
374     static void HandleActiveScanResult_Jump(otActiveScanResult *aResult, void *aContext);
375     void        HandleActiveScanResult(otActiveScanResult *aResult);
376 
377     static void HandleEnergyScanResult_Jump(otEnergyScanResult *aResult, void *aContext);
378     void        HandleEnergyScanResult(otEnergyScanResult *aResult);
379 
380     static void HandleJamStateChange_Jump(bool aJamState, void *aContext);
381     void        HandleJamStateChange(bool aJamState);
382 
383 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
384     static void HandleCommissionerEnergyReport_Jump(uint32_t       aChannelMask,
385                                                     const uint8_t *aEnergyData,
386                                                     uint8_t        aLength,
387                                                     void          *aContext);
388     void        HandleCommissionerEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyData, uint8_t aLength);
389 
390     static void HandleCommissionerPanIdConflict_Jump(uint16_t aPanId, uint32_t aChannelMask, void *aContext);
391     void        HandleCommissionerPanIdConflict(uint16_t aPanId, uint32_t aChannelMask);
392 #endif
393 
394 #if OPENTHREAD_CONFIG_JOINER_ENABLE
395     static void HandleJoinerCallback_Jump(otError aError, void *aContext);
396     void        HandleJoinerCallback(otError aError);
397 #endif
398 
399 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
400     static void HandleLinkMetricsReport_Jump(const otIp6Address        *aSource,
401                                              const otLinkMetricsValues *aMetricsValues,
402                                              otLinkMetricsStatus        aStatus,
403                                              void                      *aContext);
404 
405     void HandleLinkMetricsReport(const otIp6Address        *aSource,
406                                  const otLinkMetricsValues *aMetricsValues,
407                                  otLinkMetricsStatus        aStatus);
408 
409     static void HandleLinkMetricsMgmtResponse_Jump(const otIp6Address *aSource,
410                                                    otLinkMetricsStatus aStatus,
411                                                    void               *aContext);
412 
413     void HandleLinkMetricsMgmtResponse(const otIp6Address *aSource, otLinkMetricsStatus aStatus);
414 
415     static void HandleLinkMetricsEnhAckProbingIeReport_Jump(otShortAddress             aShortAddress,
416                                                             const otExtAddress        *aExtAddress,
417                                                             const otLinkMetricsValues *aMetricsValues,
418                                                             void                      *aContext);
419 
420     void HandleLinkMetricsEnhAckProbingIeReport(otShortAddress             aShortAddress,
421                                                 const otExtAddress        *aExtAddress,
422                                                 const otLinkMetricsValues *aMetricsValues);
423 #endif
424 
425     static void HandleMlrRegResult_Jump(void               *aContext,
426                                         otError             aError,
427                                         uint8_t             aMlrStatus,
428                                         const otIp6Address *aFailedAddresses,
429                                         uint8_t             aFailedAddressNum);
430     void        HandleMlrRegResult(otError             aError,
431                                    uint8_t             aMlrStatus,
432                                    const otIp6Address *aFailedAddresses,
433                                    uint8_t             aFailedAddressNum);
434 
435     otError EncodeOperationalDataset(const otOperationalDataset &aDataset);
436 
437     otError DecodeOperationalDataset(otOperationalDataset &aDataset,
438                                      const uint8_t       **aTlvs             = nullptr,
439                                      uint8_t              *aTlvsLength       = nullptr,
440                                      const otIp6Address  **aDestIpAddress    = nullptr,
441                                      bool                  aAllowEmptyValues = false);
442 
443     otError EncodeNeighborInfo(const otNeighborInfo &aNeighborInfo);
444 #if OPENTHREAD_CONFIG_MULTI_RADIO
445     otError EncodeNeighborMultiRadioInfo(uint32_t aSpinelRadioLink, const otRadioLinkInfo &aInfo);
446 #endif
447 
448 #if OPENTHREAD_FTD
449     otError EncodeChildInfo(const otChildInfo &aChildInfo);
450 #endif
451 
452 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
453     otError EncodeLinkMetricsValues(const otLinkMetricsValues *aMetricsValues);
454 #endif
455 
456 #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE
457     static void HandleUdpForwardStream(otMessage    *aMessage,
458                                        uint16_t      aPeerPort,
459                                        otIp6Address *aPeerAddr,
460                                        uint16_t      aSockPort,
461                                        void         *aContext);
462     void HandleUdpForwardStream(otMessage *aMessage, uint16_t aPeerPort, otIp6Address &aPeerAddr, uint16_t aPort);
463 #endif // OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE
464 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
465 
466 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
467     otError DecodeLinkMetrics(otLinkMetrics *aMetrics, bool aAllowPduCount);
468 #endif
469 
470     otError CommandHandler_NOOP(uint8_t aHeader);
471     otError CommandHandler_RESET(uint8_t aHeader);
472     // Combined command handler for `VALUE_GET`, `VALUE_SET`, `VALUE_INSERT` and `VALUE_REMOVE`.
473     otError CommandHandler_PROP_VALUE_update(uint8_t aHeader, unsigned int aCommand);
474 #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
475     otError CommandHandler_PEEK(uint8_t aHeader);
476     otError CommandHandler_POKE(uint8_t aHeader);
477 #endif
478 #if OPENTHREAD_MTD || OPENTHREAD_FTD
479     otError CommandHandler_NET_CLEAR(uint8_t aHeader);
480 #endif
481 
482     // ----------------------------------------------------------------------------
483     // Property Handlers
484     // ----------------------------------------------------------------------------
485     //
486     // There are 4 types of property handlers for "get", "set", "insert", and
487     // "remove" commands.
488     //
489     // "Get" handlers should get/retrieve the property value and then encode and
490     // write the value into the NCP buffer. If the "get" operation itself fails,
491     // "get" handler should write a `LAST_STATUS` with the error status into the NCP
492     // buffer. The `otError` returned from a "get" handler is the error of writing
493     // into the NCP buffer (e.g., running out buffer), and not of the "get" operation
494     // itself.
495     //
496     // "Set/Insert/Remove" handlers should first decode/parse the value from the
497     // input Spinel frame and then perform the corresponding set/insert/remove
498     // operation. They are not responsible for preparing the Spinel response and
499     // therefore should not write anything to the NCP buffer. The `otError` returned
500     // from a "set/insert/remove" handler indicates the error in either parsing of
501     // the input or the error of set/insert/remove operation.
502     //
503     // The corresponding command handler (e.g., `HandleCommandPropertySet()` for
504     // `VALUE_SET` command) will take care of preparing the Spinel response after
505     // invoking the "set/insert/remove" handler for a given property. For example,
506     // for a `VALUE_SET` command, if the "set" handler returns an error, then a
507     // `LAST_STATUS` update response is prepared, otherwise on success the "get"
508     // handler for the property is used to prepare a `VALUE_IS` Spinel response (in
509     // cases where there is no "get" handler for the property, the input value is
510     // echoed in the response).
511     //
512     // Few properties require special treatment where the response needs to be
513     // prepared directly in the  "set"  handler (e.g., `HOST_POWER_STATE` or
514     // `NEST_STREAM_MFG`). These properties have a different handler method format
515     // (they expect `aHeader` as an input argument) and are processed separately in
516     // `HandleCommandPropertySet()`.
517 
518     template <spinel_prop_key_t aKey> otError HandlePropertyGet(void);
519     template <spinel_prop_key_t aKey> otError HandlePropertySet(void);
520     template <spinel_prop_key_t aKey> otError HandlePropertyInsert(void);
521     template <spinel_prop_key_t aKey> otError HandlePropertyRemove(void);
522 
523     // --------------------------------------------------------------------------
524     // Property "set" handlers for special properties for which the spinel
525     // response needs to be created from within the set handler.
526 
527     otError HandlePropertySet_SPINEL_PROP_HOST_POWER_STATE(uint8_t aHeader);
528 
529 #if OPENTHREAD_CONFIG_DIAG_ENABLE
530     static_assert(OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE <=
531                       OPENTHREAD_CONFIG_NCP_TX_BUFFER_SIZE - kSpinelCmdHeaderSize - kSpinelPropIdSize,
532                   "diag output buffer should be smaller than NCP HDLC tx buffer");
533 
534     otError HandlePropertySet_SPINEL_PROP_NEST_STREAM_MFG(uint8_t aHeader);
535 #endif
536 
537 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
538     otError HandlePropertySet_SPINEL_PROP_MESHCOP_COMMISSIONER_GENERATE_PSKC(uint8_t aHeader);
539     otError HandlePropertySet_SPINEL_PROP_THREAD_COMMISSIONER_ENABLED(uint8_t aHeader);
540 #endif // OPENTHREAD_FTD
541 
542 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
543     otError DecodeStreamRawTxRequest(otRadioFrame &aFrame);
544     otError HandlePropertySet_SPINEL_PROP_STREAM_RAW(uint8_t aHeader);
545 #endif
546 
547     void ResetCounters(void);
548 
549     static uint8_t      ConvertLogLevel(otLogLevel aLogLevel);
550     static unsigned int ConvertLogRegion(otLogRegion aLogRegion);
551 
552 #if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK
553     /**
554      * Defines a vendor "command handler" hook to process vendor-specific spinel commands.
555      *
556      * @param[in] aHeader   The spinel frame header.
557      * @param[in] aCommand  The spinel command key.
558      *
559      * @retval OT_ERROR_NONE     The response is prepared.
560      * @retval OT_ERROR_NO_BUFS  Out of buffer while preparing the response.
561      *
562      */
563     otError VendorCommandHandler(uint8_t aHeader, unsigned int aCommand);
564 
565     /**
566      * Is a callback which mirrors `NcpBase::HandleFrameRemovedFromNcpBuffer()`. It is called when a
567      * spinel frame is sent and removed from NCP buffer.
568      *
569      * (a) This can be used to track and verify that a vendor spinel frame response is delivered to the host (tracking
570      *     the frame using its tag).
571      *
572      * (b) It indicates that NCP buffer space is now available (since a spinel frame is removed). This can be used to
573      *     implement mechanisms to re-send a failed/pending response or an async spinel frame.
574      *
575      * @param[in] aFrameTag    The tag of the frame removed from NCP buffer.
576      *
577      */
578     void VendorHandleFrameRemovedFromNcpBuffer(Spinel::Buffer::FrameTag aFrameTag);
579 
580     /**
581      * Defines a vendor "get property handler" hook to process vendor spinel properties.
582      *
583      * The vendor handler should return `OT_ERROR_NOT_FOUND` status if it does not support "get" operation for the
584      * given property key. Otherwise, the vendor handler should behave like other property get handlers, i.e., it
585      * should retrieve the property value and then encode and write the value into the NCP buffer. If the "get"
586      * operation itself fails, handler should write a `LAST_STATUS` with the error status into the NCP buffer.
587      *
588      * @param[in] aPropKey            The spinel property key.
589      *
590      * @retval OT_ERROR_NONE          Successfully retrieved the property value and prepared the response.
591      * @retval OT_ERROR_NOT_FOUND     Does not support the given property key.
592      * @retval OT_ERROR_NO_BUFS       Out of buffer while preparing the response.
593      *
594      */
595     otError VendorGetPropertyHandler(spinel_prop_key_t aPropKey);
596 
597     /**
598      * Defines a vendor "set property handler" hook to process vendor spinel properties.
599      *
600      * The vendor handler should return `OT_ERROR_NOT_FOUND` status if it does not support "set" operation for the
601      * given property key. Otherwise, the vendor handler should behave like other property set handlers, i.e., it
602      * should first decode the value from the input spinel frame and then perform the corresponding set operation. The
603      * handler should not prepare the spinel response and therefore should not write anything to the NCP buffer. The
604      * `otError` returned from handler (other than `OT_ERROR_NOT_FOUND`) indicates the error in either parsing of the
605      * input or the error of the set operation. In case of a successful "set", `NcpBase` set command handler will call
606      * the `VendorGetPropertyHandler()` for the same property key to prepare the response.
607      *
608      * @param[in] aPropKey  The spinel property key.
609      *
610      * @returns OT_ERROR_NOT_FOUND if it does not support the given property key, otherwise the error in either parsing
611      *          of the input or the "set" operation.
612      *
613      */
614     otError VendorSetPropertyHandler(spinel_prop_key_t aPropKey);
615 
616 #endif // OPENTHREAD_ENABLE_NCP_VENDOR_HOOK
617 
618 protected:
619     static NcpBase        *sNcpInstance;
620     static spinel_status_t ThreadErrorToSpinelStatus(otError aError);
621     static uint8_t         LinkFlagsToFlagByte(bool aRxOnWhenIdle, bool aDeviceType, bool aNetworkData);
622 
623     enum
624     {
625         kTxBufferSize       = OPENTHREAD_CONFIG_NCP_TX_BUFFER_SIZE, // Tx Buffer size (used by mTxFrameBuffer).
626         kResponseQueueSize  = OPENTHREAD_CONFIG_NCP_SPINEL_RESPONSE_QUEUE_SIZE,
627         kInvalidScanChannel = -1, // Invalid scan channel.
628     };
629 
630     Instance *mInstance;
631 #if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && OPENTHREAD_RADIO
632     Instance *mInstances[kSpinelInterfaceCount];
633 #endif
634     Spinel::Buffer  mTxFrameBuffer;
635     Spinel::Encoder mEncoder;
636     Spinel::Decoder mDecoder;
637     bool            mHostPowerStateInProgress;
638 
639     spinel_status_t mLastStatus;
640     uint32_t        mScanChannelMask;
641     uint16_t        mScanPeriod;
642     bool            mDiscoveryScanJoinerFlag;
643     bool            mDiscoveryScanEnableFiltering;
644     uint16_t        mDiscoveryScanPanId;
645 
646     Tasklet         mUpdateChangedPropsTask;
647     uint32_t        mThreadChangedFlags;
648     ChangedPropsSet mChangedPropsSet;
649 
650     spinel_host_power_state_t mHostPowerState;
651     Spinel::Buffer::FrameTag  mHostPowerReplyFrameTag;
652     uint8_t                   mHostPowerStateHeader;
653 
654 #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
655     otNcpDelegateAllowPeekPoke mAllowPeekDelegate;
656     otNcpDelegateAllowPeekPoke mAllowPokeDelegate;
657 #endif
658 
659     uint8_t mTxBuffer[kTxBufferSize];
660 
661     spinel_tid_t mNextExpectedTid[kSpinelInterfaceCount];
662 
663     uint8_t       mResponseQueueHead;
664     uint8_t       mResponseQueueTail;
665     ResponseEntry mResponseQueue[kResponseQueueSize];
666 
667     bool mAllowLocalNetworkDataChange;
668     bool mRequireJoinExistingNetwork;
669     bool mIsRawStreamEnabled[kSpinelInterfaceCount];
670     bool mPcapEnabled;
671     bool mDisableStreamWrite;
672     bool mShouldEmitChildTableUpdate;
673 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
674     bool mAllowLocalServerDataChange;
675 #endif
676 
677 #if OPENTHREAD_FTD
678 #if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE
679     otExtAddress mSteeringDataAddress;
680 #endif
681     uint8_t mPreferredRouteId;
682 #endif
683     uint8_t mCurCommandIid;
684 
685 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
686     uint8_t mCurTransmitTID[kSpinelInterfaceCount];
687     int8_t  mCurScanChannel[kSpinelInterfaceCount];
688     bool    mSrcMatchEnabled[kSpinelInterfaceCount];
689 #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
690 
691 #if OPENTHREAD_MTD || OPENTHREAD_FTD
692     otMessageQueue mMessageQueue;
693 
694     uint32_t mInboundSecureIpFrameCounter;    // Number of secure inbound data/IP frames.
695     uint32_t mInboundInsecureIpFrameCounter;  // Number of insecure inbound data/IP frames.
696     uint32_t mOutboundSecureIpFrameCounter;   // Number of secure outbound data/IP frames.
697     uint32_t mOutboundInsecureIpFrameCounter; // Number of insecure outbound data/IP frames.
698     uint32_t mDroppedOutboundIpFrameCounter;  // Number of dropped outbound data/IP frames.
699     uint32_t mDroppedInboundIpFrameCounter;   // Number of dropped inbound data/IP frames.
700 
701 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
702     enum : uint8_t
703     {
704         kSrpClientMaxHostAddresses = OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_MAX_HOST_ADDRESSES,
705     };
706 
707     otError EncodeSrpClientHostInfo(const otSrpClientHostInfo &aHostInfo);
708     otError EncodeSrpClientServices(const otSrpClientService *aServices);
709 
710     static void HandleSrpClientCallback(otError                    aError,
711                                         const otSrpClientHostInfo *aHostInfo,
712                                         const otSrpClientService  *aServices,
713                                         const otSrpClientService  *aRemovedServices,
714                                         void                      *aContext);
715     void        HandleSrpClientCallback(otError                    aError,
716                                         const otSrpClientHostInfo *aHostInfo,
717                                         const otSrpClientService  *aServices,
718                                         const otSrpClientService  *aRemovedServices);
719 
720     bool mSrpClientCallbackEnabled;
721 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
722 
723 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
724 
725     uint32_t mFramingErrorCounter;          // Number of improperly formed received spinel frames.
726     uint32_t mRxSpinelFrameCounter;         // Number of received (inbound) spinel frames.
727     uint32_t mRxSpinelOutOfOrderTidCounter; // Number of out of order received spinel frames (tid increase > 1).
728     uint32_t mTxSpinelFrameCounter;         // Number of sent (outbound) spinel frames.
729 
730     bool mDidInitialUpdates;
731 
732     uint64_t mLogTimestampBase; // Timestamp base used for logging
733 };
734 
735 } // namespace Ncp
736 } // namespace ot
737 
738 #endif // NCP_BASE_HPP_
739