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