blob: 311801634acc7a72f862d45a8bf00a01bb13fc9c [file] [log] [blame]
/*
* Copyright (c) 2016, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements a Spinel interface to the OpenThread stack.
*/
#include <openthread/config.h>
#include "ncp_base.hpp"
#include <stdarg.h>
#include <stdlib.h>
#if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD
#include <openthread/tmf_proxy.h>
#endif
#if OPENTHREAD_ENABLE_BORDER_ROUTER
#include <openthread/border_router.h>
#endif
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
#include <meshcop/commissioner.hpp>
#endif
#include <openthread/diag.h>
#include <openthread/icmp6.h>
#if OPENTHREAD_ENABLE_JAM_DETECTION
#include <openthread/jam_detection.h>
#endif
#include <openthread/ncp.h>
#include <openthread/openthread.h>
#if OPENTHREAD_FTD
#include <openthread/thread_ftd.h>
#endif
#include <openthread/platform/misc.h>
#include <openthread/platform/radio.h>
#include "openthread-instance.h"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "net/ip6.hpp"
namespace ot {
#define NCP_INVALID_SCAN_CHANNEL (-1)
#define NCP_CHANGED_PLATFORM_RESET (1U << 31)
#define NCP_CHANGED_THREAD_ON_MESH_NETS (1U << 30)
#define NCP_CHANGED_THREAD_OFF_MESH_ROUTES (1U << 29)
#define RSSI_OVERRIDE_DISABLED 127 // Used for PROP_MAC_WHITELIST
#define IGNORE_RETURN_VALUE(s) do { if (s){} } while (0)
// ----------------------------------------------------------------------------
// MARK: Command/Property Jump Tables
// ----------------------------------------------------------------------------
#define NCP_CMD_HANDLER_ENTRY(name) { SPINEL_CMD_##name, &NcpBase::CommandHandler_##name }
const NcpBase::CommandHandlerEntry NcpBase::mCommandHandlerTable[] =
{
NCP_CMD_HANDLER_ENTRY(NOOP),
NCP_CMD_HANDLER_ENTRY(RESET),
NCP_CMD_HANDLER_ENTRY(PROP_VALUE_GET),
NCP_CMD_HANDLER_ENTRY(PROP_VALUE_SET),
NCP_CMD_HANDLER_ENTRY(PROP_VALUE_INSERT),
NCP_CMD_HANDLER_ENTRY(PROP_VALUE_REMOVE),
NCP_CMD_HANDLER_ENTRY(NET_SAVE),
NCP_CMD_HANDLER_ENTRY(NET_CLEAR),
NCP_CMD_HANDLER_ENTRY(NET_RECALL),
#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
NCP_CMD_HANDLER_ENTRY(PEEK),
NCP_CMD_HANDLER_ENTRY(POKE),
#endif
};
#define NCP_GET_PROP_HANDLER_ENTRY(name) { SPINEL_PROP_##name, &NcpBase::GetPropertyHandler_##name }
#define NCP_GET_PROP_HANDLER_ENTRY_METHOD(name, method) { SPINEL_PROP_##name, &NcpBase::GetPropertyHandler_##method }
const NcpBase::GetPropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] =
{
NCP_GET_PROP_HANDLER_ENTRY(LAST_STATUS),
NCP_GET_PROP_HANDLER_ENTRY(PROTOCOL_VERSION),
NCP_GET_PROP_HANDLER_ENTRY(INTERFACE_TYPE),
NCP_GET_PROP_HANDLER_ENTRY(VENDOR_ID),
NCP_GET_PROP_HANDLER_ENTRY(CAPS),
NCP_GET_PROP_HANDLER_ENTRY(NCP_VERSION),
NCP_GET_PROP_HANDLER_ENTRY(INTERFACE_COUNT),
NCP_GET_PROP_HANDLER_ENTRY(POWER_STATE),
NCP_GET_PROP_HANDLER_ENTRY(HWADDR),
NCP_GET_PROP_HANDLER_ENTRY(LOCK),
NCP_GET_PROP_HANDLER_ENTRY(HOST_POWER_STATE),
NCP_GET_PROP_HANDLER_ENTRY(PHY_ENABLED),
NCP_GET_PROP_HANDLER_ENTRY(PHY_FREQ),
NCP_GET_PROP_HANDLER_ENTRY(PHY_CHAN_SUPPORTED),
NCP_GET_PROP_HANDLER_ENTRY(PHY_CHAN),
NCP_GET_PROP_HANDLER_ENTRY(PHY_RSSI),
NCP_GET_PROP_HANDLER_ENTRY(PHY_TX_POWER),
NCP_GET_PROP_HANDLER_ENTRY(PHY_RX_SENSITIVITY),
NCP_GET_PROP_HANDLER_ENTRY(MAC_SCAN_STATE),
NCP_GET_PROP_HANDLER_ENTRY(MAC_SCAN_MASK),
NCP_GET_PROP_HANDLER_ENTRY(MAC_SCAN_PERIOD),
NCP_GET_PROP_HANDLER_ENTRY(MAC_15_4_PANID),
NCP_GET_PROP_HANDLER_ENTRY(MAC_15_4_LADDR),
NCP_GET_PROP_HANDLER_ENTRY(MAC_15_4_SADDR),
NCP_GET_PROP_HANDLER_ENTRY(MAC_RAW_STREAM_ENABLED),
NCP_GET_PROP_HANDLER_ENTRY(MAC_PROMISCUOUS_MODE),
NCP_GET_PROP_HANDLER_ENTRY(MAC_EXTENDED_ADDR),
NCP_GET_PROP_HANDLER_ENTRY(MAC_DATA_POLL_PERIOD),
NCP_GET_PROP_HANDLER_ENTRY(NET_SAVED),
NCP_GET_PROP_HANDLER_ENTRY(NET_IF_UP),
NCP_GET_PROP_HANDLER_ENTRY(NET_STACK_UP),
NCP_GET_PROP_HANDLER_ENTRY(NET_ROLE),
NCP_GET_PROP_HANDLER_ENTRY(NET_NETWORK_NAME),
NCP_GET_PROP_HANDLER_ENTRY(NET_XPANID),
NCP_GET_PROP_HANDLER_ENTRY(NET_MASTER_KEY),
NCP_GET_PROP_HANDLER_ENTRY(NET_KEY_SEQUENCE_COUNTER),
NCP_GET_PROP_HANDLER_ENTRY(NET_PARTITION_ID),
NCP_GET_PROP_HANDLER_ENTRY(NET_KEY_SWITCH_GUARDTIME),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_LEADER_ADDR),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_PARENT),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_NEIGHBOR_TABLE),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_LEADER_RID),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_LEADER_WEIGHT),
#if OPENTHREAD_ENABLE_BORDER_ROUTER
NCP_GET_PROP_HANDLER_ENTRY(THREAD_NETWORK_DATA),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_STABLE_NETWORK_DATA),
#endif
NCP_GET_PROP_HANDLER_ENTRY(THREAD_NETWORK_DATA_VERSION),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_STABLE_NETWORK_DATA_VERSION),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_LEADER_NETWORK_DATA),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_STABLE_LEADER_NETWORK_DATA),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_OFF_MESH_ROUTES),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ASSISTING_PORTS),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ALLOW_LOCAL_NET_DATA_CHANGE),
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
NCP_GET_PROP_HANDLER_ENTRY(THREAD_COMMISSIONER_ENABLED),
#endif
#if OPENTHREAD_ENABLE_MAC_WHITELIST
NCP_GET_PROP_HANDLER_ENTRY(MAC_WHITELIST),
NCP_GET_PROP_HANDLER_ENTRY(MAC_WHITELIST_ENABLED),
NCP_GET_PROP_HANDLER_ENTRY(MAC_BLACKLIST),
NCP_GET_PROP_HANDLER_ENTRY(MAC_BLACKLIST_ENABLED),
#endif
NCP_GET_PROP_HANDLER_ENTRY(THREAD_MODE),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_CHILD_TIMEOUT),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_RLOC16),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ON_MESH_NETS),
NCP_GET_PROP_HANDLER_ENTRY(NET_REQUIRE_JOIN_EXISTING),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_ML_PREFIX),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_ML_ADDR),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_LL_ADDR),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_ADDRESS_TABLE),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_ROUTE_TABLE),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_ICMP_PING_OFFLOAD),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_RLOC16_DEBUG_PASSTHRU),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_JOINER_FLAG),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_ENABLE_FILTERING),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_PANID),
NCP_GET_PROP_HANDLER_ENTRY(STREAM_NET),
#if OPENTHREAD_FTD
NCP_GET_PROP_HANDLER_ENTRY(THREAD_CHILD_TABLE),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_LOCAL_LEADER_WEIGHT),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ROUTER_ROLE_ENABLED),
NCP_GET_PROP_HANDLER_ENTRY(NET_PSKC),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_CHILD_COUNT_MAX),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ROUTER_UPGRADE_THRESHOLD),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ROUTER_DOWNGRADE_THRESHOLD),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_CONTEXT_REUSE_DELAY),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_NETWORK_ID_TIMEOUT),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ROUTER_SELECTION_JITTER),
#endif
#if OPENTHREAD_ENABLE_JAM_DETECTION
NCP_GET_PROP_HANDLER_ENTRY(JAM_DETECT_ENABLE),
NCP_GET_PROP_HANDLER_ENTRY(JAM_DETECTED),
NCP_GET_PROP_HANDLER_ENTRY(JAM_DETECT_RSSI_THRESHOLD),
NCP_GET_PROP_HANDLER_ENTRY(JAM_DETECT_WINDOW),
NCP_GET_PROP_HANDLER_ENTRY(JAM_DETECT_BUSY),
NCP_GET_PROP_HANDLER_ENTRY(JAM_DETECT_HISTORY_BITMAP),
#endif
#if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD
NCP_GET_PROP_HANDLER_ENTRY(THREAD_TMF_PROXY_ENABLED),
#endif
// MAC counters
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_TOTAL, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_ACK_REQ, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_ACKED, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_NO_ACK_REQ, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_DATA, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_DATA_POLL, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_BEACON, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_BEACON_REQ, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_OTHER, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_RETRY, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_UNICAST, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_BROADCAST, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_ERR_CCA, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_ERR_ABORT, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_TOTAL, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_DATA, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_DATA_POLL, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_BEACON, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_BEACON_REQ, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_OTHER, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_FILT_WL, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_FILT_DA, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_UNICAST, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_BROADCAST, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_ERR_EMPTY, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_ERR_UKWN_NBR, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_ERR_NVLD_SADDR, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_ERR_SECURITY, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_ERR_BAD_FCS, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_ERR_OTHER, MAC_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_DUP, MAC_CNTR),
// NCP counters
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_IP_SEC_TOTAL, NCP_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_IP_INSEC_TOTAL, NCP_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_IP_DROPPED, NCP_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_IP_SEC_TOTAL, NCP_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_IP_INSEC_TOTAL, NCP_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_IP_DROPPED, NCP_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_SPINEL_TOTAL, NCP_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_SPINEL_TOTAL, NCP_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_SPINEL_ERR, NCP_CNTR),
// IP counters
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_IP_TX_SUCCESS, IP_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_IP_RX_SUCCESS, IP_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_IP_TX_FAILURE, IP_CNTR),
NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_IP_RX_FAILURE, IP_CNTR),
NCP_GET_PROP_HANDLER_ENTRY(MSG_BUFFER_COUNTERS),
NCP_GET_PROP_HANDLER_ENTRY(DEBUG_TEST_ASSERT),
NCP_GET_PROP_HANDLER_ENTRY(DEBUG_NCP_LOG_LEVEL),
#if OPENTHREAD_ENABLE_LEGACY
NCP_GET_PROP_HANDLER_ENTRY(NEST_LEGACY_ULA_PREFIX),
#endif
};
#define NCP_SET_PROP_HANDLER_ENTRY(name) { SPINEL_PROP_##name, &NcpBase::SetPropertyHandler_##name }
const NcpBase::SetPropertyHandlerEntry NcpBase::mSetPropertyHandlerTable[] =
{
NCP_SET_PROP_HANDLER_ENTRY(POWER_STATE),
NCP_SET_PROP_HANDLER_ENTRY(HOST_POWER_STATE),
#if OPENTHREAD_ENABLE_RAW_LINK_API
NCP_SET_PROP_HANDLER_ENTRY(PHY_ENABLED),
NCP_SET_PROP_HANDLER_ENTRY(MAC_15_4_SADDR),
NCP_SET_PROP_HANDLER_ENTRY(STREAM_RAW),
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
NCP_SET_PROP_HANDLER_ENTRY(PHY_TX_POWER),
NCP_SET_PROP_HANDLER_ENTRY(PHY_CHAN),
NCP_SET_PROP_HANDLER_ENTRY(MAC_PROMISCUOUS_MODE),
NCP_SET_PROP_HANDLER_ENTRY(MAC_SCAN_MASK),
NCP_SET_PROP_HANDLER_ENTRY(MAC_SCAN_STATE),
NCP_SET_PROP_HANDLER_ENTRY(MAC_SCAN_PERIOD),
NCP_SET_PROP_HANDLER_ENTRY(MAC_15_4_PANID),
NCP_SET_PROP_HANDLER_ENTRY(MAC_15_4_LADDR),
NCP_SET_PROP_HANDLER_ENTRY(MAC_DATA_POLL_PERIOD),
NCP_SET_PROP_HANDLER_ENTRY(MAC_RAW_STREAM_ENABLED),
NCP_SET_PROP_HANDLER_ENTRY(NET_IF_UP),
NCP_SET_PROP_HANDLER_ENTRY(NET_STACK_UP),
NCP_SET_PROP_HANDLER_ENTRY(NET_ROLE),
NCP_SET_PROP_HANDLER_ENTRY(NET_NETWORK_NAME),
NCP_SET_PROP_HANDLER_ENTRY(NET_XPANID),
NCP_SET_PROP_HANDLER_ENTRY(NET_MASTER_KEY),
NCP_SET_PROP_HANDLER_ENTRY(NET_KEY_SEQUENCE_COUNTER),
NCP_SET_PROP_HANDLER_ENTRY(NET_KEY_SWITCH_GUARDTIME),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_ASSISTING_PORTS),
NCP_SET_PROP_HANDLER_ENTRY(STREAM_NET_INSECURE),
NCP_SET_PROP_HANDLER_ENTRY(STREAM_NET),
NCP_SET_PROP_HANDLER_ENTRY(IPV6_ML_PREFIX),
NCP_SET_PROP_HANDLER_ENTRY(IPV6_ICMP_PING_OFFLOAD),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_RLOC16_DEBUG_PASSTHRU),
#if OPENTHREAD_ENABLE_MAC_WHITELIST
NCP_SET_PROP_HANDLER_ENTRY(MAC_WHITELIST),
NCP_SET_PROP_HANDLER_ENTRY(MAC_WHITELIST_ENABLED),
NCP_SET_PROP_HANDLER_ENTRY(MAC_BLACKLIST),
NCP_SET_PROP_HANDLER_ENTRY(MAC_BLACKLIST_ENABLED),
#endif
#if OPENTHREAD_ENABLE_RAW_LINK_API
NCP_SET_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_ENABLED),
NCP_SET_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_SHORT_ADDRESSES),
NCP_SET_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_EXTENDED_ADDRESSES),
#endif
NCP_SET_PROP_HANDLER_ENTRY(THREAD_MODE),
NCP_SET_PROP_HANDLER_ENTRY(NET_REQUIRE_JOIN_EXISTING),
NCP_SET_PROP_HANDLER_ENTRY(DEBUG_NCP_LOG_LEVEL),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_JOINER_FLAG),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_ENABLE_FILTERING),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_PANID),
#if OPENTHREAD_ENABLE_BORDER_ROUTER
NCP_SET_PROP_HANDLER_ENTRY(THREAD_ALLOW_LOCAL_NET_DATA_CHANGE),
#endif
#if OPENTHREAD_FTD
NCP_SET_PROP_HANDLER_ENTRY(NET_PSKC),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_CHILD_TIMEOUT),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_NETWORK_ID_TIMEOUT),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_LOCAL_LEADER_WEIGHT),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_ROUTER_ROLE_ENABLED),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_CHILD_COUNT_MAX),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_ROUTER_UPGRADE_THRESHOLD),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_ROUTER_DOWNGRADE_THRESHOLD),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_CONTEXT_REUSE_DELAY),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_ROUTER_SELECTION_JITTER),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_PREFERRED_ROUTER_ID),
#if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
NCP_SET_PROP_HANDLER_ENTRY(THREAD_STEERING_DATA),
#endif
#endif // #if OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_JAM_DETECTION
NCP_SET_PROP_HANDLER_ENTRY(JAM_DETECT_ENABLE),
NCP_SET_PROP_HANDLER_ENTRY(JAM_DETECT_RSSI_THRESHOLD),
NCP_SET_PROP_HANDLER_ENTRY(JAM_DETECT_WINDOW),
NCP_SET_PROP_HANDLER_ENTRY(JAM_DETECT_BUSY),
#endif
#if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD
NCP_SET_PROP_HANDLER_ENTRY(THREAD_TMF_PROXY_ENABLED),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_TMF_PROXY_STREAM),
#endif
#if OPENTHREAD_ENABLE_DIAG
NCP_SET_PROP_HANDLER_ENTRY(NEST_STREAM_MFG),
#endif
#if OPENTHREAD_ENABLE_LEGACY
NCP_SET_PROP_HANDLER_ENTRY(NEST_LEGACY_ULA_PREFIX),
#endif
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
NCP_SET_PROP_HANDLER_ENTRY(THREAD_COMMISSIONER_ENABLED),
#endif
NCP_SET_PROP_HANDLER_ENTRY(CNTR_RESET),
};
#define NCP_INSERT_PROP_HANDLER_ENTRY(name) { SPINEL_PROP_##name, &NcpBase::InsertPropertyHandler_##name }
const NcpBase::InsertPropertyHandlerEntry NcpBase::mInsertPropertyHandlerTable[] =
{
#if OPENTHREAD_ENABLE_RAW_LINK_API
NCP_INSERT_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_SHORT_ADDRESSES),
NCP_INSERT_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_EXTENDED_ADDRESSES),
#endif
NCP_INSERT_PROP_HANDLER_ENTRY(IPV6_ADDRESS_TABLE),
NCP_INSERT_PROP_HANDLER_ENTRY(THREAD_ASSISTING_PORTS),
#if OPENTHREAD_ENABLE_BORDER_ROUTER
NCP_INSERT_PROP_HANDLER_ENTRY(THREAD_OFF_MESH_ROUTES),
NCP_INSERT_PROP_HANDLER_ENTRY(THREAD_ON_MESH_NETS),
#endif
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
NCP_INSERT_PROP_HANDLER_ENTRY(THREAD_JOINERS),
#endif
#if OPENTHREAD_ENABLE_MAC_WHITELIST
NCP_INSERT_PROP_HANDLER_ENTRY(MAC_WHITELIST),
NCP_INSERT_PROP_HANDLER_ENTRY(MAC_BLACKLIST),
#endif
};
#define NCP_REMOVE_PROP_HANDLER_ENTRY(name) { SPINEL_PROP_##name, &NcpBase::RemovePropertyHandler_##name }
const NcpBase::RemovePropertyHandlerEntry NcpBase::mRemovePropertyHandlerTable[] =
{
#if OPENTHREAD_ENABLE_RAW_LINK_API
NCP_REMOVE_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_SHORT_ADDRESSES),
NCP_REMOVE_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_EXTENDED_ADDRESSES),
#endif
NCP_REMOVE_PROP_HANDLER_ENTRY(IPV6_ADDRESS_TABLE),
#if OPENTHREAD_ENABLE_BORDER_ROUTER
NCP_REMOVE_PROP_HANDLER_ENTRY(THREAD_OFF_MESH_ROUTES),
NCP_REMOVE_PROP_HANDLER_ENTRY(THREAD_ON_MESH_NETS),
#endif
NCP_REMOVE_PROP_HANDLER_ENTRY(THREAD_ASSISTING_PORTS),
#if OPENTHREAD_ENABLE_MAC_WHITELIST
NCP_REMOVE_PROP_HANDLER_ENTRY(MAC_WHITELIST),
NCP_REMOVE_PROP_HANDLER_ENTRY(MAC_BLACKLIST),
#endif
#if OPENTHREAD_FTD
NCP_REMOVE_PROP_HANDLER_ENTRY(THREAD_ACTIVE_ROUTER_IDS),
#endif
};
// ----------------------------------------------------------------------------
// MARK: Utility Functions
// ----------------------------------------------------------------------------
static spinel_status_t ThreadErrorToSpinelStatus(otError aError)
{
spinel_status_t ret;
switch (aError)
{
case OT_ERROR_NONE:
ret = SPINEL_STATUS_OK;
break;
case OT_ERROR_FAILED:
ret = SPINEL_STATUS_FAILURE;
break;
case OT_ERROR_DROP:
ret = SPINEL_STATUS_DROPPED;
break;
case OT_ERROR_NO_BUFS:
ret = SPINEL_STATUS_NOMEM;
break;
case OT_ERROR_BUSY:
ret = SPINEL_STATUS_BUSY;
break;
case OT_ERROR_PARSE:
ret = SPINEL_STATUS_PARSE_ERROR;
break;
case OT_ERROR_INVALID_ARGS:
ret = SPINEL_STATUS_INVALID_ARGUMENT;
break;
case OT_ERROR_NOT_IMPLEMENTED:
ret = SPINEL_STATUS_UNIMPLEMENTED;
break;
case OT_ERROR_INVALID_STATE:
ret = SPINEL_STATUS_INVALID_STATE;
break;
case OT_ERROR_NO_ACK:
ret = SPINEL_STATUS_NO_ACK;
break;
case OT_ERROR_CHANNEL_ACCESS_FAILURE:
ret = SPINEL_STATUS_CCA_FAILURE;
break;
case OT_ERROR_ALREADY:
ret = SPINEL_STATUS_ALREADY;
break;
case OT_ERROR_NOT_FOUND:
ret = SPINEL_STATUS_ITEM_NOT_FOUND;
break;
case OT_ERROR_DISABLED_FEATURE:
ret = SPINEL_STATUS_INVALID_COMMAND_FOR_PROP;
break;
default:
// Unknown error code. Wrap it as a Spinel status and return that.
ret = static_cast<spinel_status_t>(SPINEL_STATUS_STACK_NATIVE__BEGIN + static_cast<uint32_t>(aError));
break;
}
return ret;
}
static spinel_status_t ResetReasonToSpinelStatus(otPlatResetReason aReason)
{
spinel_status_t ret;
switch (aReason)
{
case OT_PLAT_RESET_REASON_POWER_ON:
ret = SPINEL_STATUS_RESET_POWER_ON;
break;
case OT_PLAT_RESET_REASON_EXTERNAL:
ret = SPINEL_STATUS_RESET_EXTERNAL;
break;
case OT_PLAT_RESET_REASON_SOFTWARE:
ret = SPINEL_STATUS_RESET_SOFTWARE;
break;
case OT_PLAT_RESET_REASON_FAULT:
ret = SPINEL_STATUS_RESET_FAULT;
break;
case OT_PLAT_RESET_REASON_CRASH:
ret = SPINEL_STATUS_RESET_CRASH;
break;
case OT_PLAT_RESET_REASON_ASSERT:
ret = SPINEL_STATUS_RESET_ASSERT;
break;
case OT_PLAT_RESET_REASON_WATCHDOG:
ret = SPINEL_STATUS_RESET_WATCHDOG;
break;
case OT_PLAT_RESET_REASON_OTHER:
ret = SPINEL_STATUS_RESET_OTHER;
break;
default:
ret = SPINEL_STATUS_RESET_UNKNOWN;
break;
}
return ret;
}
static uint8_t BorderRouterConfigToFlagByte(const otBorderRouterConfig &aConfig)
{
uint8_t flags(0);
if (aConfig.mPreferred)
{
flags |= SPINEL_NET_FLAG_PREFERRED;
}
if (aConfig.mSlaac)
{
flags |= SPINEL_NET_FLAG_SLAAC;
}
if (aConfig.mDhcp)
{
flags |= SPINEL_NET_FLAG_DHCP;
}
if (aConfig.mDefaultRoute)
{
flags |= SPINEL_NET_FLAG_DEFAULT_ROUTE;
}
if (aConfig.mConfigure)
{
flags |= SPINEL_NET_FLAG_CONFIGURE;
}
if (aConfig.mOnMesh)
{
flags |= SPINEL_NET_FLAG_ON_MESH;
}
flags |= (aConfig.mPreference << SPINEL_NET_FLAG_PREFERENCE_OFFSET);
return flags;
}
static uint8_t LinkFlagsToFlagByte(bool aRxOnWhenIdle, bool aSecureDataRequests, bool aDeviceType, bool aNetworkData)
{
uint8_t flags(0);
if (aRxOnWhenIdle)
{
flags |= SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE;
}
if (aSecureDataRequests)
{
flags |= SPINEL_THREAD_MODE_SECURE_DATA_REQUEST;
}
if (aDeviceType)
{
flags |= SPINEL_THREAD_MODE_FULL_FUNCTION_DEV;
}
if (aNetworkData)
{
flags |= SPINEL_THREAD_MODE_FULL_NETWORK_DATA;
}
return flags;
}
static uint8_t ExternalRoutePreferenceToFlagByte(int aPreference)
{
uint8_t flags;
switch (aPreference)
{
case OT_ROUTE_PREFERENCE_LOW:
flags = SPINEL_ROUTE_PREFERENCE_LOW;
break;
case OT_ROUTE_PREFERENCE_MED:
flags = SPINEL_ROUTE_PREFERENCE_MEDIUM;
break;
case OT_ROUTE_PREFERENCE_HIGH:
flags = SPINEL_ROUTE_PREFERENCE_HIGH;
break;
default:
flags = SPINEL_ROUTE_PREFERENCE_MEDIUM;
break;
}
return flags;
}
#if OPENTHREAD_ENABLE_BORDER_ROUTER
static int FlagByteToExternalRoutePreference(uint8_t aFlags)
{
int route_preference = 0;
switch (aFlags & SPINEL_NET_FLAG_PREFERENCE_MASK)
{
case SPINEL_ROUTE_PREFERENCE_HIGH:
route_preference = OT_ROUTE_PREFERENCE_HIGH;
break;
case SPINEL_ROUTE_PREFERENCE_MEDIUM:
route_preference = OT_ROUTE_PREFERENCE_MED;
break;
case SPINEL_ROUTE_PREFERENCE_LOW:
route_preference = OT_ROUTE_PREFERENCE_LOW;
break;
}
return route_preference;
}
#endif // OPENTHREAD_ENABLE_BORDER_ROUTER
// ----------------------------------------------------------------------------
// MARK: Class Boilerplate
// ----------------------------------------------------------------------------
NcpBase *NcpBase::sNcpInstance = NULL;
NcpBase::NcpBase(otInstance *aInstance):
mInstance(aInstance),
mTxFrameBuffer(mTxBuffer, sizeof(mTxBuffer)),
mLastStatus(SPINEL_STATUS_OK),
mSupportedChannelMask(OT_RADIO_SUPPORTED_CHANNELS),
mChannelMask(OT_RADIO_SUPPORTED_CHANNELS),
mScanPeriod(200), // ms
mDiscoveryScanJoinerFlag(false),
mDiscoveryScanEnableFiltering(false),
mDiscoveryScanPanId(0xffff),
mUpdateChangedPropsTask(aInstance->mIp6.mTaskletScheduler, &NcpBase::UpdateChangedProps, this),
mChangedFlags(NCP_CHANGED_PLATFORM_RESET),
mShouldSignalEndOfScan(false),
mHostPowerState(SPINEL_HOST_POWER_STATE_ONLINE),
mHostPowerStateInProgress(false),
mHostPowerReplyFrameTag(NcpFrameBuffer::kInvalidTag),
mHostPowerStateHeader(0),
#if OPENTHREAD_ENABLE_JAM_DETECTION
mShouldSignalJamStateChange(false),
#endif
#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
mAllowPeekDelegate(NULL),
mAllowPokeDelegate(NULL),
#endif
mDroppedReplyTid(0),
mDroppedReplyTidBitSet(0),
mNextExpectedTid(0),
mAllowLocalNetworkDataChange(false),
mRequireJoinExistingNetwork(false),
mIsRawStreamEnabled(false),
mDisableStreamWrite(false),
#if OPENTHREAD_ENABLE_RAW_LINK_API
mCurTransmitTID(0),
mCurReceiveChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL),
mCurScanChannel(NCP_INVALID_SCAN_CHANNEL),
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
mFramingErrorCounter(0),
mRxSpinelFrameCounter(0),
mRxSpinelOutOfOrderTidCounter(0),
mTxSpinelFrameCounter(0),
mInboundSecureIpFrameCounter(0),
mInboundInsecureIpFrameCounter(0),
mOutboundSecureIpFrameCounter(0),
mOutboundInsecureIpFrameCounter(0),
mDroppedOutboundIpFrameCounter(0),
mDroppedInboundIpFrameCounter(0)
{
assert(mInstance != NULL);
sNcpInstance = this;
mTxFrameBuffer.SetFrameRemovedCallback(&NcpBase::HandleFrameRemovedFromNcpBuffer, this);
otSetStateChangedCallback(mInstance, &NcpBase::HandleNetifStateChanged, this);
otIp6SetReceiveCallback(mInstance, &NcpBase::HandleDatagramFromStack, this);
otIp6SetReceiveFilterEnabled(mInstance, true);
otLinkSetPcapCallback(mInstance, &NcpBase::HandleRawFrame, static_cast<void *>(this));
otIcmp6SetEchoEnabled(mInstance, false);
mUpdateChangedPropsTask.Post();
#if OPENTHREAD_ENABLE_LEGACY
mLegacyNodeDidJoin = false;
mLegacyHandlers = NULL;
memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix));
#endif
}
NcpBase *NcpBase::GetNcpInstance(void)
{
return sNcpInstance;
}
// ----------------------------------------------------------------------------
// MARK: Outbound Frame methods
// ----------------------------------------------------------------------------
otError NcpBase::OutboundFrameBegin(void)
{
return mTxFrameBuffer.InFrameBegin();
}
otError NcpBase::OutboundFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength)
{
return mTxFrameBuffer.InFrameFeedData(aDataBuffer, aDataBufferLength);
}
otError NcpBase::OutboundFrameFeedMessage(otMessage *aMessage)
{
return mTxFrameBuffer.InFrameFeedMessage(aMessage);
}
otError NcpBase::OutboundFrameEnd(void)
{
return mTxFrameBuffer.InFrameEnd();
}
NcpFrameBuffer::FrameTag NcpBase::GetLastOutboundFrameTag(void)
{
return mTxFrameBuffer.InFrameGetLastTag();
}
#if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD
void NcpBase::HandleTmfProxyStream(otMessage *aMessage, uint16_t aLocator, uint16_t aPort, void *aContext)
{
static_cast<NcpBase *>(aContext)->HandleTmfProxyStream(aMessage, aLocator, aPort);
}
void NcpBase::HandleTmfProxyStream(otMessage *aMessage, uint16_t aLocator, uint16_t aPort)
{
otError error = OT_ERROR_NONE;
uint16_t length = otMessageGetLength(aMessage);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S,
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_THREAD_TMF_PROXY_STREAM,
length
));
SuccessOrExit(error = OutboundFrameFeedMessage(aMessage));
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_UINT16_S,
aLocator,
aPort
));
// Set the `aMessage` pointer to NULL to indicate that it does
// not need to be freed at the exit. The `aMessage` is now owned
// by the OutboundFrame and will be freed when the frame is either
// successfully sent and then removed, or if the frame gets
// discarded.
aMessage = NULL;
SuccessOrExit(error = OutboundFrameSend());
exit:
if (aMessage != NULL)
{
otMessageFree(aMessage);
}
if (error != OT_ERROR_NONE)
{
SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_DROPPED);
}
}
#endif // OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD
// ----------------------------------------------------------------------------
// MARK: Outbound Datagram Handling
// ----------------------------------------------------------------------------
void NcpBase::HandleDatagramFromStack(otMessage *aMessage, void *aContext)
{
static_cast<NcpBase *>(aContext)->HandleDatagramFromStack(aMessage);
}
void NcpBase::HandleDatagramFromStack(otMessage *aMessage)
{
otError error = OT_ERROR_NONE;
bool isSecure = otMessageIsLinkSecurityEnabled(aMessage);
uint16_t length = otMessageGetLength(aMessage);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S,
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
isSecure ? SPINEL_PROP_STREAM_NET : SPINEL_PROP_STREAM_NET_INSECURE,
length
));
SuccessOrExit(error = OutboundFrameFeedMessage(aMessage));
// Set the `aMessage` pointer to NULL to indicate that it does
// not need to be freed at the exit. The `aMessage` is now owned
// by the OutboundFrame and will be freed when the frame is either
// successfully sent and then removed, or if the frame gets
// discarded.
aMessage = NULL;
// Append any metadata (rssi, lqi, channel, etc) here!
SuccessOrExit(error = OutboundFrameSend());
exit:
if (aMessage != NULL)
{
otMessageFree(aMessage);
}
if (error != OT_ERROR_NONE)
{
SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_DROPPED);
mDroppedOutboundIpFrameCounter++;
}
else
{
if (isSecure)
{
mOutboundSecureIpFrameCounter++;
}
else
{
mOutboundInsecureIpFrameCounter++;
}
}
}
// ----------------------------------------------------------------------------
// MARK: Raw frame handling
// ----------------------------------------------------------------------------
void NcpBase::HandleRawFrame(const otRadioFrame *aFrame, void *aContext)
{
static_cast<NcpBase *>(aContext)->HandleRawFrame(aFrame);
}
void NcpBase::HandleRawFrame(const otRadioFrame *aFrame)
{
uint16_t flags = 0;
if (!mIsRawStreamEnabled)
{
goto exit;
}
SuccessOrExit(OutboundFrameBegin());
if (aFrame->mDidTX)
{
flags |= SPINEL_MD_FLAG_TX;
}
// Append frame header and frame length
SuccessOrExit(
OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S,
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_STREAM_RAW,
aFrame->mLength
));
// Append the frame contents
SuccessOrExit(OutboundFrameFeedData(aFrame->mPsdu, aFrame->mLength));
// Append metadata (rssi, etc)
SuccessOrExit(
OutboundFrameFeedPacked(
SPINEL_DATATYPE_INT8_S
SPINEL_DATATYPE_INT8_S
SPINEL_DATATYPE_UINT16_S
SPINEL_DATATYPE_STRUCT_S( // PHY-data
SPINEL_DATATYPE_NULL_S // Empty for now
)
SPINEL_DATATYPE_STRUCT_S( // Vendor-data
SPINEL_DATATYPE_NULL_S // Empty for now
),
aFrame->mPower, // TX Power
-128, // Noise Floor (Currently unused)
flags // Flags
// Skip PHY and Vendor data for now
));
SuccessOrExit(OutboundFrameSend());
exit:
return;
}
// ----------------------------------------------------------------------------
// MARK: Scan Results Glue
// ----------------------------------------------------------------------------
void NcpBase::HandleActiveScanResult_Jump(otActiveScanResult *aResult, void *aContext)
{
static_cast<NcpBase *>(aContext)->HandleActiveScanResult(aResult);
}
void NcpBase::HandleActiveScanResult(otActiveScanResult *aResult)
{
otError error;
if (aResult)
{
uint8_t flags = static_cast<uint8_t>(aResult->mVersion << SPINEL_BEACON_THREAD_FLAG_VERSION_SHIFT);
if (aResult->mIsJoinable)
{
flags |= SPINEL_BEACON_THREAD_FLAG_JOINABLE;
}
if (aResult->mIsNative)
{
flags |= SPINEL_BEACON_THREAD_FLAG_NATIVE;
}
SendPropertyUpdate(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_INSERTED,
SPINEL_PROP_MAC_SCAN_BEACON,
SPINEL_DATATYPE_MAC_SCAN_RESULT_S(
SPINEL_802_15_4_DATATYPE_MAC_SCAN_RESULT_V1_S,
SPINEL_NET_DATATYPE_MAC_SCAN_RESULT_V2_S
),
aResult->mChannel, // Channel
aResult->mRssi, // RSSI
// "mac-layer data"
aResult->mExtAddress.m8, // laddr
0xFFFF, // saddr, not given
aResult->mPanId, // panid
aResult->mLqi, // lqi
// "net-layer data"
SPINEL_PROTOCOL_TYPE_THREAD, // type
flags, // flags
aResult->mNetworkName.m8, // network name
aResult->mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE, // xpanid
aResult->mSteeringData.m8, aResult->mSteeringData.mLength // steering data
);
}
else
{
// We are finished with the scan, so send out
// a property update indicating such.
error = SendPropertyUpdate(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_MAC_SCAN_STATE,
SPINEL_DATATYPE_UINT8_S,
SPINEL_SCAN_STATE_IDLE
);
// If we could not send the end of scan indicator message now (no
// buffer space), we set `mShouldSignalEndOfScan` to true to send
// it out when buffer space becomes available.
if (error != OT_ERROR_NONE)
{
mShouldSignalEndOfScan = true;
}
}
}
void NcpBase::HandleEnergyScanResult_Jump(otEnergyScanResult *aResult, void *aContext)
{
static_cast<NcpBase *>(aContext)->HandleEnergyScanResult(aResult);
}
void NcpBase::HandleEnergyScanResult(otEnergyScanResult *aResult)
{
otError error;
if (aResult)
{
SendPropertyUpdate(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_INSERTED,
SPINEL_PROP_MAC_ENERGY_SCAN_RESULT,
(
SPINEL_DATATYPE_UINT8_S // Channel
SPINEL_DATATYPE_INT8_S // Rssi
),
aResult->mChannel,
aResult->mMaxRssi
);
}
else
{
// We are finished with the scan, so send out
// a property update indicating such.
error = SendPropertyUpdate(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_MAC_SCAN_STATE,
SPINEL_DATATYPE_UINT8_S,
SPINEL_SCAN_STATE_IDLE
);
// If we could not send the end of scan indicator message now (no
// buffer space), we set `mShouldSignalEndOfScan` to true to send
// it out when buffer space becomes available.
if (error != OT_ERROR_NONE)
{
mShouldSignalEndOfScan = true;
}
}
}
// ----------------------------------------------------------------------------
// MARK: Raw Link-Layer Datapath Glue
// ----------------------------------------------------------------------------
#if OPENTHREAD_ENABLE_RAW_LINK_API
void NcpBase::LinkRawReceiveDone(otInstance *, otRadioFrame *aFrame, otError aError)
{
sNcpInstance->LinkRawReceiveDone(aFrame, aError);
}
void NcpBase::LinkRawReceiveDone(otRadioFrame *aFrame, otError aError)
{
uint16_t flags = 0;
SuccessOrExit(OutboundFrameBegin());
if (aFrame->mDidTX)
{
flags |= SPINEL_MD_FLAG_TX;
}
// Append frame header and frame length
SuccessOrExit(
OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S,
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_STREAM_RAW,
(aError == OT_ERROR_NONE) ? aFrame->mLength : 0
));
if (aError == OT_ERROR_NONE)
{
// Append the frame contents
SuccessOrExit(OutboundFrameFeedData(aFrame->mPsdu, aFrame->mLength));
}
// Append metadata (rssi, etc)
SuccessOrExit(
OutboundFrameFeedPacked(
SPINEL_DATATYPE_INT8_S
SPINEL_DATATYPE_INT8_S
SPINEL_DATATYPE_UINT16_S
SPINEL_DATATYPE_STRUCT_S( // PHY-data
SPINEL_DATATYPE_UINT8_S // 802.15.4 channel
SPINEL_DATATYPE_UINT8_S // 802.15.4 LQI
)
SPINEL_DATATYPE_STRUCT_S( // Vendor-data
SPINEL_DATATYPE_UINT_PACKED_S
),
aFrame->mPower, // TX Power
-128, // Noise Floor (Currently unused)
flags, // Flags
aFrame->mChannel, // Receive channel
aFrame->mLqi, // Link quality indicator
aError // Receive error
));
SuccessOrExit(OutboundFrameSend());
exit:
return;
}
void NcpBase::LinkRawTransmitDone(otInstance *, otRadioFrame *aFrame, bool aFramePending, otError aError)
{
sNcpInstance->LinkRawTransmitDone(aFrame, aFramePending, aError);
}
void NcpBase::LinkRawTransmitDone(otRadioFrame *, bool aFramePending, otError aError)
{
if (mCurTransmitTID)
{
SendPropertyUpdate(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | mCurTransmitTID,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_LAST_STATUS,
SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_BOOL_S,
ThreadErrorToSpinelStatus(aError),
aFramePending
);
// Clear cached transmit TID
mCurTransmitTID = 0;
}
}
void NcpBase::LinkRawEnergyScanDone(otInstance *, int8_t aEnergyScanMaxRssi)
{
sNcpInstance->LinkRawEnergyScanDone(aEnergyScanMaxRssi);
}
void NcpBase::LinkRawEnergyScanDone(int8_t aEnergyScanMaxRssi)
{
SendPropertyUpdate(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_MAC_ENERGY_SCAN_RESULT,
SPINEL_DATATYPE_UINT8_S
SPINEL_DATATYPE_INT8_S,
mCurScanChannel,
aEnergyScanMaxRssi
);
// Clear current scan channel
mCurScanChannel = NCP_INVALID_SCAN_CHANNEL;
// Make sure we are back listening on the original receive channel,
// since the energy scan could have been on a different channel.
otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone);
// We are finished with the scan, so send out
// a property update indicating such.
SendPropertyUpdate(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_MAC_SCAN_STATE,
SPINEL_DATATYPE_UINT8_S,
SPINEL_SCAN_STATE_IDLE
);
}
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
// ----------------------------------------------------------------------------
// MARK: Address Table Changed Glue
// ----------------------------------------------------------------------------
void NcpBase::HandleNetifStateChanged(uint32_t aFlags, void *aContext)
{
NcpBase *obj = static_cast<NcpBase *>(aContext);
obj->mChangedFlags |= aFlags;
obj->mUpdateChangedPropsTask.Post();
}
void NcpBase::UpdateChangedProps(Tasklet &aTasklet)
{
OT_UNUSED_VARIABLE(aTasklet);
GetNcpInstance()->UpdateChangedProps();
}
void NcpBase::UpdateChangedProps(void)
{
while (mChangedFlags != 0)
{
if ((mChangedFlags & NCP_CHANGED_PLATFORM_RESET) != 0)
{
SuccessOrExit(
SendLastStatus(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
ResetReasonToSpinelStatus(otPlatGetResetReason(mInstance))
));
mChangedFlags &= ~static_cast<uint32_t>(NCP_CHANGED_PLATFORM_RESET);
}
else if ((mChangedFlags & OT_CHANGED_THREAD_LL_ADDR) != 0)
{
SuccessOrExit(
HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_IPV6_LL_ADDR
));
mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_LL_ADDR);
}
else if ((mChangedFlags & OT_CHANGED_THREAD_ML_ADDR) != 0)
{
SuccessOrExit(
HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_IPV6_ML_ADDR
));
mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_ML_ADDR);
}
else if ((mChangedFlags & OT_CHANGED_THREAD_ROLE) != 0)
{
if (mRequireJoinExistingNetwork)
{
switch (otThreadGetDeviceRole(mInstance))
{
case OT_DEVICE_ROLE_DETACHED:
case OT_DEVICE_ROLE_DISABLED:
break;
default:
mRequireJoinExistingNetwork = false;
break;
}
if ((otThreadGetDeviceRole(mInstance) == OT_DEVICE_ROLE_LEADER)
&& otThreadIsSingleton(mInstance)
#if OPENTHREAD_ENABLE_LEGACY
&& !mLegacyNodeDidJoin
#endif
)
{
mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_PARTITION_ID);
otThreadSetEnabled(mInstance, false);
// TODO: It would be nice to be able to indicate
// something more specific than SPINEL_STATUS_JOIN_FAILURE
// here, but it isn't clear how that would work
// with the current OpenThread API.
SuccessOrExit(
SendLastStatus(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_STATUS_JOIN_FAILURE
));
SuccessOrExit(
HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_NET_STACK_UP
));
}
SuccessOrExit(
HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING
));
}
SuccessOrExit(
HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_NET_ROLE
));
mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_ROLE);
}
else if ((mChangedFlags & OT_CHANGED_THREAD_PARTITION_ID) != 0)
{
SuccessOrExit(
HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_NET_PARTITION_ID
));
mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_PARTITION_ID);
}
else if ((mChangedFlags & OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER) != 0)
{
SuccessOrExit(
HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER
));
mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER);
}
else if ((mChangedFlags & (OT_CHANGED_IP6_ADDRESS_ADDED | OT_CHANGED_IP6_ADDRESS_REMOVED)) != 0)
{
SuccessOrExit(
HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_IPV6_ADDRESS_TABLE
));
mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_IP6_ADDRESS_ADDED | OT_CHANGED_IP6_ADDRESS_REMOVED);
}
else if ((mChangedFlags & (OT_CHANGED_THREAD_CHILD_ADDED | OT_CHANGED_THREAD_CHILD_REMOVED)) != 0)
{
SuccessOrExit(
HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_THREAD_CHILD_TABLE
));
mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_CHILD_ADDED | OT_CHANGED_THREAD_CHILD_REMOVED);
}
else if ((mChangedFlags & OT_CHANGED_THREAD_NETDATA) != 0)
{
SuccessOrExit(
HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_THREAD_LEADER_NETWORK_DATA
));
mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_NETDATA);
// If the network data is updated, after successfully sending (or queuing) the
// network data spinel message, we add `NCP_CHANGED_THREAD_ON_MESH_NETS` and
// `NCP_CHANGED_THREAD_OFF_MESH_ROUTES` to the `mChangedFlags` so that we
// separately send the list of on-mesh prefixes and off-mesh routes.
mChangedFlags |= NCP_CHANGED_THREAD_ON_MESH_NETS | NCP_CHANGED_THREAD_OFF_MESH_ROUTES;
}
else if ((mChangedFlags & NCP_CHANGED_THREAD_ON_MESH_NETS) != 0)
{
SuccessOrExit(
HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_THREAD_ON_MESH_NETS
));
mChangedFlags &= ~static_cast<uint32_t>(NCP_CHANGED_THREAD_ON_MESH_NETS);
}
else if ((mChangedFlags & NCP_CHANGED_THREAD_OFF_MESH_ROUTES) != 0)
{
SuccessOrExit(
HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_THREAD_OFF_MESH_ROUTES
));
mChangedFlags &= ~static_cast<uint32_t>(NCP_CHANGED_THREAD_OFF_MESH_ROUTES);
}
else if ((mChangedFlags & (OT_CHANGED_THREAD_RLOC_ADDED | OT_CHANGED_THREAD_RLOC_REMOVED)) != 0)
{
mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_RLOC_ADDED | OT_CHANGED_THREAD_RLOC_REMOVED);
}
}
exit:
return;
}
// ----------------------------------------------------------------------------
// MARK: Serial Traffic Glue
// ----------------------------------------------------------------------------
otError NcpBase::OutboundFrameSend(void)
{
otError error;
SuccessOrExit(error = OutboundFrameEnd());
mTxSpinelFrameCounter++;
exit:
return error;
}
void NcpBase::HandleReceive(const uint8_t *aBuf, uint16_t aBufLength)
{
uint8_t header = 0;
unsigned int command = 0;
spinel_ssize_t parsedLength;
const uint8_t *argPtr = NULL;
unsigned int argLen = 0;
otError error = OT_ERROR_NONE;
spinel_tid_t tid = 0;
parsedLength = spinel_datatype_unpack(
aBuf,
aBufLength,
SPINEL_DATATYPE_COMMAND_S SPINEL_DATATYPE_DATA_S,
&header,
&command,
&argPtr,
&argLen
);
tid = SPINEL_HEADER_GET_TID(header);
// Receiving any message from the host has the side effect of transitioning the host power state to online.
mHostPowerState = SPINEL_HOST_POWER_STATE_ONLINE;
mHostPowerStateInProgress = false;
if (parsedLength == aBufLength)
{
error = HandleCommand(header, command, argPtr, static_cast<uint16_t>(argLen));
// Check if we may have missed a `tid` in the sequence.
if ((mNextExpectedTid != 0) && (tid != mNextExpectedTid))
{
mRxSpinelOutOfOrderTidCounter++;
}
mNextExpectedTid = SPINEL_GET_NEXT_TID(tid);
}
else
{
error = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
if (error == OT_ERROR_NO_BUFS)
{
// If we cannot send a response due to buffer space not being
// available, we remember the TID of command so to send an
// error status when buffer space becomes available later.
// Valid TID range is 1-15 (zero being used as special case
// where no reply is expected). TIDs for dropped reply are
// stored in two variables: `mDroppedReplyTidBitSet` which
// is a bit set (bits 1-15 correspond to TID values 1-15).
// The first/next dropped TID value in the set is stored in
// `mDroppedReplyTid` (with value zero indicating that there
// is no dropped reply).
if (tid != 0)
{
if (mDroppedReplyTid == 0)
{
mDroppedReplyTid = tid;
}
mDroppedReplyTidBitSet |= (1 << tid);
}
}
mRxSpinelFrameCounter++;
}
void NcpBase::HandleFrameRemovedFromNcpBuffer(void *aContext, NcpFrameBuffer::FrameTag aFrameTag,
NcpFrameBuffer *aNcpBuffer)
{
OT_UNUSED_VARIABLE(aNcpBuffer);
static_cast<NcpBase *>(aContext)->HandleFrameRemovedFromNcpBuffer(aFrameTag);
}
void NcpBase::HandleFrameRemovedFromNcpBuffer(NcpFrameBuffer::FrameTag aFrameTag)
{
if (mHostPowerStateInProgress == true)
{
if (aFrameTag == mHostPowerReplyFrameTag)
{
mHostPowerStateInProgress = false;
}
}
// Space is now available in ncp tx frame buffer.
while (mDroppedReplyTid != 0)
{
SuccessOrExit(
SendLastStatus(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | mDroppedReplyTid,
SPINEL_STATUS_NOMEM
));
mDroppedReplyTidBitSet &= ~(1 << mDroppedReplyTid);
if (mDroppedReplyTidBitSet == 0)
{
mDroppedReplyTid = 0;
break;
}
do
{
mDroppedReplyTid = SPINEL_GET_NEXT_TID(mDroppedReplyTid);
}
while ((mDroppedReplyTidBitSet & (1 << mDroppedReplyTid)) == 0);
}
if (mShouldSignalEndOfScan)
{
SuccessOrExit(
SendPropertyUpdate(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_MAC_SCAN_STATE,
SPINEL_DATATYPE_UINT8_S,
SPINEL_SCAN_STATE_IDLE
));
mShouldSignalEndOfScan = false;
}
#if OPENTHREAD_ENABLE_JAM_DETECTION
if (mShouldSignalJamStateChange)
{
SuccessOrExit(
SendPropertyUpdate(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_JAM_DETECTED,
SPINEL_DATATYPE_BOOL_S,
otJamDetectionGetState(mInstance)
));
mShouldSignalJamStateChange = false;
}
#endif // OPENTHREAD_ENABLE_JAM_DETECTION
if (mHostPowerStateHeader)
{
SuccessOrExit(
GetPropertyHandler_HOST_POWER_STATE(
mHostPowerStateHeader,
SPINEL_PROP_HOST_POWER_STATE
));
mHostPowerStateHeader = 0;
if (mHostPowerState != SPINEL_HOST_POWER_STATE_ONLINE)
{
mHostPowerReplyFrameTag = GetLastOutboundFrameTag();
mHostPowerStateInProgress = true;
}
}
UpdateChangedProps();
exit:
return;
}
bool NcpBase::ShouldWakeHost(void)
{
return (mHostPowerState != SPINEL_HOST_POWER_STATE_ONLINE && !mHostPowerStateInProgress);
}
bool NcpBase::ShouldDeferHostSend(void)
{
return (mHostPowerState == SPINEL_HOST_POWER_STATE_DEEP_SLEEP && !mHostPowerStateInProgress);
}
void NcpBase::IncrementFrameErrorCounter(void)
{
mFramingErrorCounter++;
}
// ----------------------------------------------------------------------------
// MARK: Inbound Command Handlers
// ----------------------------------------------------------------------------
otError NcpBase::HandleCommand(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, uint16_t aArgLen)
{
unsigned i;
otError error = OT_ERROR_NONE;
// Skip if this isn't a spinel frame
VerifyOrExit((SPINEL_HEADER_FLAG & aHeader) == SPINEL_HEADER_FLAG, error = OT_ERROR_INVALID_ARGS);
// We only support IID zero for now.
VerifyOrExit(
SPINEL_HEADER_GET_IID(aHeader) == 0,
error = SendLastStatus(aHeader, SPINEL_STATUS_INVALID_INTERFACE)
);
for (i = 0; i < sizeof(mCommandHandlerTable) / sizeof(mCommandHandlerTable[0]); i++)
{
if (mCommandHandlerTable[i].mCommand == aCommand)
{
break;
}
}
if (i < sizeof(mCommandHandlerTable) / sizeof(mCommandHandlerTable[0]))
{
error = (this->*mCommandHandlerTable[i].mHandler)(aHeader, aCommand, aArgPtr, aArgLen);
}
else
{
error = SendLastStatus(aHeader, SPINEL_STATUS_INVALID_COMMAND);
}
exit:
return error;
}
otError NcpBase::HandleCommandPropertyGet(uint8_t aHeader, spinel_prop_key_t aKey)
{
unsigned i;
otError error = OT_ERROR_NONE;
for (i = 0; i < sizeof(mGetPropertyHandlerTable) / sizeof(mGetPropertyHandlerTable[0]); i++)
{
if (mGetPropertyHandlerTable[i].mPropKey == aKey)
{
break;
}
}
if (i < sizeof(mGetPropertyHandlerTable) / sizeof(mGetPropertyHandlerTable[0]))
{
error = (this->*mGetPropertyHandlerTable[i].mHandler)(aHeader, aKey);
}
else
{
error = SendLastStatus(aHeader, SPINEL_STATUS_PROP_NOT_FOUND);
}
return error;
}
otError NcpBase::HandleCommandPropertySet(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
unsigned i;
otError error = OT_ERROR_NONE;
for (i = 0; i < sizeof(mSetPropertyHandlerTable) / sizeof(mSetPropertyHandlerTable[0]); i++)
{
if (mSetPropertyHandlerTable[i].mPropKey == aKey)
{
break;
}
}
if (i < sizeof(mSetPropertyHandlerTable) / sizeof(mSetPropertyHandlerTable[0]))
{
error = (this->*mSetPropertyHandlerTable[i].mHandler)(aHeader, aKey, aValuePtr, aValueLen);
}
else
{
error = SendLastStatus(aHeader, SPINEL_STATUS_PROP_NOT_FOUND);
}
return error;
}
otError NcpBase::HandleCommandPropertyInsert(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
unsigned i;
otError error = OT_ERROR_NONE;
for (i = 0; i < sizeof(mInsertPropertyHandlerTable) / sizeof(mInsertPropertyHandlerTable[0]); i++)
{
if (mInsertPropertyHandlerTable[i].mPropKey == aKey)
{
break;
}
}
if (i < sizeof(mInsertPropertyHandlerTable) / sizeof(mInsertPropertyHandlerTable[0]))
{
error = (this->*mInsertPropertyHandlerTable[i].mHandler)(aHeader, aKey, aValuePtr, aValueLen);
}
else
{
error = SendLastStatus(aHeader, SPINEL_STATUS_PROP_NOT_FOUND);
}
return error;
}
otError NcpBase::HandleCommandPropertyRemove(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
unsigned i;
otError error = OT_ERROR_NONE;
for (i = 0; i < sizeof(mRemovePropertyHandlerTable) / sizeof(mRemovePropertyHandlerTable[0]); i++)
{
if (mRemovePropertyHandlerTable[i].mPropKey == aKey)
{
break;
}
}
if (i < sizeof(mRemovePropertyHandlerTable) / sizeof(mRemovePropertyHandlerTable[0]))
{
error = (this->*mRemovePropertyHandlerTable[i].mHandler)(aHeader, aKey, aValuePtr, aValueLen);
}
else
{
error = SendLastStatus(aHeader, SPINEL_STATUS_PROP_NOT_FOUND);
}
return error;
}
// ----------------------------------------------------------------------------
// MARK: Outbound Command Handlers
// ----------------------------------------------------------------------------
otError NcpBase::SendLastStatus(uint8_t aHeader, spinel_status_t aLastStatus)
{
if (SPINEL_HEADER_GET_IID(aHeader) == 0)
{
mLastStatus = aLastStatus;
}
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_LAST_STATUS,
SPINEL_DATATYPE_UINT_PACKED_S,
aLastStatus
);
}
otError NcpBase::SendPropertyUpdate(uint8_t aHeader, uint8_t aCommand, spinel_prop_key_t aKey,
const char *aPackFormat, ...)
{
otError error = OT_ERROR_NONE;
va_list args;
va_start(args, aPackFormat);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, aCommand, aKey));
SuccessOrExit(error = OutboundFrameFeedVPacked(aPackFormat, args));
SuccessOrExit(error = OutboundFrameSend());
exit:
va_end(args);
return error;
}
otError NcpBase::SendPropertyUpdate(uint8_t aHeader, uint8_t aCommand, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, aCommand, aKey));
SuccessOrExit(error = OutboundFrameFeedData(aValuePtr, aValueLen));
SuccessOrExit(error = OutboundFrameSend());
exit:
return error;
}
otError NcpBase::SendPropertyUpdate(uint8_t aHeader, uint8_t aCommand, spinel_prop_key_t aKey, otMessage *aMessage)
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, aCommand, aKey));
SuccessOrExit(error = OutboundFrameFeedMessage(aMessage));
// Set the `aMessage` pointer to NULL to indicate that it does
// not need to be freed at the exit. The `aMessage` is now owned
// by the OutboundFrame and will be freed when the frame is either
// successfully sent and then removed, or if the frame gets
// discarded.
aMessage = NULL;
SuccessOrExit(error = OutboundFrameSend());
exit:
if (aMessage != NULL)
{
otMessageFree(aMessage);
}
return error;
}
otError NcpBase::OutboundFrameFeedVPacked(const char *aPackFormat, va_list aArgs)
{
uint8_t buf[96];
otError error = OT_ERROR_NO_BUFS;
spinel_ssize_t packedLen;
packedLen = spinel_datatype_vpack(buf, sizeof(buf), aPackFormat, aArgs);
if ((packedLen > 0) && (packedLen <= static_cast<spinel_ssize_t>(sizeof(buf))))
{
error = OutboundFrameFeedData(buf, static_cast<uint16_t>(packedLen));
}
return error;
}
otError NcpBase::OutboundFrameFeedPacked(const char *aPackFormat, ...)
{
otError error;
va_list args;
va_start(args, aPackFormat);
error = OutboundFrameFeedVPacked(aPackFormat, args);
va_end(args);
return error;
}
// ----------------------------------------------------------------------------
// MARK: Individual Command Handlers
// ----------------------------------------------------------------------------
otError NcpBase::CommandHandler_NOOP(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, uint16_t aArgLen)
{
OT_UNUSED_VARIABLE(aCommand);
OT_UNUSED_VARIABLE(aArgPtr);
OT_UNUSED_VARIABLE(aArgLen);
return SendLastStatus(aHeader, SPINEL_STATUS_OK);
}
otError NcpBase::CommandHandler_RESET(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr,
uint16_t aArgLen)
{
otError error = OT_ERROR_NONE;
// We aren't using any of the arguments to this function.
OT_UNUSED_VARIABLE(aHeader);
OT_UNUSED_VARIABLE(aCommand);
OT_UNUSED_VARIABLE(aArgPtr);
OT_UNUSED_VARIABLE(aArgLen);
// Signal a platform reset. If implemented, this function
// shouldn't return.
otInstanceReset(mInstance);
// We only get to this point if the
// platform doesn't support resetting.
// In such a case we fake it.
otThreadSetEnabled(mInstance, false);
otIp6SetEnabled(mInstance, false);
error = SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_RESET_SOFTWARE);
if (error != OT_ERROR_NONE)
{
mChangedFlags |= NCP_CHANGED_PLATFORM_RESET;
mUpdateChangedPropsTask.Post();
}
return error;
}
otError NcpBase::CommandHandler_PROP_VALUE_GET(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr,
uint16_t aArgLen)
{
unsigned int propKey = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(aArgPtr, aArgLen, SPINEL_DATATYPE_UINT_PACKED_S, &propKey);
if (parsedLength > 0)
{
error = HandleCommandPropertyGet(aHeader, static_cast<spinel_prop_key_t>(propKey));
}
else
{
error = SendLastStatus(aHeader, SPINEL_STATUS_PARSE_ERROR);
}
OT_UNUSED_VARIABLE(aCommand);
return error;
}
otError NcpBase::CommandHandler_PROP_VALUE_SET(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr,
uint16_t aArgLen)
{
unsigned int propKey = 0;
spinel_ssize_t parsedLength;
const uint8_t *valuePtr;
unsigned int valueLen;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aArgPtr,
aArgLen,
SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_DATA_S,
&propKey,
&valuePtr,
&valueLen
);
if (parsedLength == aArgLen)
{
error = HandleCommandPropertySet(
aHeader,
static_cast<spinel_prop_key_t>(propKey),
valuePtr,
static_cast<uint16_t>(valueLen)
);
}
else
{
error = SendLastStatus(aHeader, SPINEL_STATUS_PARSE_ERROR);
}
OT_UNUSED_VARIABLE(aCommand);
return error;
}
otError NcpBase::CommandHandler_PROP_VALUE_INSERT(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr,
uint16_t aArgLen)
{
unsigned int propKey = 0;
spinel_ssize_t parsedLength;
const uint8_t *valuePtr;
unsigned int valueLen;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aArgPtr,
aArgLen,
SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_DATA_S,
&propKey,
&valuePtr,
&valueLen
);
if (parsedLength == aArgLen)
{
error = HandleCommandPropertyInsert(
aHeader,
static_cast<spinel_prop_key_t>(propKey),
valuePtr,
static_cast<uint16_t>(valueLen)
);
}
else
{
error = SendLastStatus(aHeader, SPINEL_STATUS_PARSE_ERROR);
}
OT_UNUSED_VARIABLE(aCommand);
return error;
}
otError NcpBase::CommandHandler_PROP_VALUE_REMOVE(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr,
uint16_t aArgLen)
{
unsigned int propKey = 0;
spinel_ssize_t parsedLength;
const uint8_t *valuePtr;
unsigned int valueLen;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aArgPtr,
aArgLen,
SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_DATA_S,
&propKey,
&valuePtr,
&valueLen
);
if (parsedLength == aArgLen)
{
error = HandleCommandPropertyRemove(
aHeader,
static_cast<spinel_prop_key_t>(propKey),
valuePtr,
static_cast<uint16_t>(valueLen)
);
}
else
{
error = SendLastStatus(aHeader, SPINEL_STATUS_PARSE_ERROR);
}
OT_UNUSED_VARIABLE(aCommand);
return error;
}
otError NcpBase::CommandHandler_NET_SAVE(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr,
uint16_t aArgLen)
{
OT_UNUSED_VARIABLE(aCommand);
OT_UNUSED_VARIABLE(aArgPtr);
OT_UNUSED_VARIABLE(aArgLen);
return SendLastStatus(aHeader, SPINEL_STATUS_UNIMPLEMENTED);
}
otError NcpBase::CommandHandler_NET_CLEAR(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr,
uint16_t aArgLen)
{
OT_UNUSED_VARIABLE(aCommand);
OT_UNUSED_VARIABLE(aArgPtr);
OT_UNUSED_VARIABLE(aArgLen);
return SendLastStatus(aHeader, ThreadErrorToSpinelStatus(otInstanceErasePersistentInfo(mInstance)));
}
otError NcpBase::CommandHandler_NET_RECALL(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr,
uint16_t aArgLen)
{
OT_UNUSED_VARIABLE(aCommand);
OT_UNUSED_VARIABLE(aArgPtr);
OT_UNUSED_VARIABLE(aArgLen);
return SendLastStatus(aHeader, SPINEL_STATUS_UNIMPLEMENTED);
}
#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
otError NcpBase::CommandHandler_PEEK(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, uint16_t aArgLen)
{
spinel_ssize_t parsedLength;
uint32_t address;
uint16_t count;
spinel_status_t spinelError = SPINEL_STATUS_OK;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aArgPtr,
aArgLen,
(
SPINEL_DATATYPE_UINT32_S // Address
SPINEL_DATATYPE_UINT16_S // Count
),
&address,
&count
);
VerifyOrExit(parsedLength == aArgLen, spinelError = SPINEL_STATUS_PARSE_ERROR);
VerifyOrExit(count != 0, spinelError = SPINEL_STATUS_INVALID_ARGUMENT);
if (mAllowPeekDelegate != NULL)
{
VerifyOrExit(mAllowPeekDelegate(address, count), spinelError = SPINEL_STATUS_INVALID_ARGUMENT);
}
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
(
SPINEL_DATATYPE_COMMAND_S // Header and Command
SPINEL_DATATYPE_UINT32_S // Address
SPINEL_DATATYPE_UINT16_S // Count
),
aHeader,
SPINEL_CMD_PEEK_RET,
address,
count
));
SuccessOrExit(error = OutboundFrameFeedData(reinterpret_cast<const uint8_t *>(address), count));
SuccessOrExit(error = OutboundFrameSend());
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
OT_UNUSED_VARIABLE(aCommand);
return error;
}
otError NcpBase::CommandHandler_POKE(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, uint16_t aArgLen)
{
spinel_ssize_t parsedLength;
uint32_t address;
uint16_t count;
const uint8_t *dataPtr = NULL;
spinel_size_t dataLen;
spinel_status_t spinelError = SPINEL_STATUS_OK;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aArgPtr,
aArgLen,
(
SPINEL_DATATYPE_UINT32_S // Address
SPINEL_DATATYPE_UINT16_S // Count
SPINEL_DATATYPE_DATA_S // Bytes
),
&address,
&count,
&dataPtr,
&dataLen
);
VerifyOrExit(parsedLength == aArgLen, spinelError = SPINEL_STATUS_PARSE_ERROR);
VerifyOrExit(count != 0, spinelError = SPINEL_STATUS_INVALID_ARGUMENT);
VerifyOrExit(count <= dataLen, spinelError = SPINEL_STATUS_INVALID_ARGUMENT);
if (mAllowPokeDelegate != NULL)
{
VerifyOrExit(mAllowPokeDelegate(address, count), spinelError = SPINEL_STATUS_INVALID_ARGUMENT);
}
memcpy(reinterpret_cast<uint8_t *>(address), dataPtr, count);
exit:
error = SendLastStatus(aHeader, spinelError);
OT_UNUSED_VARIABLE(aCommand);
return error;
}
#endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
// ----------------------------------------------------------------------------
// MARK: Individual Property Getters
// ----------------------------------------------------------------------------
otError NcpBase::GetPropertyHandler_LAST_STATUS(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(aHeader, SPINEL_CMD_PROP_VALUE_IS, aKey, SPINEL_DATATYPE_UINT_PACKED_S, mLastStatus);
}
otError NcpBase::GetPropertyHandler_PROTOCOL_VERSION(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
(
SPINEL_DATATYPE_UINT_PACKED_S // Major
SPINEL_DATATYPE_UINT_PACKED_S // Minor
),
SPINEL_PROTOCOL_VERSION_THREAD_MAJOR,
SPINEL_PROTOCOL_VERSION_THREAD_MINOR
);
}
otError NcpBase::GetPropertyHandler_INTERFACE_TYPE(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT_PACKED_S,
SPINEL_PROTOCOL_TYPE_THREAD
);
}
otError NcpBase::GetPropertyHandler_VENDOR_ID(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT_PACKED_S,
0 // Vendor ID. Zero for unknown.
);
}
otError NcpBase::GetPropertyHandler_CAPS(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey
));
// Begin adding capabilities //////////////////////////////////////////////
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_NET_THREAD_1_0));
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_COUNTERS));
#if OPENTHREAD_ENABLE_MAC_WHITELIST
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_MAC_WHITELIST));
#endif
#if OPENTHREAD_ENABLE_RAW_LINK_API
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_MAC_RAW));
#endif
#if OPENTHREAD_ENABLE_JAM_DETECTION
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_JAM_DETECT));
#endif
#if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_OOB_STEERING_DATA));
#endif
#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_PEEK_POKE));
#endif
// TODO: Somehow get the following capability from the radio.
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_802_15_4_2450MHZ_OQPSK));
#if OPENTHREAD_CONFIG_MAX_CHILDREN > 0
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_ROLE_ROUTER));
#endif
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_ROLE_SLEEPY));
#if OPENTHREAD_ENABLE_LEGACY
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_NEST_LEGACY_INTERFACE));
#endif
#if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_THREAD_TMF_PROXY));
#endif
// End adding capabilities /////////////////////////////////////////////////
SuccessOrExit(error = OutboundFrameSend());
exit:
return error;
}
otError NcpBase::GetPropertyHandler_NCP_VERSION(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UTF8_S,
otGetVersionString()
);
}
otError NcpBase::GetPropertyHandler_INTERFACE_COUNT(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
1 // Only one interface for now
);
}
otError NcpBase::GetPropertyHandler_POWER_STATE(uint8_t aHeader, spinel_prop_key_t aKey)
{
// Always online at the moment
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
SPINEL_POWER_STATE_ONLINE
);
}
otError NcpBase::GetPropertyHandler_HWADDR(uint8_t aHeader, spinel_prop_key_t aKey)
{
otExtAddress hwAddr;
otLinkGetFactoryAssignedIeeeEui64(mInstance, &hwAddr);
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_EUI64_S,
hwAddr.m8
);
}
otError NcpBase::GetPropertyHandler_LOCK(uint8_t aHeader, spinel_prop_key_t aKey)
{
// TODO: Implement property lock (Needs API!)
OT_UNUSED_VARIABLE(aKey);
return SendLastStatus(aHeader, SPINEL_STATUS_UNIMPLEMENTED);
}
otError NcpBase::GetPropertyHandler_HOST_POWER_STATE(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
mHostPowerState
);
}
otError NcpBase::GetPropertyHandler_PHY_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
#if OPENTHREAD_ENABLE_RAW_LINK_API
otLinkRawIsEnabled(mInstance)
#else
false
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
);
}
otError NcpBase::GetPropertyHandler_PHY_FREQ(uint8_t aHeader, spinel_prop_key_t aKey)
{
uint32_t freq_khz(0);
const uint8_t chan(otLinkGetChannel(mInstance));
if (chan == 0)
{
freq_khz = 868300;
}
else if (chan < 11)
{
freq_khz = 906000 - (2000 * 1) + 2000 * (chan);
}
else if (chan < 26)
{
freq_khz = 2405000 - (5000 * 11) + 5000 * (chan);
}
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT32_S,
freq_khz
);
}
otError NcpBase::GetPropertyHandler_PHY_CHAN_SUPPORTED(uint8_t aHeader, spinel_prop_key_t aKey)
{
return GetPropertyHandler_ChannelMaskHelper(aHeader, aKey, mSupportedChannelMask);
}
otError NcpBase::GetPropertyHandler_PHY_CHAN(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
otLinkGetChannel(mInstance)
);
}
otError NcpBase::GetPropertyHandler_PHY_RSSI(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_INT8_S,
otPlatRadioGetRssi(mInstance)
);
}
otError NcpBase::GetPropertyHandler_PHY_TX_POWER(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_INT8_S,
otLinkGetMaxTransmitPower(mInstance)
);
}
otError NcpBase::GetPropertyHandler_PHY_RX_SENSITIVITY(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_INT8_S,
otPlatRadioGetReceiveSensitivity(mInstance)
);
}
otError NcpBase::GetPropertyHandler_MAC_SCAN_STATE(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
#if OPENTHREAD_ENABLE_RAW_LINK_API
if (otLinkRawIsEnabled(mInstance))
{
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
mCurScanChannel == NCP_INVALID_SCAN_CHANNEL
? SPINEL_SCAN_STATE_IDLE
: SPINEL_SCAN_STATE_ENERGY
);
}
else
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
{
if (otLinkIsActiveScanInProgress(mInstance))
{
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
SPINEL_SCAN_STATE_BEACON
);
}
else if (otLinkIsEnergyScanInProgress(mInstance))
{
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
SPINEL_SCAN_STATE_ENERGY
);
}
else if (otThreadIsDiscoverInProgress(mInstance))
{
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
SPINEL_SCAN_STATE_DISCOVER
);
}
else
{
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
SPINEL_SCAN_STATE_IDLE
);
}
}
return error;
}
otError NcpBase::GetPropertyHandler_MAC_SCAN_PERIOD(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT16_S,
mScanPeriod
);
}
otError NcpBase::GetPropertyHandler_ChannelMaskHelper(uint8_t aHeader, spinel_prop_key_t aKey, uint32_t aChannelMask)
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey
));
for (int i = 0; i < 32; i++)
{
if (0 != (aChannelMask & (1 << i)))
{
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT8_S, i));
}
}
SuccessOrExit(error = OutboundFrameSend());
exit:
return error;
}
otError NcpBase::GetPropertyHandler_MAC_SCAN_MASK(uint8_t aHeader, spinel_prop_key_t aKey)
{
return GetPropertyHandler_ChannelMaskHelper(aHeader, aKey, mChannelMask);
}
otError NcpBase::GetPropertyHandler_MAC_15_4_PANID(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT16_S,
otLinkGetPanId(mInstance)
);
}
otError NcpBase::GetPropertyHandler_MAC_PROMISCUOUS_MODE(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_INT8_S,
otPlatRadioGetPromiscuous(mInstance)
? SPINEL_MAC_PROMISCUOUS_MODE_FULL
: SPINEL_MAC_PROMISCUOUS_MODE_OFF
);
}
otError NcpBase::GetPropertyHandler_MAC_15_4_LADDR(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_EUI64_S,
otLinkGetExtendedAddress(mInstance)
);
}
otError NcpBase::GetPropertyHandler_MAC_15_4_SADDR(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT16_S,
otLinkGetShortAddress(mInstance)
);
}
otError NcpBase::GetPropertyHandler_MAC_EXTENDED_ADDR(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_EUI64_S,
otLinkGetExtendedAddress(mInstance)
);
}
otError NcpBase::GetPropertyHandler_MAC_DATA_POLL_PERIOD(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT32_S,
otLinkGetPollPeriod(mInstance)
);
}
otError NcpBase::GetPropertyHandler_MAC_RAW_STREAM_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
mIsRawStreamEnabled
);
}
otError NcpBase::GetPropertyHandler_NET_SAVED(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
otDatasetIsCommissioned(mInstance)
);
}
otError NcpBase::GetPropertyHandler_NET_IF_UP(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
otIp6IsEnabled(mInstance)
);
}
otError NcpBase::GetPropertyHandler_NET_STACK_UP(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED
);
}
otError NcpBase::GetPropertyHandler_NET_ROLE(uint8_t aHeader, spinel_prop_key_t aKey)
{
spinel_net_role_t role(SPINEL_NET_ROLE_DETACHED);
switch (otThreadGetDeviceRole(mInstance))
{
case OT_DEVICE_ROLE_DISABLED:
case OT_DEVICE_ROLE_DETACHED:
role = SPINEL_NET_ROLE_DETACHED;
break;
case OT_DEVICE_ROLE_CHILD:
role = SPINEL_NET_ROLE_CHILD;
break;
case OT_DEVICE_ROLE_ROUTER:
role = SPINEL_NET_ROLE_ROUTER;
break;
case OT_DEVICE_ROLE_LEADER:
role = SPINEL_NET_ROLE_LEADER;
break;
}
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
role
);
}
otError NcpBase::GetPropertyHandler_NET_NETWORK_NAME(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UTF8_S,
otThreadGetNetworkName(mInstance)
);
}
otError NcpBase::GetPropertyHandler_NET_XPANID(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_DATA_S,
otThreadGetExtendedPanId(mInstance),
sizeof(spinel_net_xpanid_t)
);
}
otError NcpBase::GetPropertyHandler_NET_MASTER_KEY(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_DATA_S,
otThreadGetMasterKey(mInstance)->m8,
OT_MASTER_KEY_SIZE
);
}
otError NcpBase::GetPropertyHandler_NET_KEY_SEQUENCE_COUNTER(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT32_S,
otThreadGetKeySequenceCounter(mInstance)
);
}
otError NcpBase::GetPropertyHandler_NET_PARTITION_ID(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT32_S,
otThreadGetPartitionId(mInstance)
);
}
otError NcpBase::GetPropertyHandler_NET_KEY_SWITCH_GUARDTIME(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT32_S,
otThreadGetKeySwitchGuardTime(mInstance)
);
}
otError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA_VERSION(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
otNetDataGetVersion(mInstance)
);
}
otError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA_VERSION(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
otNetDataGetStableVersion(mInstance)
);
}
#if OPENTHREAD_ENABLE_BORDER_ROUTER
otError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
uint8_t networkData[255];
uint8_t networkDataLen = 255;
otBorderRouterGetNetData(
mInstance,
false, // Stable?
networkData,
&networkDataLen
);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey
));
SuccessOrExit(error = OutboundFrameFeedData(networkData, networkDataLen));
SuccessOrExit(error = OutboundFrameSend());
exit:
return error;
}
otError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
uint8_t networkData[255];
uint8_t networkDataLen = 255;
otBorderRouterGetNetData(
mInstance,
true, // Stable?
networkData,
&networkDataLen
);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey
));
SuccessOrExit(error = OutboundFrameFeedData(networkData, networkDataLen));
SuccessOrExit(error = OutboundFrameSend());
exit:
return error;
}
#endif // OPENTHREAD_ENABLE_BORDER_ROUTER
otError NcpBase::GetPropertyHandler_THREAD_LEADER_NETWORK_DATA(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
uint8_t networkData[255];
uint8_t networkDataLen = 255;
otNetDataGet(
mInstance,
false, // Stable?
networkData,
&networkDataLen
);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey
));
SuccessOrExit(error = OutboundFrameFeedData(networkData, networkDataLen));
SuccessOrExit(error = OutboundFrameSend());
exit:
return error;
}
otError NcpBase::GetPropertyHandler_THREAD_STABLE_LEADER_NETWORK_DATA(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
uint8_t networkData[255];
uint8_t networkDataLen = 255;
otNetDataGet(
mInstance,
true, // Stable?
networkData,
&networkDataLen
);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey
));
SuccessOrExit(error = OutboundFrameFeedData(networkData, networkDataLen));
SuccessOrExit(error = OutboundFrameSend());
exit:
return error;
}
otError NcpBase::GetPropertyHandler_THREAD_LEADER_RID(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
otThreadGetLeaderRouterId(mInstance)
);
}
#if OPENTHREAD_FTD
otError NcpBase::GetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
otThreadGetLocalLeaderWeight(mInstance)
);
}
#endif // OPENTHREAD_FTD
otError NcpBase::GetPropertyHandler_THREAD_LEADER_WEIGHT(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
otThreadGetLeaderWeight(mInstance)
);
}
otError NcpBase::GetPropertyHandler_THREAD_LEADER_ADDR(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
otIp6Address address;
error = otThreadGetLeaderRloc(mInstance, &address);
if (error == OT_ERROR_NONE)
{
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_IPv6ADDR_S,
&address
);
}
else
{
error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error));
}
return error;
}
otError NcpBase::GetPropertyHandler_THREAD_PARENT(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
otRouterInfo parentInfo;
error = otThreadGetParentInfo(mInstance, &parentInfo);
if (error == OT_ERROR_NONE)
{
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
(
SPINEL_DATATYPE_EUI64_S // Parent's extended address
SPINEL_DATATYPE_UINT16_S // Parent's rloc16
),
parentInfo.mExtAddress.m8,
parentInfo.mRloc16
);
}
else
{
error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error));
}
return error;
}
#if OPENTHREAD_FTD
otError NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
otChildInfo childInfo;
uint8_t maxChildren;
uint8_t modeFlags;
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey
));
maxChildren = otThreadGetMaxAllowedChildren(mInstance);
for (uint8_t index = 0; index < maxChildren; index++)
{
error = otThreadGetChildInfoByIndex(mInstance, index, &childInfo);
if (error != OT_ERROR_NONE)
{
continue;
}
modeFlags = LinkFlagsToFlagByte(childInfo.mRxOnWhenIdle,
childInfo.mSecureDataRequest,
childInfo.mFullFunction,
childInfo.mFullNetworkData);
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_EUI64_S // EUI64 Address
SPINEL_DATATYPE_UINT16_S // Rloc16
SPINEL_DATATYPE_UINT32_S // Timeout
SPINEL_DATATYPE_UINT32_S // Age
SPINEL_DATATYPE_UINT8_S // Network Data Version
SPINEL_DATATYPE_UINT8_S // Link Quality In
SPINEL_DATATYPE_INT8_S // Average RSS
SPINEL_DATATYPE_UINT8_S // Mode (flags)
SPINEL_DATATYPE_INT8_S // Most recent RSS
),
childInfo.mExtAddress.m8,
childInfo.mRloc16,
childInfo.mTimeout,
childInfo.mAge,
childInfo.mNetworkDataVersion,
childInfo.mLinkQualityIn,
childInfo.mAverageRssi,
modeFlags,
childInfo.mLastRssi
));
}
SuccessOrExit(error = OutboundFrameSend());
exit:
mDisableStreamWrite = false;
return error;
}
#endif // OPENTHREAD_FTD
otError NcpBase::GetPropertyHandler_THREAD_NEIGHBOR_TABLE(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
otNeighborInfoIterator iter = OT_NEIGHBOR_INFO_ITERATOR_INIT;
otNeighborInfo neighInfo;
uint8_t modeFlags;
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, SPINEL_CMD_PROP_VALUE_IS,
aKey));
while (otThreadGetNextNeighborInfo(mInstance, &iter, &neighInfo) == OT_ERROR_NONE)
{
modeFlags = LinkFlagsToFlagByte(neighInfo.mRxOnWhenIdle,
neighInfo.mSecureDataRequest,
neighInfo.mFullFunction,
neighInfo.mFullNetworkData);
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_EUI64_S // EUI64 Address
SPINEL_DATATYPE_UINT16_S // Rloc16
SPINEL_DATATYPE_UINT32_S // Age
SPINEL_DATATYPE_UINT8_S // Link Quality In
SPINEL_DATATYPE_INT8_S // Average RSS
SPINEL_DATATYPE_UINT8_S // Mode (flags)
SPINEL_DATATYPE_BOOL_S // Is Child
SPINEL_DATATYPE_UINT32_S // Link Frame Counter
SPINEL_DATATYPE_UINT32_S // MLE Frame Counter
SPINEL_DATATYPE_INT8_S // Most recent RSS
),
neighInfo.mExtAddress.m8,
neighInfo.mRloc16,
neighInfo.mAge,
neighInfo.mLinkQualityIn,
neighInfo.mAverageRssi,
modeFlags,
neighInfo.mIsChild,
neighInfo.mLinkFrameCounter,
neighInfo.mMleFrameCounter,
neighInfo.mLastRssi
));
}
SuccessOrExit(error = OutboundFrameSend());
exit:
mDisableStreamWrite = false;
return error;
}
otError NcpBase::GetPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
uint8_t numEntries = 0;
const uint16_t *ports = otIp6GetUnsecurePorts(mInstance, &numEntries);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, SPINEL_CMD_PROP_VALUE_IS,
aKey));
for (; numEntries != 0; ports++, numEntries--)
{
SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT16_S, *ports));
}
SuccessOrExit(error = OutboundFrameSend());
exit:
return error;
}
otError NcpBase::GetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
mAllowLocalNetworkDataChange
);
}
#if OPENTHREAD_FTD
otError NcpBase::GetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
otThreadIsRouterRoleEnabled(mInstance)
);
}
#endif // OPENTHREAD_FTD
otError NcpBase::GetPropertyHandler_THREAD_ON_MESH_NETS(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
otBorderRouterConfig borderRouterConfig;
uint8_t flags;
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey
));
// Fill from non-local network data first
for (otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT ;;)
{
error = otNetDataGetNextOnMeshPrefix(mInstance, &iter, &borderRouterConfig);
if (error != OT_ERROR_NONE)
{
break;
}
flags = BorderRouterConfigToFlagByte(borderRouterConfig);
SuccessOrExit(error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_IPv6ADDR_S // IPv6 Prefix
SPINEL_DATATYPE_UINT8_S // Prefix Length (in bits)
SPINEL_DATATYPE_BOOL_S // isStable
SPINEL_DATATYPE_UINT8_S // Flags
SPINEL_DATATYPE_BOOL_S // isLocal
),
&borderRouterConfig.mPrefix,
64,
borderRouterConfig.mStable,
flags,
false
));
}
#if OPENTHREAD_ENABLE_BORDER_ROUTER
// Fill from local network data last
for (otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT ;;)
{
error = otBorderRouterGetNextOnMeshPrefix(mInstance, &iter, &borderRouterConfig);
if (error != OT_ERROR_NONE)
{
break;
}
flags = BorderRouterConfigToFlagByte(borderRouterConfig);
SuccessOrExit(error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_IPv6ADDR_S // IPv6 Prefix
SPINEL_DATATYPE_UINT8_S // Prefix Length (in bits)
SPINEL_DATATYPE_BOOL_S // isStable
SPINEL_DATATYPE_UINT8_S // Flags
SPINEL_DATATYPE_BOOL_S // isLocal
),
&borderRouterConfig.mPrefix,
64,
borderRouterConfig.mStable,
flags,
true
));
}
#endif // OPENTHREAD_ENABLE_BORDER_ROUTER
SuccessOrExit(error = OutboundFrameSend());
exit:
mDisableStreamWrite = false;
return error;
}
otError NcpBase::GetPropertyHandler_THREAD_DISCOVERY_SCAN_JOINER_FLAG(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
mDiscoveryScanJoinerFlag
);
}
otError NcpBase::GetPropertyHandler_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
mDiscoveryScanEnableFiltering
);
}
otError NcpBase::GetPropertyHandler_THREAD_DISCOVERY_SCAN_PANID(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT16_S,
mDiscoveryScanPanId
);
}
otError NcpBase::GetPropertyHandler_IPV6_ML_PREFIX(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
const uint8_t *mlPrefix = otThreadGetMeshLocalPrefix(mInstance);
if (mlPrefix)
{
otIp6Address addr;
memcpy(addr.mFields.m8, mlPrefix, 8);
// Zero out the last 8 bytes.
memset(addr.mFields.m8 + 8, 0, 8);
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
(
SPINEL_DATATYPE_IPv6ADDR_S // Mesh-local IPv6 address
SPINEL_DATATYPE_UINT8_S // Prefix length (in bits)
),
&addr,
64
);
}
else
{
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_VOID_S
);
}
return error;
}
otError NcpBase::GetPropertyHandler_IPV6_ML_ADDR(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
const otIp6Address *ml64 = otThreadGetMeshLocalEid(mInstance);
if (ml64)
{
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_IPv6ADDR_S,
ml64
);
}
else
{
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_VOID_S
);
}
return error;
}
otError NcpBase::GetPropertyHandler_IPV6_LL_ADDR(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
const otIp6Address *address = otThreadGetLinkLocalIp6Address(mInstance);
if (address)
{
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_IPv6ADDR_S,
address
);
}
else
{
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_VOID_S
);
}
return error;
}
otError NcpBase::GetPropertyHandler_IPV6_ADDRESS_TABLE(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey
));
for (const otNetifAddress *address = otIp6GetUnicastAddresses(mInstance); address; address = address->mNext)
{
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_IPv6ADDR_S // IPv6 address
SPINEL_DATATYPE_UINT8_S // Prefix length (in bits)
SPINEL_DATATYPE_UINT32_S // Preferred lifetime
SPINEL_DATATYPE_UINT32_S // Valid lifetime
),
&address->mAddress,
address->mPrefixLength,
address->mPreferred ? 0xffffffff : 0,
address->mValid ? 0xffffffff : 0
));
}
SuccessOrExit(error = OutboundFrameSend());
exit:
mDisableStreamWrite = false;
return error;
}
otError NcpBase::GetPropertyHandler_IPV6_ROUTE_TABLE(uint8_t aHeader, spinel_prop_key_t aKey)
{
// TODO: Implement get route table
OT_UNUSED_VARIABLE(aKey);
return SendLastStatus(aHeader, SPINEL_STATUS_UNIMPLEMENTED);
}
otError NcpBase::GetPropertyHandler_IPV6_ICMP_PING_OFFLOAD(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
otIcmp6IsEchoEnabled(mInstance)
);
}
otError NcpBase::GetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU(uint8_t aHeader, spinel_prop_key_t aKey)
{
// Note reverse logic: passthru enabled = filter disabled
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
!otIp6IsReceiveFilterEnabled(mInstance)
);
}
otError NcpBase::GetPropertyHandler_THREAD_OFF_MESH_ROUTES(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
otExternalRouteConfig external_route_config;
otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT;
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey
));
while (otNetDataGetNextRoute(mInstance, &iter, &external_route_config) == OT_ERROR_NONE)
{
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_IPv6ADDR_S // IPv6 Prefix
SPINEL_DATATYPE_UINT8_S // Prefix Length (in bits)
SPINEL_DATATYPE_BOOL_S // IsStable
SPINEL_DATATYPE_UINT8_S // Route Preference Flags
SPINEL_DATATYPE_BOOL_S // IsLocal
SPINEL_DATATYPE_BOOL_S // NextHopIsThisDevice
),
&external_route_config.mPrefix.mPrefix,
external_route_config.mPrefix.mLength,
external_route_config.mStable,
ExternalRoutePreferenceToFlagByte(external_route_config.mPreference),
false,
external_route_config.mNextHopIsThisDevice
));
}
#if OPENTHREAD_ENABLE_BORDER_ROUTER
while (otBorderRouterGetNextRoute(mInstance, &iter, &external_route_config) == OT_ERROR_NONE)
{
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_IPv6ADDR_S // IPv6 Prefix
SPINEL_DATATYPE_UINT8_S // Prefix Length (in bits)
SPINEL_DATATYPE_BOOL_S // IsStable
SPINEL_DATATYPE_UINT8_S // Route Preference Flags
SPINEL_DATATYPE_BOOL_S // IsLocal
SPINEL_DATATYPE_BOOL_S // NextHopIsThisDevice
),
&external_route_config.mPrefix.mPrefix,
external_route_config.mPrefix.mLength,
external_route_config.mStable,
ExternalRoutePreferenceToFlagByte(external_route_config.mPreference),
true,
external_route_config.mNextHopIsThisDevice
));
}
#endif // OPENTHREAD_ENABLE_BORDER_ROUTER
SuccessOrExit(error = OutboundFrameSend());
exit:
mDisableStreamWrite = false;
return error;
}
otError NcpBase::GetPropertyHandler_STREAM_NET(uint8_t aHeader, spinel_prop_key_t aKey)
{
// TODO: Implement explicit data poll.
OT_UNUSED_VARIABLE(aKey);
return SendLastStatus(aHeader, SPINEL_STATUS_UNIMPLEMENTED);
}
#if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD
otError NcpBase::GetPropertyHandler_THREAD_TMF_PROXY_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
otTmfProxyIsEnabled(mInstance)
);
}
#endif // OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_JAM_DETECTION
otError NcpBase::GetPropertyHandler_JAM_DETECT_ENABLE(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
otJamDetectionIsEnabled(mInstance)
);
}
otError NcpBase::GetPropertyHandler_JAM_DETECTED(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
otJamDetectionGetState(mInstance)
);
}
otError NcpBase::GetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_INT8_S,
otJamDetectionGetRssiThreshold(mInstance)
);
}
otError NcpBase::GetPropertyHandler_JAM_DETECT_WINDOW(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
otJamDetectionGetWindow(mInstance)
);
}
otError NcpBase::GetPropertyHandler_JAM_DETECT_BUSY(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
otJamDetectionGetBusyPeriod(mInstance)
);
}
otError NcpBase::GetPropertyHandler_JAM_DETECT_HISTORY_BITMAP(uint8_t aHeader, spinel_prop_key_t aKey)
{
uint64_t historyBitmap = otJamDetectionGetHistoryBitmap(mInstance);
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
(
SPINEL_DATATYPE_UINT32_S // History bitmap - bits 0-31
SPINEL_DATATYPE_UINT32_S // History bitmap - bits 32-63
),
static_cast<uint32_t>(historyBitmap & 0xffffffff),
static_cast<uint32_t>(historyBitmap >> 32)
);
}
#endif // OPENTHREAD_ENABLE_JAM_DETECTION
otError NcpBase::GetPropertyHandler_MAC_CNTR(uint8_t aHeader, spinel_prop_key_t aKey)
{
uint32_t value;
const otMacCounters *macCounters;
otError error = OT_ERROR_NONE;
macCounters = otLinkGetCounters(mInstance);
assert(macCounters != NULL);
switch (aKey)
{
case SPINEL_PROP_CNTR_TX_PKT_TOTAL:
value = macCounters->mTxTotal;
break;
case SPINEL_PROP_CNTR_TX_PKT_ACK_REQ:
value = macCounters->mTxAckRequested;
break;
case SPINEL_PROP_CNTR_TX_PKT_ACKED:
value = macCounters->mTxAcked;
break;
case SPINEL_PROP_CNTR_TX_PKT_NO_ACK_REQ:
value = macCounters->mTxNoAckRequested;
break;
case SPINEL_PROP_CNTR_TX_PKT_DATA:
value = macCounters->mTxData;
break;
case SPINEL_PROP_CNTR_TX_PKT_DATA_POLL:
value = macCounters->mTxDataPoll;
break;
case SPINEL_PROP_CNTR_TX_PKT_BEACON:
value = macCounters->mTxBeacon;
break;
case SPINEL_PROP_CNTR_TX_PKT_BEACON_REQ:
value = macCounters->mTxBeaconRequest;
break;
case SPINEL_PROP_CNTR_TX_PKT_OTHER:
value = macCounters->mTxOther;
break;
case SPINEL_PROP_CNTR_TX_PKT_RETRY:
value = macCounters->mTxRetry;
break;
case SPINEL_PROP_CNTR_TX_ERR_CCA:
value = macCounters->mTxErrCca;
break;
case SPINEL_PROP_CNTR_TX_PKT_UNICAST:
value = macCounters->mTxUnicast;
break;
case SPINEL_PROP_CNTR_TX_PKT_BROADCAST:
value = macCounters->mTxBroadcast;
break;
case SPINEL_PROP_CNTR_TX_ERR_ABORT:
value = macCounters->mTxErrAbort;
break;
case SPINEL_PROP_CNTR_RX_PKT_TOTAL:
value = macCounters->mRxTotal;
break;
case SPINEL_PROP_CNTR_RX_PKT_DATA:
value = macCounters->mRxData;
break;
case SPINEL_PROP_CNTR_RX_PKT_DATA_POLL:
value = macCounters->mRxDataPoll;
break;
case SPINEL_PROP_CNTR_RX_PKT_BEACON:
value = macCounters->mRxBeacon;
break;
case SPINEL_PROP_CNTR_RX_PKT_BEACON_REQ:
value = macCounters->mRxBeaconRequest;
break;
case SPINEL_PROP_CNTR_RX_PKT_OTHER:
value = macCounters->mRxOther;
break;
case SPINEL_PROP_CNTR_RX_PKT_FILT_WL:
value = macCounters->mRxWhitelistFiltered;
break;
case SPINEL_PROP_CNTR_RX_PKT_FILT_DA:
value = macCounters->mRxDestAddrFiltered;
break;
case SPINEL_PROP_CNTR_RX_PKT_DUP:
value = macCounters->mRxDuplicated;
break;
case SPINEL_PROP_CNTR_RX_PKT_UNICAST:
value = macCounters->mRxUnicast;
break;
case SPINEL_PROP_CNTR_RX_PKT_BROADCAST:
value = macCounters->mRxBroadcast;
break;
case SPINEL_PROP_CNTR_RX_ERR_EMPTY:
value = macCounters->mRxErrNoFrame;
break;
case SPINEL_PROP_CNTR_RX_ERR_UKWN_NBR:
value = macCounters->mRxErrUnknownNeighbor;
break;
case SPINEL_PROP_CNTR_RX_ERR_NVLD_SADDR:
value = macCounters->mRxErrInvalidSrcAddr;
break;
case SPINEL_PROP_CNTR_RX_ERR_SECURITY:
value = macCounters->mRxErrSec;
break;
case SPINEL_PROP_CNTR_RX_ERR_BAD_FCS:
value = macCounters->mRxErrFcs;
break;
case SPINEL_PROP_CNTR_RX_ERR_OTHER:
value = macCounters->mRxErrOther;
break;
default:
error = SendLastStatus(aHeader, SPINEL_STATUS_INTERNAL_ERROR);
ExitNow();
}
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT32_S,
value
);
exit:
return error;
}
otError NcpBase::GetPropertyHandler_NCP_CNTR(uint8_t aHeader, spinel_prop_key_t aKey)
{
uint32_t value;
otError error = OT_ERROR_NONE;
switch (aKey)
{
case SPINEL_PROP_CNTR_TX_IP_SEC_TOTAL:
value = mInboundSecureIpFrameCounter;
break;
case SPINEL_PROP_CNTR_TX_IP_INSEC_TOTAL:
value = mInboundInsecureIpFrameCounter;
break;
case SPINEL_PROP_CNTR_TX_IP_DROPPED:
value = mDroppedInboundIpFrameCounter;
break;
case SPINEL_PROP_CNTR_RX_IP_SEC_TOTAL:
value = mOutboundSecureIpFrameCounter;
break;
case SPINEL_PROP_CNTR_RX_IP_INSEC_TOTAL:
value = mOutboundInsecureIpFrameCounter;
break;
case SPINEL_PROP_CNTR_RX_IP_DROPPED:
value = mDroppedOutboundIpFrameCounter;
break;
case SPINEL_PROP_CNTR_TX_SPINEL_TOTAL:
value = mTxSpinelFrameCounter;
break;
case SPINEL_PROP_CNTR_RX_SPINEL_TOTAL:
value = mRxSpinelFrameCounter;
break;
case SPINEL_PROP_CNTR_RX_SPINEL_OUT_OF_ORDER_TID:
value = mRxSpinelOutOfOrderTidCounter;
break;
case SPINEL_PROP_CNTR_RX_SPINEL_ERR:
value = mFramingErrorCounter;
break;
default:
error = SendLastStatus(aHeader, SPINEL_STATUS_INTERNAL_ERROR);
ExitNow();
}
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT32_S,
value
);
exit:
return error;
}
otError NcpBase::GetPropertyHandler_IP_CNTR(uint8_t aHeader, spinel_prop_key_t aKey)
{
uint32_t value;
otError error = OT_ERROR_NONE;
const otIpCounters *counters = otThreadGetIp6Counters(mInstance);
switch (aKey)
{
case SPINEL_PROP_CNTR_IP_TX_SUCCESS:
value = counters->mTxSuccess;
break;
case SPINEL_PROP_CNTR_IP_RX_SUCCESS:
value = counters->mRxSuccess;
break;
case SPINEL_PROP_CNTR_IP_TX_FAILURE:
value = counters->mTxFailure;
break;
case SPINEL_PROP_CNTR_IP_RX_FAILURE:
value = counters->mRxFailure;
break;
default:
error = SendLastStatus(aHeader, SPINEL_STATUS_INTERNAL_ERROR);
ExitNow();
break;
}
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT32_S,
value
);
exit:
return error;
}
otError NcpBase::GetPropertyHandler_MSG_BUFFER_COUNTERS(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
otBufferInfo bufferInfo;
otMessageGetBufferInfo(mInstance, &bufferInfo);
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey
));
SuccessOrExit(
error = OutboundFrameFeedPacked(
(
SPINEL_DATATYPE_UINT16_S // Total buffers
SPINEL_DATATYPE_UINT16_S // Free buffers
SPINEL_DATATYPE_UINT16_S // Lowpan send messages
SPINEL_DATATYPE_UINT16_S // Lowpan send buffers
SPINEL_DATATYPE_UINT16_S // Lowpan reassembly messages
SPINEL_DATATYPE_UINT16_S // Lowpan reassembly buffers
SPINEL_DATATYPE_UINT16_S // Ip6 messages
SPINEL_DATATYPE_UINT16_S // Ip6 buffers
SPINEL_DATATYPE_UINT16_S // Mpl messages
SPINEL_DATATYPE_UINT16_S // Mpl buffers
SPINEL_DATATYPE_UINT16_S // Mle messages
SPINEL_DATATYPE_UINT16_S // Mle buffers
SPINEL_DATATYPE_UINT16_S // Arp messages
SPINEL_DATATYPE_UINT16_S // Arp buffers
SPINEL_DATATYPE_UINT16_S // Coap messages
SPINEL_DATATYPE_UINT16_S // Coap buffers
),
bufferInfo.mTotalBuffers,
bufferInfo.mFreeBuffers,
bufferInfo.m6loSendMessages,
bufferInfo.m6loSendBuffers,
bufferInfo.m6loReassemblyMessages,
bufferInfo.m6loReassemblyBuffers,
bufferInfo.mIp6Messages,
bufferInfo.mIp6Buffers,
bufferInfo.mMplMessages,
bufferInfo.mMplBuffers,
bufferInfo.mMleMessages,
bufferInfo.mMleBuffers,
bufferInfo.mArpMessages,
bufferInfo.mArpBuffers,
bufferInfo.mCoapMessages,
bufferInfo.mCoapBuffers
));
SuccessOrExit(error = OutboundFrameSend());
exit:
return error;
}
otError NcpBase::GetPropertyHandler_DEBUG_TEST_ASSERT(uint8_t aHeader, spinel_prop_key_t aKey)
{
assert(false);
// We only get to this point if `assert(false)`
// does not cause an NCP reset on the platform.
// In such a case we return `false` as the
// property value to indicate this.
OT_UNREACHABLE_CODE(
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
false
);
)
}
otError NcpBase::GetPropertyHandler_DEBUG_NCP_LOG_LEVEL(uint8_t aHeader, spinel_prop_key_t aKey)
{
uint8_t logLevel = 0;
switch (otGetDynamicLogLevel(mInstance))
{
case OT_LOG_LEVEL_NONE:
logLevel = SPINEL_NCP_LOG_LEVEL_EMERG;
break;
case OT_LOG_LEVEL_CRIT:
logLevel = SPINEL_NCP_LOG_LEVEL_CRIT;
break;
case OT_LOG_LEVEL_WARN:
logLevel = SPINEL_NCP_LOG_LEVEL_WARN;
break;
case OT_LOG_LEVEL_INFO:
logLevel = SPINEL_NCP_LOG_LEVEL_INFO;
break;
case OT_LOG_LEVEL_DEBG:
logLevel = SPINEL_NCP_LOG_LEVEL_DEBUG;
break;
}
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
logLevel
);
}
#if OPENTHREAD_ENABLE_MAC_WHITELIST
otError NcpBase::GetPropertyHandler_MAC_WHITELIST(uint8_t aHeader, spinel_prop_key_t aKey)
{
otMacWhitelistEntry entry;
otError error = OT_ERROR_NONE;
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey
));
for (uint8_t i = 0; (i != 255) && (error == OT_ERROR_NONE); i++)
{
error = otLinkGetWhitelistEntry(mInstance, i, &entry);
if (error != OT_ERROR_NONE)
{
break;
}
if (entry.mValid)
{
if (!entry.mFixedRssi)
{
entry.mRssi = RSSI_OVERRIDE_DISABLED;
}
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_EUI64_S // Extended address
SPINEL_DATATYPE_INT8_S // Rssi
),
entry.mExtAddress.m8,
entry.mRssi
));
}
}
SuccessOrExit(error = OutboundFrameSend());
exit:
mDisableStreamWrite = false;
return error;
}
otError NcpBase::GetPropertyHandler_MAC_WHITELIST_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
otLinkIsWhitelistEnabled(mInstance)
);
}
otError NcpBase::GetPropertyHandler_MAC_BLACKLIST(uint8_t aHeader, spinel_prop_key_t aKey)
{
otMacBlacklistEntry entry;
otError error = OT_ERROR_NONE;
mDisableStreamWrite = true;
SuccessOrExit(error = OutboundFrameBegin());
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S,
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey
));
for (uint8_t i = 0; (i != 255) && (error == OT_ERROR_NONE); i++)
{
error = otLinkGetBlacklistEntry(mInstance, i, &entry);
if (error != OT_ERROR_NONE)
{
break;
}
if (entry.mValid)
{
SuccessOrExit(
error = OutboundFrameFeedPacked(
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_EUI64_S // Extended address
),
entry.mExtAddress.m8
));
}
}
SuccessOrExit(error = OutboundFrameSend());
exit:
mDisableStreamWrite = false;
return error;
}
otError NcpBase::GetPropertyHandler_MAC_BLACKLIST_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
otLinkIsBlacklistEnabled(mInstance)
);
}
#endif // OPENTHREAD_ENABLE_MAC_WHITELIST
#if OPENTHREAD_FTD
otError NcpBase::GetPropertyHandler_NET_PSKC(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_DATA_S,
otThreadGetPSKc(mInstance),
sizeof(spinel_net_pskc_t)
);
}
#endif // OPENTHREAD_FTD
otError NcpBase::GetPropertyHandler_THREAD_MODE(uint8_t aHeader, spinel_prop_key_t aKey)
{
uint8_t numericMode;
otLinkModeConfig modeConfig = otThreadGetLinkMode(mInstance);
numericMode = LinkFlagsToFlagByte(modeConfig.mRxOnWhenIdle,
modeConfig.mSecureDataRequests,
modeConfig.mDeviceType,
modeConfig.mNetworkData);
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
numericMode
);
}
#if OPENTHREAD_FTD
otError NcpBase::GetPropertyHandler_THREAD_CHILD_COUNT_MAX(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
otThreadGetMaxAllowedChildren(mInstance)
);
}
#endif // OPENTHREAD_FTD
otError NcpBase::GetPropertyHandler_THREAD_CHILD_TIMEOUT(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT32_S,
otThreadGetChildTimeout(mInstance)
);
}
otError NcpBase::GetPropertyHandler_THREAD_RLOC16(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT16_S,
otThreadGetRloc16(mInstance)
);
}
#if OPENTHREAD_FTD
otError NcpBase::GetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
otThreadGetRouterUpgradeThreshold(mInstance)
);
}
otError NcpBase::GetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
otThreadGetRouterDowngradeThreshold(mInstance)
);
}
otError NcpBase::GetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
otThreadGetRouterSelectionJitter(mInstance)
);
}
otError NcpBase::GetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT32_S,
otThreadGetContextIdReuseDelay(mInstance)
);
}
otError NcpBase::GetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
otThreadGetNetworkIdTimeout(mInstance)
);
}
#endif // OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
otError NcpBase::GetPropertyHandler_THREAD_COMMISSIONER_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey)
{
bool enabled = false;
if (otCommissionerGetState(mInstance) == OT_COMMISSIONER_STATE_ACTIVE)
{
enabled = true;
}
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
enabled
);
}
#endif
otError NcpBase::GetPropertyHandler_NET_REQUIRE_JOIN_EXISTING(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_BOOL_S,
mRequireJoinExistingNetwork
);
}
#if OPENTHREAD_ENABLE_LEGACY
otError NcpBase::GetPropertyHandler_NEST_LEGACY_ULA_PREFIX(uint8_t aHeader, spinel_prop_key_t aKey)
{
return SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_DATA_S,
mLegacyUlaPrefix,
sizeof(mLegacyUlaPrefix)
);
}
#endif // OPENTHREAD_ENABLE_LEGACY
// ----------------------------------------------------------------------------
// MARK: Individual Property Setters
// ----------------------------------------------------------------------------
otError NcpBase::SendSetPropertyResponse(uint8_t aHeader, spinel_prop_key_t aKey, otError aError)
{
if (aError == OT_ERROR_NONE)
{
aError = HandleCommandPropertyGet(aHeader, aKey);
}
else
{
aError = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(aError));
}
return aError;
}
otError NcpBase::SetPropertyHandler_POWER_STATE(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
// TODO: Implement POWER_STATE
OT_UNUSED_VARIABLE(aKey);
OT_UNUSED_VARIABLE(aValuePtr);
OT_UNUSED_VARIABLE(aValueLen);
return SendLastStatus(aHeader, SPINEL_STATUS_UNIMPLEMENTED);
}
otError NcpBase::SetPropertyHandler_HOST_POWER_STATE(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
uint8_t value;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&value
);
if (parsedLength > 0)
{
switch (value)
{
case SPINEL_HOST_POWER_STATE_OFFLINE:
case SPINEL_HOST_POWER_STATE_DEEP_SLEEP:
case SPINEL_HOST_POWER_STATE_LOW_POWER:
case SPINEL_HOST_POWER_STATE_ONLINE:
// Adopt the requested power state.
mHostPowerState = static_cast<spinel_host_power_state_t>(value);
break;
case SPINEL_HOST_POWER_STATE_RESERVED:
// Per the specification, treat this as synonymous with SPINEL_HOST_POWER_STATE_DEEP_SLEEP.
mHostPowerState = SPINEL_HOST_POWER_STATE_DEEP_SLEEP;
break;
default:
// Per the specification, treat unrecognized values as synonymous with SPINEL_HOST_POWER_STATE_LOW_POWER.
mHostPowerState = SPINEL_HOST_POWER_STATE_LOW_POWER;
break;
}
}
else
{
error = OT_ERROR_PARSE;
}
if (error == OT_ERROR_NONE)
{
mHostPowerStateHeader = 0;
error = HandleCommandPropertyGet(aHeader, aKey);
if (mHostPowerState != SPINEL_HOST_POWER_STATE_ONLINE)
{
if (error == OT_ERROR_NONE)
{
mHostPowerReplyFrameTag = GetLastOutboundFrameTag();
}
else
{
mHostPowerReplyFrameTag = NcpFrameBuffer::kInvalidTag;
}
mHostPowerStateInProgress = true;
}
if (error != OT_ERROR_NONE)
{
mHostPowerStateHeader = aHeader;
// The reply will be queued when buffer space becomes available
// in the NCP tx buffer so we return `success` to avoid sending a
// NOMEM status for the same tid through `mDroppedReplyTid` list.
error = OT_ERROR_NONE;
}
}
else
{
error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error));
}
return error;
}
#if OPENTHREAD_ENABLE_RAW_LINK_API
otError NcpBase::SetPropertyHandler_PHY_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
bool value = false;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&value
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
if (value == false)
{
// If we have raw stream enabled stop receiving
if (mIsRawStreamEnabled)
{
otLinkRawSleep(mInstance);
}
error = otLinkRawSetEnable(mInstance, false);
}
else
{
error = otLinkRawSetEnable(mInstance, true);
// If we have raw stream enabled already, start receiving
if (error == OT_ERROR_NONE && mIsRawStreamEnabled)
{
error = otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone);
}
}
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
otError NcpBase::SetPropertyHandler_PHY_TX_POWER(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
int8_t value = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_INT8_S,
&value
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otLinkSetMaxTransmitPower(mInstance, value);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_PHY_CHAN(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
unsigned int channel = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT_PACKED_S,
&channel
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
error = otLinkSetChannel(mInstance, static_cast<uint8_t>(channel));
#if OPENTHREAD_ENABLE_RAW_LINK_API
SuccessOrExit(error);
// Cache the channel. If the raw link layer isn't enabled yet, the otSetChannel call
// doesn't call into the radio layer to set the channel. We will have to do it
// manually whenever the radios are enabled and/or raw stream is enabled.
mCurReceiveChannel = static_cast<uint8_t>(channel);
// Make sure we are update the receiving channel if raw link is enabled and we have raw
// stream enabled already
if (otLinkRawIsEnabled(mInstance) && mIsRawStreamEnabled)
{
error = otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone);
}
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_MAC_PROMISCUOUS_MODE(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint8_t mode = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&mode
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
switch (mode)
{
case SPINEL_MAC_PROMISCUOUS_MODE_OFF:
otPlatRadioSetPromiscuous(mInstance, false);
break;
case SPINEL_MAC_PROMISCUOUS_MODE_NETWORK:
case SPINEL_MAC_PROMISCUOUS_MODE_FULL:
otPlatRadioSetPromiscuous(mInstance, true);
break;
default:
error = OT_ERROR_INVALID_ARGS;
break;
}
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_MAC_SCAN_MASK(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
uint32_t newMask = 0;
otError error = OT_ERROR_NONE;
for (; aValueLen != 0; aValueLen--, aValuePtr++)
{
VerifyOrExit(aValuePtr[0] <= 31, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit((mSupportedChannelMask & (1 << aValuePtr[0])) != 0, error = OT_ERROR_INVALID_ARGS);
newMask |= (1 << aValuePtr[0]);
}
mChannelMask = newMask;
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_MAC_SCAN_PERIOD(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
uint16_t period = mScanPeriod;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT16_S,
&period
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
mScanPeriod = period;
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_NET_REQUIRE_JOIN_EXISTING(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
bool value = mRequireJoinExistingNetwork;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&value
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
mRequireJoinExistingNetwork = value;
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
static bool HasOnly1BitSet(uint32_t aValue)
{
return aValue != 0 && ((aValue & (aValue - 1)) == 0);
}
static uint8_t IndexOfMSB(uint32_t aValue)
{
uint8_t index = 0;
while (aValue >>= 1)
{
index++;
}
return index;
}
otError NcpBase::SetPropertyHandler_MAC_SCAN_STATE(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
uint8_t state = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&state
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
switch (state)
{
case SPINEL_SCAN_STATE_IDLE:
error = OT_ERROR_NONE;
break;
case SPINEL_SCAN_STATE_BEACON:
#if OPENTHREAD_ENABLE_RAW_LINK_API
if (otLinkRawIsEnabled(mInstance))
{
error = OT_ERROR_NOT_IMPLEMENTED;
}
else
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
{
error = otLinkActiveScan(
mInstance,
mChannelMask,
mScanPeriod,
&HandleActiveScanResult_Jump,
this
);
}
SuccessOrExit(error);
mShouldSignalEndOfScan = false;
break;
case SPINEL_SCAN_STATE_ENERGY:
#if OPENTHREAD_ENABLE_RAW_LINK_API
if (otLinkRawIsEnabled(mInstance))
{
uint8_t scanChannel;
// Make sure we aren't already scanning and that we have
// only 1 bit set for the channel mask.
VerifyOrExit(mCurScanChannel == NCP_INVALID_SCAN_CHANNEL, error = OT_ERROR_INVALID_STATE);
VerifyOrExit(HasOnly1BitSet(mChannelMask), error = OT_ERROR_INVALID_ARGS);
scanChannel = IndexOfMSB(mChannelMask);
mCurScanChannel = (int8_t)scanChannel;
error = otLinkRawEnergyScan(
mInstance,
scanChannel,
mScanPeriod,
LinkRawEnergyScanDone
);
}
else
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
{
error = otLinkEnergyScan(
mInstance,
mChannelMask,
mScanPeriod,
&HandleEnergyScanResult_Jump,
this
);
}
SuccessOrExit(error);
mShouldSignalEndOfScan = false;
break;
case SPINEL_SCAN_STATE_DISCOVER:
error = otThreadDiscover(
mInstance,
mChannelMask,
mDiscoveryScanPanId,
mDiscoveryScanJoinerFlag,
mDiscoveryScanEnableFiltering,
&HandleActiveScanResult_Jump,
this
);
SuccessOrExit(error);
mShouldSignalEndOfScan = false;
break;
default:
error = OT_ERROR_INVALID_ARGS;
break;
}
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_MAC_15_4_PANID(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
uint16_t panid;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT16_S,
&panid
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
error = otLinkSetPanId(mInstance, panid);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_MAC_15_4_LADDR(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
otExtAddress *extAddress;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_EUI64_S,
&extAddress
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
error = otLinkSetExtendedAddress(mInstance, extAddress);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_MAC_DATA_POLL_PERIOD(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint32_t pollPeriod;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT32_S,
&pollPeriod
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otLinkSetPollPeriod(mInstance, pollPeriod);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_MAC_RAW_STREAM_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
bool value = false;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&value
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
#if OPENTHREAD_ENABLE_RAW_LINK_API
if (otLinkRawIsEnabled(mInstance))
{
if (value)
{
error = otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone);
}
else
{
error = otLinkRawSleep(mInstance);
}
}
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
mIsRawStreamEnabled = value;
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
#if OPENTHREAD_ENABLE_RAW_LINK_API
otError NcpBase::SetPropertyHandler_MAC_15_4_SADDR(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
uint16_t shortAddress;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT16_S,
&shortAddress
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
error = otLinkRawSetShortAddress(mInstance, shortAddress);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_STREAM_RAW(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
uint8_t *frame_buffer = NULL;
otRadioFrame *frame;
unsigned int frameLen = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
VerifyOrExit(otLinkRawIsEnabled(mInstance), error = OT_ERROR_INVALID_STATE);
frame = otLinkRawGetTransmitBuffer(mInstance);
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_DATA_WLEN_S
SPINEL_DATATYPE_UINT8_S
SPINEL_DATATYPE_INT8_S,
&frame_buffer,
&frameLen,
&frame->mChannel,
&frame->mPower
);
VerifyOrExit(parsedLength > 0 && frameLen <= OT_RADIO_FRAME_MAX_SIZE, error = OT_ERROR_PARSE);
// Cache the transaction ID for async response
mCurTransmitTID = SPINEL_HEADER_GET_TID(aHeader);
// Update frame buffer and length
frame->mLength = static_cast<uint8_t>(frameLen);
memcpy(frame->mPsdu, frame_buffer, frame->mLength);
// TODO: This should be later added in the STREAM_RAW argument to allow user to directly specify it.
frame->mMaxTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT;
// Pass frame to the radio layer. Note, this fails if we
// haven't enabled raw stream or are already transmitting.
error = otLinkRawTransmit(mInstance, frame, &NcpBase::LinkRawTransmitDone);
exit:
if (error == OT_ERROR_NONE)
{
// Don't do anything here yet. We will complete the transaction when we get a transmit done callback
}
else
{
error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error));
}
OT_UNUSED_VARIABLE(aKey);
return error;
}
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
otError NcpBase::SetPropertyHandler_NET_IF_UP(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
bool enabled = false;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&enabled
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
error = otIp6SetEnabled(mInstance, enabled);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_NET_STACK_UP(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
bool enabled = false;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&enabled
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
// If the value has changed...
if ((enabled != false) != (otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED))
{
if (enabled != false)
{
error = otThreadSetEnabled(mInstance, true);
#if OPENTHREAD_ENABLE_LEGACY
mLegacyNodeDidJoin = false;
if ((mLegacyHandlers != NULL) && (mLegacyHandlers->mStartLegacy != NULL))
{
mLegacyHandlers->mStartLegacy();
}
#endif // OPENTHREAD_ENABLE_LEGACY
}
else
{
error = otThreadSetEnabled(mInstance, false);
#if OPENTHREAD_ENABLE_LEGACY
mLegacyNodeDidJoin = false;
if ((mLegacyHandlers != NULL) && (mLegacyHandlers->mStopLegacy != NULL))
{
mLegacyHandlers->mStopLegacy();
}
#endif // OPENTHREAD_ENABLE_LEGACY
}
}
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_NET_ROLE(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
unsigned int role = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT_PACKED_S,
&role
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
switch (role)
{
case SPINEL_NET_ROLE_DETACHED:
error = otThreadBecomeDetached(mInstance);
break;
#if OPENTHREAD_FTD
case SPINEL_NET_ROLE_ROUTER:
error = otThreadBecomeRouter(mInstance);
break;
case SPINEL_NET_ROLE_LEADER:
error = otThreadBecomeLeader(mInstance);
break;
#endif // OPENTHREAD_FTD
case SPINEL_NET_ROLE_CHILD:
error = otThreadBecomeChild(mInstance);
break;
}
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_NET_NETWORK_NAME(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
const char *string = NULL;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UTF8_S,
&string
);
VerifyOrExit((parsedLength > 0) && (string != NULL), error = OT_ERROR_PARSE);
error = otThreadSetNetworkName(mInstance, string);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_NET_XPANID(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
const uint8_t *ptr = NULL;
spinel_size_t len;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_DATA_S,
&ptr,
&len
);
VerifyOrExit((parsedLength > 0) && (len == sizeof(spinel_net_xpanid_t)), error = OT_ERROR_PARSE);
error = otThreadSetExtendedPanId(mInstance, ptr);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_NET_MASTER_KEY(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
const uint8_t *ptr = NULL;
spinel_size_t len;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_DATA_S,
&ptr,
&len
);
VerifyOrExit((parsedLength > 0) && (len == OT_MASTER_KEY_SIZE), error = OT_ERROR_PARSE);
error = otThreadSetMasterKey(mInstance, reinterpret_cast<const otMasterKey *>(ptr));
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_NET_KEY_SEQUENCE_COUNTER(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
unsigned int keySeqCounter;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT32_S,
&keySeqCounter
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otThreadSetKeySequenceCounter(mInstance, keySeqCounter);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_NET_KEY_SWITCH_GUARDTIME(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
unsigned int keyGuardTime;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT32_S,
&keyGuardTime
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otThreadSetKeySwitchGuardTime(mInstance, keyGuardTime);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
#if OPENTHREAD_FTD
otError NcpBase::SetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint8_t weight;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&weight
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otThreadSetLocalLeaderWeight(mInstance, weight);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
#endif // OPENTHREAD_FTD
otError NcpBase::SetPropertyHandler_STREAM_NET_INSECURE(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
const uint8_t *framePtr = NULL;
unsigned int frameLen = 0;
const uint8_t *metaPtr = NULL;
unsigned int metaLen = 0;
otMessage *message;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
// STREAM_NET_INSECURE packets are not secured at layer 2.
message = otIp6NewMessage(mInstance, false);
VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS);
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
(
SPINEL_DATATYPE_DATA_WLEN_S // Frame data
SPINEL_DATATYPE_DATA_S // Meta data
),
&framePtr,
&frameLen,
&metaPtr,
&metaLen
);
// We ignore metadata for now.
// May later include TX power, allow retransmits, etc...
OT_UNUSED_VARIABLE(metaPtr);
OT_UNUSED_VARIABLE(metaLen);
OT_UNUSED_VARIABLE(parsedLength);
SuccessOrExit(error = otMessageAppend(message, framePtr, static_cast<uint16_t>(frameLen)));
// Ensure the insecure message is forwarded using direct transmission.
otMessageSetDirectTransmission(message, true);
error = otIp6Send(mInstance, message);
// `otIp6Send()` takes ownership of `message` (in both success or
// failure cases). `message` is set to NULL so it is not freed at
// exit.
message = NULL;
exit:
if (message != NULL)
{
otMessageFree(message);
}
if (error == OT_ERROR_NONE)
{
mInboundInsecureIpFrameCounter++;
if (SPINEL_HEADER_GET_TID(aHeader) != 0)
{
// Only send a successful status update if
// there was a transaction id in the aHeader.
error = SendLastStatus(aHeader, SPINEL_STATUS_OK);
}
}
else
{
mDroppedInboundIpFrameCounter++;
error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error));
}
OT_UNUSED_VARIABLE(aKey);
return error;
}
#if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD
otError NcpBase::SetPropertyHandler_THREAD_TMF_PROXY_STREAM(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
const uint8_t *framePtr = NULL;
unsigned int frameLen = 0;
uint16_t locator;
uint16_t port;
otMessage *message;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
// THREAD_TMF_PROXY_STREAM requires layer 2 security.
message = otIp6NewMessage(mInstance, true);
VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS);
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
(
SPINEL_DATATYPE_DATA_WLEN_S // Frame data
SPINEL_DATATYPE_UINT16_S // Locator
SPINEL_DATATYPE_UINT16_S // Port
),
&framePtr,
&frameLen,
&locator,
&port
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
SuccessOrExit(error = otMessageAppend(message, framePtr, static_cast<uint16_t>(frameLen)));
error = otTmfProxySend(mInstance, message, locator, port);
// `otTmfProxySend()` takes ownership of `message` (in both success
// or failure cases). `message` is set to NULL so it is not freed at
// exit.
message = NULL;
exit:
if (message != NULL)
{
otMessageFree(message);
}
if (error == OT_ERROR_NONE)
{
if (SPINEL_HEADER_GET_TID(aHeader) != 0)
{
// Only send a successful status update if
// there was a transaction id in the aHeader.
error = SendLastStatus(aHeader, SPINEL_STATUS_OK);
}
}
else
{
error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error));
}
OT_UNUSED_VARIABLE(aKey);
return error;
}
#endif // OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD
otError NcpBase::SetPropertyHandler_STREAM_NET(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
const uint8_t *framePtr = NULL;
unsigned int frameLen = 0;
const uint8_t *metaPtr = NULL;
unsigned int metaLen = 0;
otMessage *message;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
// STREAM_NET requires layer 2 security.
message = otIp6NewMessage(mInstance, true);
VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS);
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
(
SPINEL_DATATYPE_DATA_WLEN_S // Frame data
SPINEL_DATATYPE_DATA_S // Meta data
),
&framePtr,
&frameLen,
&metaPtr,
&metaLen
);
// We ignore metadata for now.
// May later include TX power, allow retransmits, etc...
OT_UNUSED_VARIABLE(metaPtr);
OT_UNUSED_VARIABLE(metaLen);
OT_UNUSED_VARIABLE(parsedLength);
SuccessOrExit(error = otMessageAppend(message, framePtr, static_cast<uint16_t>(frameLen)));
error = otIp6Send(mInstance, message);
// `otIp6Send()` takes ownership of `message` (in both success or
// failure cases). `message` is set to NULL so it is not freed at
// exit.
message = NULL;
exit:
if (message != NULL)
{
otMessageFree(message);
}
if (error == OT_ERROR_NONE)
{
mInboundSecureIpFrameCounter++;
if (SPINEL_HEADER_GET_TID(aHeader) != 0)
{
// Only send a successful status update if
// there was a transaction id in the aHeader.
error = SendLastStatus(aHeader, SPINEL_STATUS_OK);
}
}
else
{
mDroppedInboundIpFrameCounter++;
error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error));
}
OT_UNUSED_VARIABLE(aKey);
return error;
}
otError NcpBase::SetPropertyHandler_IPV6_ML_PREFIX(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(aValueLen >= 8, error = OT_ERROR_PARSE);
error = otThreadSetMeshLocalPrefix(mInstance, aValuePtr);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_IPV6_ICMP_PING_OFFLOAD(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
bool enabled = false;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&enabled
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otIcmp6SetEchoEnabled(mInstance, enabled);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
bool enabled = false;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&enabled
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
// Note reverse logic: passthru enabled = filter disabled
otIp6SetReceiveFilterEnabled(mInstance, !enabled);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_THREAD_DISCOVERY_SCAN_JOINER_FLAG(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
bool joinerFlag = false;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&joinerFlag
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
mDiscoveryScanJoinerFlag = joinerFlag;
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr,
uint16_t aValueLen)
{
bool enabled = false;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&enabled
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
mDiscoveryScanEnableFiltering = enabled;
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_THREAD_DISCOVERY_SCAN_PANID(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint16_t panid;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT16_S,
&panid
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
mDiscoveryScanPanId = panid;
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint8_t numEntries = 0;
const uint16_t *ports = otIp6GetUnsecurePorts(mInstance, &numEntries);
spinel_ssize_t parsedLength;
bool portsChanged = false;
otError error = OT_ERROR_NONE;
// First, we need to remove all of the current assisting ports.
for (; numEntries != 0; ports++, numEntries--)
{
SuccessOrExit(error = otIp6RemoveUnsecurePort(mInstance, *ports));
portsChanged = true;
}
while (aValueLen >= 2)
{
uint16_t port;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT16_S,
&port
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
SuccessOrExit(error = otIp6AddUnsecurePort(mInstance, port));
aValuePtr += parsedLength;
aValueLen -= parsedLength;
portsChanged = true;
}
// No error happened so the new state of ports will
// be reported in the response.
portsChanged = false;
exit:
error = SendSetPropertyResponse(aHeader, aKey, error);
if (portsChanged)
{
// We had an error, but we've actually changed
// the state of these ports---so we need to report
// those incomplete changes via an asynchronous
// change event.
HandleCommandPropertyGet(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, aKey);
}
return error;
}
#if OPENTHREAD_ENABLE_BORDER_ROUTER
otError NcpBase::SetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
bool value = false;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
bool shouldRegisterWithLeader = false;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&value
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
// Register any net data changes on transition from `true` to `false`.
shouldRegisterWithLeader = (mAllowLocalNetworkDataChange == true) && (value == false);
mAllowLocalNetworkDataChange = value;
exit:
error = SendSetPropertyResponse(aHeader, aKey, error);
if (shouldRegisterWithLeader)
{
otBorderRouterRegister(mInstance);
}
return error;
}
#endif // OPENTHREAD_ENABLE_BORDER_ROUTER
#if OPENTHREAD_FTD
otError NcpBase::SetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
bool enabled;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&enabled
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otThreadSetRouterRoleEnabled(mInstance, enabled);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
#if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
otError NcpBase::SetPropertyHandler_THREAD_STEERING_DATA(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
otExtAddress *extAddress;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_EUI64_S,
&extAddress
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
error = otThreadSetSteeringData(mInstance, extAddress);
exit:
if (error == OT_ERROR_NONE)
{
// Note that there is no get handler for this property.
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_EUI64_S,
extAddress->m8
);
}
else
{
error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error));
}
return error;
}
#endif // #if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
#endif // #if OPENTHREAD_FTD
otError NcpBase::SetPropertyHandler_CNTR_RESET(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
uint8_t value = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&value
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
VerifyOrExit(value == 1, error = OT_ERROR_INVALID_ARGS);
// TODO: Implement counter reset!
error = OT_ERROR_NOT_IMPLEMENTED;
exit:
OT_UNUSED_VARIABLE(aKey);
// There is currently no getter for PROP_CNTR_RESET, so we just
// return SPINEL_STATUS_OK for success when the counters are reset.
return SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error));
}
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
otError NcpBase::SetPropertyHandler_THREAD_COMMISSIONER_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr,
uint16_t aValueLen)
{
bool enabled = false;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&enabled
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
if (enabled == false)
{
error = otCommissionerStop(mInstance);
}
else
{
error = otCommissionerStart(mInstance);
}
exit:
OT_UNUSED_VARIABLE(aKey);
return SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error));
}
#endif // OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_MAC_WHITELIST
otError NcpBase::SetPropertyHandler_MAC_WHITELIST(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
bool reportAsync;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
// First, clear the whitelist.
otLinkClearWhitelist(mInstance);
while (aValueLen > 0)
{
otExtAddress *extAddress = NULL;
int8_t rssi = RSSI_OVERRIDE_DISABLED;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_EUI64_S
SPINEL_DATATYPE_INT8_S
),
&extAddress,
&rssi
);
if (parsedLength <= 0)
{
rssi = RSSI_OVERRIDE_DISABLED;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_EUI64_S
),
&extAddress
);
}
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
if (rssi == RSSI_OVERRIDE_DISABLED)
{
SuccessOrExit(error = otLinkAddWhitelist(mInstance, extAddress->m8));
}
else
{
SuccessOrExit(error = otLinkAddWhitelistRssi(mInstance, extAddress->m8, rssi));
}
aValuePtr += parsedLength;
aValueLen -= parsedLength;
}
exit:
// If we had an error, we may have actually changed
// the state of the whitelist---so we need to report
// those incomplete changes via an asynchronous
// change event.
reportAsync = (error != OT_ERROR_NONE);
error = SendSetPropertyResponse(aHeader, aKey, error);
if (reportAsync)
{
HandleCommandPropertyGet(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, aKey);
}
return error;
}
otError NcpBase::SetPropertyHandler_MAC_WHITELIST_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
bool enabled;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&enabled
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otLinkSetWhitelistEnabled(mInstance, enabled);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_MAC_BLACKLIST(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
bool reportAsync;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
// First, clear the blacklist.
otLinkClearBlacklist(mInstance);
while (aValueLen > 0)
{
otExtAddress *ext_addr = NULL;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_EUI64_S
),
&ext_addr
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
SuccessOrExit(error = otLinkAddBlacklist(mInstance, ext_addr->m8));
aValuePtr += parsedLength;
aValueLen -= parsedLength;
}
exit:
// If we had an error, we may have actually changed
// the state of the blacklist---so we need to report
// those incomplete changes via an asynchronous
// change event.
reportAsync = (error != OT_ERROR_NONE);
error = SendSetPropertyResponse(aHeader, aKey, error);
if (reportAsync)
{
HandleCommandPropertyGet(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, aKey);
}
return error;
}
otError NcpBase::SetPropertyHandler_MAC_BLACKLIST_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
bool enabled;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&enabled
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otLinkSetBlacklistEnabled(mInstance, enabled);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
#endif // OPENTHREAD_ENABLE_MAC_WHITELIST
#if OPENTHREAD_ENABLE_RAW_LINK_API
otError NcpBase::SetPropertyHandler_MAC_SRC_MATCH_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
bool enabled;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&enabled
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
error = otLinkRawSrcMatchEnable(mInstance, enabled);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
const uint8_t *data = aValuePtr;
uint16_t dataLen = aValueLen;
// Clear the list first
error = otLinkRawSrcMatchClearShortEntries(mInstance);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
// Loop through the addresses and add them
while (dataLen >= sizeof(uint16_t))
{
spinel_ssize_t parsedLength;
uint16_t short_address;
parsedLength = spinel_datatype_unpack(
data,
dataLen,
SPINEL_DATATYPE_UINT16_S,
&short_address
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
data += parsedLength;
dataLen -= (uint16_t)parsedLength;
error = otLinkRawSrcMatchAddShortEntry(mInstance, short_address);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
}
error =
SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
otError NcpBase::SetPropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
const uint8_t *data = aValuePtr;
uint16_t dataLen = aValueLen;
// Clear the list first
error = otLinkRawSrcMatchClearExtEntries(mInstance);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
// Loop through the addresses and add them
while (dataLen >= sizeof(otExtAddress))
{
spinel_ssize_t parsedLength;
uint8_t *extAddress;
parsedLength = spinel_datatype_unpack(
data,
dataLen,
SPINEL_DATATYPE_EUI64_S,
&extAddress
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
data += parsedLength;
dataLen -= (uint16_t)parsedLength;
error = otLinkRawSrcMatchAddExtEntry(mInstance, extAddress);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
}
error =
SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
#endif
#if OPENTHREAD_FTD
otError NcpBase::SetPropertyHandler_NET_PSKC(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
const uint8_t *ptr = NULL;
spinel_size_t len;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_DATA_S,
&ptr,
&len
);
VerifyOrExit((parsedLength > 0) && (len == sizeof(spinel_net_pskc_t)), error = OT_ERROR_PARSE);
error = otThreadSetPSKc(mInstance, ptr);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
#endif // OPENTHREAD_FTD
otError NcpBase::SetPropertyHandler_THREAD_MODE(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
uint8_t numericMode = 0;
otLinkModeConfig modeConfig;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&numericMode
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
modeConfig.mRxOnWhenIdle = ((numericMode & SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE) == SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE);
modeConfig.mSecureDataRequests =
((numericMode & SPINEL_THREAD_MODE_SECURE_DATA_REQUEST) == SPINEL_THREAD_MODE_SECURE_DATA_REQUEST);
modeConfig.mDeviceType = ((numericMode & SPINEL_THREAD_MODE_FULL_FUNCTION_DEV) == SPINEL_THREAD_MODE_FULL_FUNCTION_DEV);
modeConfig.mNetworkData = ((numericMode & SPINEL_THREAD_MODE_FULL_NETWORK_DATA) == SPINEL_THREAD_MODE_FULL_NETWORK_DATA);
error = otThreadSetLinkMode(mInstance, modeConfig);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
#if OPENTHREAD_FTD
otError NcpBase::SetPropertyHandler_THREAD_CHILD_COUNT_MAX(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint8_t maxChildren = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&maxChildren
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
error = otThreadSetMaxAllowedChildren(mInstance, maxChildren);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_THREAD_CHILD_TIMEOUT(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint32_t timeout = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT32_S,
&timeout
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otThreadSetChildTimeout(mInstance, timeout);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint8_t threshold = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&threshold
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otThreadSetRouterUpgradeThreshold(mInstance, threshold);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint8_t threshold = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&threshold
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otThreadSetRouterDowngradeThreshold(mInstance, threshold);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint8_t jitter = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&jitter
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otThreadSetRouterSelectionJitter(mInstance, jitter);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_THREAD_PREFERRED_ROUTER_ID(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint8_t routerId = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&routerId
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
error = otThreadSetPreferredRouterId(mInstance, routerId);
exit:
if (error == OT_ERROR_NONE)
{
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
SPINEL_DATATYPE_UINT8_S,
routerId
);
}
else
{
error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error));
}
return error;
}
#endif // OPENTHREAD_FTD
otError NcpBase::SetPropertyHandler_DEBUG_NCP_LOG_LEVEL(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint8_t spinelNcpLogLevel = 0;
otLogLevel logLevel;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&spinelNcpLogLevel
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
switch (spinelNcpLogLevel)
{
case SPINEL_NCP_LOG_LEVEL_EMERG:
case SPINEL_NCP_LOG_LEVEL_ALERT:
logLevel = OT_LOG_LEVEL_NONE;
break;
case SPINEL_NCP_LOG_LEVEL_CRIT:
logLevel = OT_LOG_LEVEL_CRIT;
break;
case SPINEL_NCP_LOG_LEVEL_ERR:
case SPINEL_NCP_LOG_LEVEL_WARN:
logLevel = OT_LOG_LEVEL_WARN;
break;
case SPINEL_NCP_LOG_LEVEL_NOTICE:
case SPINEL_NCP_LOG_LEVEL_INFO:
logLevel = OT_LOG_LEVEL_INFO;
break;
case SPINEL_NCP_LOG_LEVEL_DEBUG:
logLevel = OT_LOG_LEVEL_DEBG;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
break;
}
error = otSetDynamicLogLevel(mInstance, logLevel);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
#if OPENTHREAD_FTD
otError NcpBase::SetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint32_t delay = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT32_S,
&delay
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otThreadSetContextIdReuseDelay(mInstance, delay);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint8_t timeout = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&timeout
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
otThreadSetNetworkIdTimeout(mInstance, timeout);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
#endif // OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD
otError NcpBase::SetPropertyHandler_THREAD_TMF_PROXY_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
bool enabled;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&enabled
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
if (enabled)
{
error = otTmfProxyStart(mInstance, &NcpBase::HandleTmfProxyStream, this);
}
else
{
error = otTmfProxyStop(mInstance);
}
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
#endif // OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_JAM_DETECTION
otError NcpBase::SetPropertyHandler_JAM_DETECT_ENABLE(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
bool enabled;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_BOOL_S,
&enabled
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
if (enabled)
{
otJamDetectionStart(mInstance, &NcpBase::HandleJamStateChange_Jump, this);
}
else
{
otJamDetectionStop(mInstance);
}
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
int8_t threshold = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_INT8_S,
&threshold
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
error = otJamDetectionSetRssiThreshold(mInstance, threshold);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_JAM_DETECT_WINDOW(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
uint8_t window = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&window
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
error = otJamDetectionSetWindow(mInstance, window);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
otError NcpBase::SetPropertyHandler_JAM_DETECT_BUSY(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
uint8_t busy = 0;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&busy
);
VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE);
error = otJamDetectionSetBusyPeriod(mInstance, busy);
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
void NcpBase::HandleJamStateChange_Jump(bool aJamState, void *aContext)
{
static_cast<NcpBase *>(aContext)->HandleJamStateChange(aJamState);
}
void NcpBase::HandleJamStateChange(bool aJamState)
{
otError error;
error = SendPropertyUpdate(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_JAM_DETECTED,
SPINEL_DATATYPE_BOOL_S,
aJamState
);
// If we could not send the jam state change indicator (no
// buffer space), we set `mShouldSignalJamStateChange` to true to send
// it out when buffer space becomes available.
if (error != OT_ERROR_NONE)
{
mShouldSignalJamStateChange = true;
}
}
#endif // OPENTHREAD_ENABLE_JAM_DETECTION
#if OPENTHREAD_ENABLE_DIAG
otError NcpBase::SetPropertyHandler_NEST_STREAM_MFG(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
char *string = NULL;
char *output = NULL;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UTF8_S,
&string
);
if ((parsedLength > 0) && (string != NULL))
{
// all diagnostics related features are processed within diagnostics module
output = otDiagProcessCmdLine(string);
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_IS,
aKey,
reinterpret_cast<uint8_t *>(output),
static_cast<uint16_t>(strlen(output) + 1)
);
}
else
{
error = SendLastStatus(aHeader, SPINEL_STATUS_PARSE_ERROR);
}
return error;
}
#endif // OPENTHREAD_ENABLE_DIAG
#if OPENTHREAD_ENABLE_LEGACY
otError NcpBase::SetPropertyHandler_NEST_LEGACY_ULA_PREFIX(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
const uint8_t *ptr = NULL;
spinel_size_t len;
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_DATA_S,
&ptr,
&len
);
VerifyOrExit((parsedLength > 0) && (len <= sizeof(mLegacyUlaPrefix)), error = OT_ERROR_PARSE);
memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix));
memcpy(mLegacyUlaPrefix, ptr, len);
if ((mLegacyHandlers != NULL) && (mLegacyHandlers->mSetLegacyUlaPrefix != NULL))
{
mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix);
}
exit:
return SendSetPropertyResponse(aHeader, aKey, error);
}
#endif // OPENTHREAD_ENABLE_LEGACY
// ----------------------------------------------------------------------------
// MARK: Individual Property Inserters
// ----------------------------------------------------------------------------
#if OPENTHREAD_ENABLE_RAW_LINK_API
otError NcpBase::InsertPropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
uint16_t short_address;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT16_S,
&short_address
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
error = otLinkRawSrcMatchAddShortEntry(mInstance, short_address);
VerifyOrExit(error == OT_ERROR_NONE,
spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_INSERTED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
otError NcpBase::InsertPropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
uint8_t *extAddress = NULL;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_EUI64_S,
&extAddress
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
error = otLinkRawSrcMatchAddExtEntry(mInstance, extAddress);
VerifyOrExit(error == OT_ERROR_NONE,
spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_INSERTED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
#endif
otError NcpBase::InsertPropertyHandler_IPV6_ADDRESS_TABLE(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
otNetifAddress netifAddr;
otIp6Address *addrPtr;
uint32_t preferredLifetime;
uint32_t validLifetime;
uint8_t prefixLen;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
(
SPINEL_DATATYPE_IPv6ADDR_S // IPv6 address
SPINEL_DATATYPE_UINT8_S // Prefix length (in bits)
SPINEL_DATATYPE_UINT32_S // Preferred lifetime
SPINEL_DATATYPE_UINT32_S // Valid lifetime
),
&addrPtr,
&prefixLen,
&preferredLifetime,
&validLifetime
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
netifAddr.mAddress = *addrPtr;
netifAddr.mPrefixLength = prefixLen;
netifAddr.mPreferred = preferredLifetime != 0;
netifAddr.mValid = validLifetime != 0;
error = otIp6AddUnicastAddress(mInstance, &netifAddr);
VerifyOrExit(error == OT_ERROR_NONE,
spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_INSERTED,
aKey,
aValuePtr,
aValueLen
);
spinelError = SPINEL_STATUS_OK;
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
#if OPENTHREAD_ENABLE_BORDER_ROUTER
otError NcpBase::InsertPropertyHandler_THREAD_OFF_MESH_ROUTES(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
otExternalRouteConfig routeConfig;
otIp6Address *addrPtr;
bool stable = false;
uint8_t flags = 0;
memset(&routeConfig, 0, sizeof(otExternalRouteConfig));
VerifyOrExit(mAllowLocalNetworkDataChange == true, spinelError = SPINEL_STATUS_INVALID_STATE);
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
(
SPINEL_DATATYPE_IPv6ADDR_S // Route prefix
SPINEL_DATATYPE_UINT8_S // Prefix length (in bits)
SPINEL_DATATYPE_BOOL_S // Stable
SPINEL_DATATYPE_UINT8_S // Flags (Route Preference)
),
&addrPtr,
&routeConfig.mPrefix.mLength,
&stable,
&flags
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
routeConfig.mPrefix.mPrefix = *addrPtr;
routeConfig.mStable = stable;
routeConfig.mPreference = FlagByteToExternalRoutePreference(flags);
error = otBorderRouterAddRoute(mInstance, &routeConfig);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_INSERTED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
otError NcpBase::InsertPropertyHandler_THREAD_ON_MESH_NETS(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
otBorderRouterConfig borderRouterConfig;
otIp6Address *addrPtr;
bool stable = false;
uint8_t flags = 0;
memset(&borderRouterConfig, 0, sizeof(otBorderRouterConfig));
VerifyOrExit(mAllowLocalNetworkDataChange == true, spinelError = SPINEL_STATUS_INVALID_STATE);
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
(
SPINEL_DATATYPE_IPv6ADDR_S // On-mesh prefix
SPINEL_DATATYPE_UINT8_S // Prefix length (in bits)
SPINEL_DATATYPE_BOOL_S // Stable
SPINEL_DATATYPE_UINT8_S // Flags
),
&addrPtr,
&borderRouterConfig.mPrefix.mLength,
&stable,
&flags
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
borderRouterConfig.mPrefix.mPrefix = *addrPtr;
borderRouterConfig.mStable = stable;
borderRouterConfig.mPreference =
((flags & SPINEL_NET_FLAG_PREFERENCE_MASK) >> SPINEL_NET_FLAG_PREFERENCE_OFFSET);
borderRouterConfig.mPreferred = ((flags & SPINEL_NET_FLAG_PREFERRED) != 0);
borderRouterConfig.mSlaac = ((flags & SPINEL_NET_FLAG_SLAAC) != 0);
borderRouterConfig.mDhcp = ((flags & SPINEL_NET_FLAG_DHCP) != 0);
borderRouterConfig.mConfigure = ((flags & SPINEL_NET_FLAG_CONFIGURE) != 0);
borderRouterConfig.mDefaultRoute = ((flags & SPINEL_NET_FLAG_DEFAULT_ROUTE) != 0);
borderRouterConfig.mOnMesh = ((flags & SPINEL_NET_FLAG_ON_MESH) != 0);
error = otBorderRouterAddOnMeshPrefix(mInstance, &borderRouterConfig);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_INSERTED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
#endif // OPENTHREAD_ENABLE_BORDER_ROUTER
otError NcpBase::InsertPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
uint16_t port;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT16_S,
&port
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
error = otIp6AddUnsecurePort(mInstance, port);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_INSERTED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
#if OPENTHREAD_ENABLE_MAC_WHITELIST
otError NcpBase::InsertPropertyHandler_MAC_WHITELIST(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
otExtAddress *extAddress = NULL;
int8_t rssi = RSSI_OVERRIDE_DISABLED;
if (aValueLen > static_cast<spinel_ssize_t>(sizeof(otExtAddress)))
{
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_INT8_S,
&extAddress,
&rssi
);
}
else
{
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_EUI64_S,
&extAddress
);
}
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
if (rssi == RSSI_OVERRIDE_DISABLED)
{
error = otLinkAddWhitelist(mInstance, extAddress->m8);
}
else
{
error = otLinkAddWhitelistRssi(mInstance, extAddress->m8, rssi);
}
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_INSERTED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
otError NcpBase::InsertPropertyHandler_MAC_BLACKLIST(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
otExtAddress *extAddress = NULL;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_EUI64_S,
&extAddress
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
error = otLinkAddBlacklist(mInstance, extAddress->m8);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_INSERTED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
#endif // OPENTHREAD_ENABLE_MAC_WHITELIST
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
otError NcpBase::InsertPropertyHandler_THREAD_JOINERS(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
otExtAddress *extAddress = NULL;
const char *aPSKd = NULL;
uint32_t joinerTimeout = 0;
VerifyOrExit(mAllowLocalNetworkDataChange == true, spinelError = SPINEL_STATUS_INVALID_STATE);
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
(
SPINEL_DATATYPE_UTF8_S // PSK
SPINEL_DATATYPE_UINT32_S // Timeout
SPINEL_DATATYPE_EUI64_S // Extended address
),
&aPSKd,
&joinerTimeout,
&extAddress
);
if (parsedLength <= 0)
{
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
(
SPINEL_DATATYPE_UTF8_S // PSK
SPINEL_DATATYPE_UINT32_S // Timeout
),
&aPSKd,
&joinerTimeout
);
extAddress = NULL;
}
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
error = otCommissionerAddJoiner(mInstance, extAddress, aPSKd, joinerTimeout);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_INSERTED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
#endif // OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
// ----------------------------------------------------------------------------
// MARK: Individual Property Removers
// ----------------------------------------------------------------------------
#if OPENTHREAD_ENABLE_RAW_LINK_API
otError NcpBase::RemovePropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
uint16_t shortAddress;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT16_S,
&shortAddress
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
error = otLinkRawSrcMatchClearShortEntry(mInstance, shortAddress);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_REMOVED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
otError NcpBase::RemovePropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
uint8_t *extAddress;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_EUI64_S,
&extAddress
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
error = otLinkRawSrcMatchClearExtEntry(mInstance, extAddress);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_REMOVED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
#endif // #if OPENTHREAD_ENABLE_RAW_LINK_API
otError NcpBase::RemovePropertyHandler_IPV6_ADDRESS_TABLE(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
otIp6Address *addrPtr;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_IPv6ADDR_S,
&addrPtr
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
error = otIp6RemoveUnicastAddress(mInstance, addrPtr);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_REMOVED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
#if OPENTHREAD_ENABLE_BORDER_ROUTER
otError NcpBase::RemovePropertyHandler_THREAD_OFF_MESH_ROUTES(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
otIp6Prefix ip6Prefix;
otIp6Address *addrPtr;
memset(&ip6Prefix, 0, sizeof(otIp6Prefix));
VerifyOrExit(mAllowLocalNetworkDataChange == true, spinelError = SPINEL_STATUS_INVALID_STATE);
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
(
SPINEL_DATATYPE_IPv6ADDR_S // Route prefix
SPINEL_DATATYPE_UINT8_S // Prefix length (in bits)
),
&addrPtr,
&ip6Prefix.mLength
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
ip6Prefix.mPrefix = *addrPtr;
error = otBorderRouterRemoveRoute(mInstance, &ip6Prefix);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_REMOVED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
otError NcpBase::RemovePropertyHandler_THREAD_ON_MESH_NETS(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
otIp6Prefix ip6Prefix;
otIp6Address *addrPtr;
memset(&ip6Prefix, 0, sizeof(otIp6Prefix));
VerifyOrExit(mAllowLocalNetworkDataChange == true, spinelError = SPINEL_STATUS_INVALID_STATE);
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
(
SPINEL_DATATYPE_IPv6ADDR_S // On-mesh prefix
SPINEL_DATATYPE_UINT8_S // Prefix length (in bits)
),
&addrPtr,
&ip6Prefix.mLength
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
ip6Prefix.mPrefix = *addrPtr;
error = otBorderRouterRemoveOnMeshPrefix(mInstance, &ip6Prefix);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_REMOVED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
#endif
otError NcpBase::RemovePropertyHandler_THREAD_ASSISTING_PORTS(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
uint16_t port;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT16_S,
&port
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
error = otIp6RemoveUnsecurePort(mInstance, port);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_REMOVED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
#if OPENTHREAD_FTD
otError NcpBase::RemovePropertyHandler_THREAD_ACTIVE_ROUTER_IDS(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
uint8_t routerId;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_UINT8_S,
&routerId
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
error = otThreadReleaseRouterId(mInstance, routerId);
VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error));
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_REMOVED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
#endif // OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_MAC_WHITELIST
otError NcpBase::RemovePropertyHandler_MAC_WHITELIST(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
otExtAddress *extAddress = NULL;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_EUI64_S,
&extAddress
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
otLinkRemoveWhitelist(mInstance, extAddress->m8);
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_REMOVED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
otError NcpBase::RemovePropertyHandler_MAC_BLACKLIST(uint8_t aHeader, spinel_prop_key_t aKey,
const uint8_t *aValuePtr, uint16_t aValueLen)
{
spinel_ssize_t parsedLength;
otError error = OT_ERROR_NONE;
spinel_status_t spinelError = SPINEL_STATUS_OK;
otExtAddress *extAddress = NULL;
parsedLength = spinel_datatype_unpack(
aValuePtr,
aValueLen,
SPINEL_DATATYPE_EUI64_S,
&extAddress
);
VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR);
otLinkRemoveBlacklist(mInstance, extAddress->m8);
error = SendPropertyUpdate(
aHeader,
SPINEL_CMD_PROP_VALUE_REMOVED,
aKey,
aValuePtr,
aValueLen
);
exit:
if (spinelError != SPINEL_STATUS_OK)
{
error = SendLastStatus(aHeader, spinelError);
}
return error;
}
#endif // OPENTHREAD_ENABLE_MAC_WHITELIST
#if OPENTHREAD_ENABLE_LEGACY
void NcpBase::RegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers)
{
mLegacyHandlers = aHandlers;
bool isEnabled;
VerifyOrExit(mLegacyHandlers != NULL);
isEnabled = (otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED);
if (isEnabled)
{
if (mLegacyHandlers->mStartLegacy)
{
mLegacyHandlers->mStartLegacy();
}
}
else
{
if (mLegacyHandlers->mStopLegacy)
{
mLegacyHandlers->mStopLegacy();
}
}
if (mLegacyHandlers->mSetLegacyUlaPrefix)
{
mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix);
}
exit:
return;
}
void NcpBase::HandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix)
{
memcpy(mLegacyUlaPrefix, aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH);
SuccessOrExit(OutboundFrameBegin());
SuccessOrExit(
OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_DATA_S,
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_NEST_LEGACY_ULA_PREFIX,
aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH
));
SuccessOrExit(OutboundFrameSend());
exit:
return;
}
void NcpBase::HandleLegacyNodeDidJoin(const otExtAddress *aExtAddr)
{
mLegacyNodeDidJoin = true;
SuccessOrExit(OutboundFrameBegin());
SuccessOrExit(
OutboundFrameFeedPacked(
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_EUI64_S,
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_NEST_LEGACY_JOINED_NODE,
aExtAddr->m8
));
SuccessOrExit(OutboundFrameSend());
exit:
return;
}
#endif // OPENTHREAD_ENABLE_LEGACY
otError NcpBase::StreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen)
{
otError error = OT_ERROR_NONE;
if (aStreamId == 0)
{
aStreamId = SPINEL_PROP_STREAM_DEBUG;
}
VerifyOrExit(!mDisableStreamWrite, error = OT_ERROR_INVALID_STATE);
error = SendPropertyUpdate(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_IS,
static_cast<spinel_prop_key_t>(aStreamId),
aDataPtr,
static_cast<uint16_t>(aDataLen)
);
exit:
return error;
}
#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
void NcpBase::RegisterPeekPokeDelagates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate,
otNcpDelegateAllowPeekPoke aAllowPokeDelegate)
{
mAllowPeekDelegate = aAllowPeekDelegate;
mAllowPokeDelegate = aAllowPokeDelegate;
}
#endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
} // namespace ot
// ----------------------------------------------------------------------------
// MARK: Virtual Datastream I/O (Public API)
// ----------------------------------------------------------------------------
otError otNcpStreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen)
{
otError error = OT_ERROR_INVALID_STATE;
ot::NcpBase *ncp = ot::NcpBase::GetNcpInstance();
if (ncp != NULL)
{
error = ncp->StreamWrite(aStreamId, aDataPtr, aDataLen);
}
return error;
}
// ----------------------------------------------------------------------------
// MARK: Peek/Poke delegate API
// ----------------------------------------------------------------------------
otError otNcpRegisterPeekPokeDelagates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate,
otNcpDelegateAllowPeekPoke aAllowPokeDelegate)
{
otError error = OT_ERROR_NONE;
#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
ot::NcpBase *ncp = ot::NcpBase::GetNcpInstance();
if (ncp != NULL)
{
ncp->RegisterPeekPokeDelagates(aAllowPeekDelegate, aAllowPokeDelegate);
}
#else
OT_UNUSED_VARIABLE(aAllowPeekDelegate);
OT_UNUSED_VARIABLE(aAllowPokeDelegate);
error = OT_ERROR_DISABLED_FEATURE;
#endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
return error;
}
// ----------------------------------------------------------------------------
// MARK: Legacy network APIs
// ----------------------------------------------------------------------------
void otNcpRegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers)
{
#if OPENTHREAD_ENABLE_LEGACY
ot::NcpBase *ncp = ot::NcpBase::GetNcpInstance();
if (ncp != NULL)
{
ncp->RegisterLegacyHandlers(aHandlers);
}
#else
OT_UNUSED_VARIABLE(aHandlers);
#endif
}
void otNcpHandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix)
{
#if OPENTHREAD_ENABLE_LEGACY
ot::NcpBase *ncp = ot::NcpBase::GetNcpInstance();
if (ncp != NULL)
{
ncp->HandleDidReceiveNewLegacyUlaPrefix(aUlaPrefix);
}
#else
OT_UNUSED_VARIABLE(aUlaPrefix);
#endif
}
void otNcpHandleLegacyNodeDidJoin(const otExtAddress *aExtAddr)
{
#if OPENTHREAD_ENABLE_LEGACY
ot::NcpBase *ncp = ot::NcpBase::GetNcpInstance();
if (ncp != NULL)
{
ncp->HandleLegacyNodeDidJoin(aExtAddr);
}
#else
OT_UNUSED_VARIABLE(aExtAddr);
#endif
}