blob: 5b8cc89da525c685a09f771925f6ecebd0204b8e [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.
*/
#ifdef OPENTHREAD_CONFIG_FILE
#include OPENTHREAD_CONFIG_FILE
#else
#include <openthread-config.h>
#endif
#include <stdlib.h>
#include "openthread/openthread.h"
#include "openthread/ncp.h"
#include "openthread/diag.h"
#include "openthread/icmp6.h"
#if OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
#include "openthread/border_agent_proxy.h"
#endif
#if OPENTHREAD_ENABLE_JAM_DETECTION
#include "openthread/jam_detection.h"
#endif
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
#include "meshcop/commissioner.hpp"
#endif
#include "openthread/platform/radio.h"
#include "openthread/platform/misc.h"
#include <common/code_utils.hpp>
#include <common/debug.hpp>
#include <ncp/ncp_base.hpp>
#include <net/ip6.hpp>
#include <openthread-instance.h>
#include <stdarg.h>
namespace ot
{
#define NCP_INVALID_SCAN_CHANNEL (-1)
#define NCP_PLAT_RESET_REASON (1U<<31)
#define NCP_ON_MESH_NETS_CHANGED_BIT_FLAG (1U<<30)
enum
{
kThreadMode_RxOnWhenIdle = (1 << 3),
kThreadMode_SecureDataRequest = (1 << 2),
kThreadMode_FullFunctionDevice = (1 << 1),
kThreadMode_FullNetworkData = (1 << 0),
};
#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
// ----------------------------------------------------------------------------
const NcpBase::CommandHandlerEntry NcpBase::mCommandHandlerTable[] =
{
{ SPINEL_CMD_NOOP, &NcpBase::CommandHandler_NOOP },
{ SPINEL_CMD_RESET, &NcpBase::CommandHandler_RESET },
{ SPINEL_CMD_PROP_VALUE_GET, &NcpBase::CommandHandler_PROP_VALUE_GET },
{ SPINEL_CMD_PROP_VALUE_SET, &NcpBase::CommandHandler_PROP_VALUE_SET },
{ SPINEL_CMD_PROP_VALUE_INSERT, &NcpBase::CommandHandler_PROP_VALUE_INSERT },
{ SPINEL_CMD_PROP_VALUE_REMOVE, &NcpBase::CommandHandler_PROP_VALUE_REMOVE },
{ SPINEL_CMD_NET_SAVE, &NcpBase::CommandHandler_NET_SAVE },
{ SPINEL_CMD_NET_CLEAR, &NcpBase::CommandHandler_NET_CLEAR },
{ SPINEL_CMD_NET_RECALL, &NcpBase::CommandHandler_NET_RECALL },
};
const NcpBase::GetPropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] =
{
{ SPINEL_PROP_LAST_STATUS, &NcpBase::GetPropertyHandler_LAST_STATUS },
{ SPINEL_PROP_PROTOCOL_VERSION, &NcpBase::GetPropertyHandler_PROTOCOL_VERSION },
{ SPINEL_PROP_INTERFACE_TYPE, &NcpBase::GetPropertyHandler_INTERFACE_TYPE },
{ SPINEL_PROP_VENDOR_ID, &NcpBase::GetPropertyHandler_VENDOR_ID },
{ SPINEL_PROP_CAPS, &NcpBase::GetPropertyHandler_CAPS },
{ SPINEL_PROP_NCP_VERSION, &NcpBase::GetPropertyHandler_NCP_VERSION },
{ SPINEL_PROP_INTERFACE_COUNT, &NcpBase::GetPropertyHandler_INTERFACE_COUNT },
{ SPINEL_PROP_POWER_STATE, &NcpBase::GetPropertyHandler_POWER_STATE },
{ SPINEL_PROP_HWADDR, &NcpBase::GetPropertyHandler_HWADDR },
{ SPINEL_PROP_LOCK, &NcpBase::GetPropertyHandler_LOCK },
{ SPINEL_PROP_PHY_ENABLED, &NcpBase::GetPropertyHandler_PHY_ENABLED },
{ SPINEL_PROP_PHY_FREQ, &NcpBase::GetPropertyHandler_PHY_FREQ },
{ SPINEL_PROP_PHY_CHAN_SUPPORTED, &NcpBase::GetPropertyHandler_PHY_CHAN_SUPPORTED },
{ SPINEL_PROP_PHY_CHAN, &NcpBase::GetPropertyHandler_PHY_CHAN },
{ SPINEL_PROP_PHY_RSSI, &NcpBase::GetPropertyHandler_PHY_RSSI },
{ SPINEL_PROP_PHY_TX_POWER, &NcpBase::GetPropertyHandler_PHY_TX_POWER },
{ SPINEL_PROP_MAC_SCAN_STATE, &NcpBase::GetPropertyHandler_MAC_SCAN_STATE },
{ SPINEL_PROP_MAC_SCAN_MASK, &NcpBase::GetPropertyHandler_MAC_SCAN_MASK },
{ SPINEL_PROP_MAC_SCAN_PERIOD, &NcpBase::GetPropertyHandler_MAC_SCAN_PERIOD },
{ SPINEL_PROP_MAC_15_4_PANID, &NcpBase::GetPropertyHandler_MAC_15_4_PANID },
{ SPINEL_PROP_MAC_15_4_LADDR, &NcpBase::GetPropertyHandler_MAC_15_4_LADDR },
{ SPINEL_PROP_MAC_15_4_SADDR, &NcpBase::GetPropertyHandler_MAC_15_4_SADDR },
{ SPINEL_PROP_MAC_RAW_STREAM_ENABLED, &NcpBase::GetPropertyHandler_MAC_RAW_STREAM_ENABLED },
{ SPINEL_PROP_MAC_PROMISCUOUS_MODE, &NcpBase::GetPropertyHandler_MAC_PROMISCUOUS_MODE },
{ SPINEL_PROP_MAC_EXTENDED_ADDR, &NcpBase::GetPropertyHandler_MAC_EXTENDED_ADDR },
{ SPINEL_PROP_NET_SAVED, &NcpBase::GetPropertyHandler_NET_SAVED },
{ SPINEL_PROP_NET_IF_UP, &NcpBase::GetPropertyHandler_NET_IF_UP },
{ SPINEL_PROP_NET_STACK_UP, &NcpBase::GetPropertyHandler_NET_STACK_UP },
{ SPINEL_PROP_NET_ROLE, &NcpBase::GetPropertyHandler_NET_ROLE },
{ SPINEL_PROP_NET_NETWORK_NAME, &NcpBase::GetPropertyHandler_NET_NETWORK_NAME },
{ SPINEL_PROP_NET_XPANID, &NcpBase::GetPropertyHandler_NET_XPANID },
{ SPINEL_PROP_NET_MASTER_KEY, &NcpBase::GetPropertyHandler_NET_MASTER_KEY },
{ SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER, &NcpBase::GetPropertyHandler_NET_KEY_SEQUENCE_COUNTER },
{ SPINEL_PROP_NET_PARTITION_ID, &NcpBase::GetPropertyHandler_NET_PARTITION_ID },
{ SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME, &NcpBase::GetPropertyHandler_NET_KEY_SWITCH_GUARDTIME},
#if OPENTHREAD_FTD
{ SPINEL_PROP_NET_PSKC, &NcpBase::GetPropertyHandler_NET_PSKC },
#endif
{ SPINEL_PROP_THREAD_LEADER_ADDR, &NcpBase::GetPropertyHandler_THREAD_LEADER_ADDR },
{ SPINEL_PROP_THREAD_PARENT, &NcpBase::GetPropertyHandler_THREAD_PARENT },
{ SPINEL_PROP_THREAD_CHILD_TABLE, &NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE },
{ SPINEL_PROP_THREAD_NEIGHBOR_TABLE, &NcpBase::GetPropertyHandler_THREAD_NEIGHBOR_TABLE },
{ SPINEL_PROP_THREAD_LEADER_RID, &NcpBase::GetPropertyHandler_THREAD_LEADER_RID },
{ SPINEL_PROP_THREAD_LEADER_WEIGHT, &NcpBase::GetPropertyHandler_THREAD_LEADER_WEIGHT },
{ SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT, &NcpBase::GetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT },
{ SPINEL_PROP_THREAD_NETWORK_DATA, &NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA },
{ SPINEL_PROP_THREAD_NETWORK_DATA_VERSION, &NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA_VERSION },
{ SPINEL_PROP_THREAD_STABLE_NETWORK_DATA, &NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA },
{ SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION, &NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA_VERSION },
{ SPINEL_PROP_THREAD_LEADER_NETWORK_DATA, &NcpBase::GetPropertyHandler_THREAD_LEADER_NETWORK_DATA },
{ SPINEL_PROP_THREAD_STABLE_LEADER_NETWORK_DATA, &NcpBase::GetPropertyHandler_THREAD_STABLE_LEADER_NETWORK_DATA },
{ SPINEL_PROP_THREAD_LOCAL_ROUTES, &NcpBase::GetPropertyHandler_THREAD_LOCAL_ROUTES },
{ SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::GetPropertyHandler_THREAD_ASSISTING_PORTS },
{ SPINEL_PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE, &NcpBase::GetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE },
{ SPINEL_PROP_THREAD_ROUTER_ROLE_ENABLED, &NcpBase::GetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED },
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
{ SPINEL_PROP_THREAD_COMMISSIONER_ENABLED, &NcpBase::GetPropertyHandler_THREAD_COMMISSIONER_ENABLED },
#endif
{ SPINEL_PROP_MAC_WHITELIST, &NcpBase::GetPropertyHandler_MAC_WHITELIST },
{ SPINEL_PROP_MAC_WHITELIST_ENABLED, &NcpBase::GetPropertyHandler_MAC_WHITELIST_ENABLED },
{ SPINEL_PROP_THREAD_MODE, &NcpBase::GetPropertyHandler_THREAD_MODE },
{ SPINEL_PROP_THREAD_CHILD_COUNT_MAX, &NcpBase::GetPropertyHandler_THREAD_CHILD_COUNT_MAX },
{ SPINEL_PROP_THREAD_CHILD_TIMEOUT, &NcpBase::GetPropertyHandler_THREAD_CHILD_TIMEOUT },
{ SPINEL_PROP_THREAD_RLOC16, &NcpBase::GetPropertyHandler_THREAD_RLOC16 },
{ SPINEL_PROP_THREAD_ROUTER_UPGRADE_THRESHOLD, &NcpBase::GetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD },
{ SPINEL_PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD, &NcpBase::GetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD },
{ SPINEL_PROP_THREAD_CONTEXT_REUSE_DELAY, &NcpBase::GetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY },
{ SPINEL_PROP_THREAD_NETWORK_ID_TIMEOUT, &NcpBase::GetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT },
{ SPINEL_PROP_THREAD_ON_MESH_NETS, &NcpBase::GetPropertyHandler_THREAD_ON_MESH_NETS },
{ SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING, &NcpBase::GetPropertyHandler_NET_REQUIRE_JOIN_EXISTING },
{ SPINEL_PROP_THREAD_ROUTER_SELECTION_JITTER, &NcpBase::GetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER },
{ SPINEL_PROP_IPV6_ML_PREFIX, &NcpBase::GetPropertyHandler_IPV6_ML_PREFIX },
{ SPINEL_PROP_IPV6_ML_ADDR, &NcpBase::GetPropertyHandler_IPV6_ML_ADDR },
{ SPINEL_PROP_IPV6_LL_ADDR, &NcpBase::GetPropertyHandler_IPV6_LL_ADDR },
{ SPINEL_PROP_IPV6_ADDRESS_TABLE, &NcpBase::GetPropertyHandler_IPV6_ADDRESS_TABLE },
{ SPINEL_PROP_IPV6_ROUTE_TABLE, &NcpBase::GetPropertyHandler_IPV6_ROUTE_TABLE },
{ SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD, &NcpBase::GetPropertyHandler_IPV6_ICMP_PING_OFFLOAD },
{ SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU, &NcpBase::GetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU },
{ SPINEL_PROP_STREAM_NET, &NcpBase::GetPropertyHandler_STREAM_NET },
#if OPENTHREAD_ENABLE_JAM_DETECTION
{ SPINEL_PROP_JAM_DETECT_ENABLE, &NcpBase::GetPropertyHandler_JAM_DETECT_ENABLE },
{ SPINEL_PROP_JAM_DETECTED, &NcpBase::GetPropertyHandler_JAM_DETECTED },
{ SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD, &NcpBase::GetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD },
{ SPINEL_PROP_JAM_DETECT_WINDOW, &NcpBase::GetPropertyHandler_JAM_DETECT_WINDOW },
{ SPINEL_PROP_JAM_DETECT_BUSY, &NcpBase::GetPropertyHandler_JAM_DETECT_BUSY },
{ SPINEL_PROP_JAM_DETECT_HISTORY_BITMAP, &NcpBase::GetPropertyHandler_JAM_DETECT_HISTORY_BITMAP },
#endif
#if OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
{ SPINEL_PROP_THREAD_BA_PROXY_ENABLED, &NcpBase::GetPropertyHandler_BA_PROXY_ENABLED },
#endif
{ SPINEL_PROP_CNTR_TX_PKT_TOTAL, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_PKT_ACK_REQ, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_PKT_ACKED, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_PKT_NO_ACK_REQ, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_PKT_DATA, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_PKT_DATA_POLL, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_PKT_BEACON, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_PKT_BEACON_REQ, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_PKT_OTHER, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_PKT_RETRY, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_PKT_UNICAST, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_PKT_BROADCAST, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_ERR_CCA, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_ERR_ABORT, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_PKT_TOTAL, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_PKT_DATA, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_PKT_DATA_POLL, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_PKT_BEACON, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_PKT_BEACON_REQ, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_PKT_OTHER, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_PKT_FILT_WL, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_PKT_FILT_DA, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_PKT_UNICAST, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_PKT_BROADCAST, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_ERR_EMPTY, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_ERR_UKWN_NBR, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_ERR_NVLD_SADDR, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_ERR_SECURITY, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_ERR_BAD_FCS, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_ERR_OTHER, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_RX_PKT_DUP, &NcpBase::GetPropertyHandler_MAC_CNTR },
{ SPINEL_PROP_CNTR_TX_IP_SEC_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR },
{ SPINEL_PROP_CNTR_TX_IP_INSEC_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR },
{ SPINEL_PROP_CNTR_TX_IP_DROPPED, &NcpBase::GetPropertyHandler_NCP_CNTR },
{ SPINEL_PROP_CNTR_RX_IP_SEC_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR },
{ SPINEL_PROP_CNTR_RX_IP_INSEC_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR },
{ SPINEL_PROP_CNTR_RX_IP_DROPPED, &NcpBase::GetPropertyHandler_NCP_CNTR },
{ SPINEL_PROP_CNTR_TX_SPINEL_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR },
{ SPINEL_PROP_CNTR_RX_SPINEL_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR },
{ SPINEL_PROP_CNTR_RX_SPINEL_ERR, &NcpBase::GetPropertyHandler_NCP_CNTR },
{ SPINEL_PROP_MSG_BUFFER_COUNTERS, &NcpBase::GetPropertyHandler_MSG_BUFFER_COUNTERS },
{ SPINEL_PROP_DEBUG_TEST_ASSERT, &NcpBase::GetPropertyHandler_DEBUG_TEST_ASSERT },
{ SPINEL_PROP_DEBUG_NCP_LOG_LEVEL, &NcpBase::GetPropertyHandler_DEBUG_NCP_LOG_LEVEL },
#if OPENTHREAD_ENABLE_LEGACY
{ SPINEL_PROP_NEST_LEGACY_ULA_PREFIX, &NcpBase::GetPropertyHandler_NEST_LEGACY_ULA_PREFIX },
#endif
};
const NcpBase::SetPropertyHandlerEntry NcpBase::mSetPropertyHandlerTable[] =
{
{ SPINEL_PROP_POWER_STATE, &NcpBase::SetPropertyHandler_POWER_STATE },
#if OPENTHREAD_ENABLE_RAW_LINK_API
{ SPINEL_PROP_PHY_ENABLED, &NcpBase::SetPropertyHandler_PHY_ENABLED },
{ SPINEL_PROP_MAC_15_4_SADDR, &NcpBase::SetPropertyHandler_MAC_15_4_SADDR },
{ SPINEL_PROP_STREAM_RAW, &NcpBase::SetPropertyHandler_STREAM_RAW },
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
{ SPINEL_PROP_PHY_TX_POWER, &NcpBase::SetPropertyHandler_PHY_TX_POWER },
{ SPINEL_PROP_PHY_CHAN, &NcpBase::SetPropertyHandler_PHY_CHAN },
{ SPINEL_PROP_MAC_PROMISCUOUS_MODE, &NcpBase::SetPropertyHandler_MAC_PROMISCUOUS_MODE },
{ SPINEL_PROP_MAC_SCAN_MASK, &NcpBase::SetPropertyHandler_MAC_SCAN_MASK },
{ SPINEL_PROP_MAC_SCAN_STATE, &NcpBase::SetPropertyHandler_MAC_SCAN_STATE },
{ SPINEL_PROP_MAC_SCAN_PERIOD, &NcpBase::SetPropertyHandler_MAC_SCAN_PERIOD },
{ SPINEL_PROP_MAC_15_4_PANID, &NcpBase::SetPropertyHandler_MAC_15_4_PANID },
{ SPINEL_PROP_MAC_15_4_LADDR, &NcpBase::SetPropertyHandler_MAC_15_4_LADDR },
{ SPINEL_PROP_MAC_RAW_STREAM_ENABLED, &NcpBase::SetPropertyHandler_MAC_RAW_STREAM_ENABLED },
{ SPINEL_PROP_NET_IF_UP, &NcpBase::SetPropertyHandler_NET_IF_UP },
{ SPINEL_PROP_NET_STACK_UP, &NcpBase::SetPropertyHandler_NET_STACK_UP },
{ SPINEL_PROP_NET_ROLE, &NcpBase::SetPropertyHandler_NET_ROLE },
{ SPINEL_PROP_NET_NETWORK_NAME, &NcpBase::SetPropertyHandler_NET_NETWORK_NAME },
{ SPINEL_PROP_NET_XPANID, &NcpBase::SetPropertyHandler_NET_XPANID },
{ SPINEL_PROP_NET_MASTER_KEY, &NcpBase::SetPropertyHandler_NET_MASTER_KEY },
{ SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER, &NcpBase::SetPropertyHandler_NET_KEY_SEQUENCE_COUNTER },
{ SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME, &NcpBase::SetPropertyHandler_NET_KEY_SWITCH_GUARDTIME},
#if OPENTHREAD_FTD
{ SPINEL_PROP_NET_PSKC, &NcpBase::SetPropertyHandler_NET_PSKC },
#endif
{ SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT, &NcpBase::SetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT },
{ SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::SetPropertyHandler_THREAD_ASSISTING_PORTS },
{ SPINEL_PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE, &NcpBase::SetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE },
{ SPINEL_PROP_THREAD_NETWORK_ID_TIMEOUT, &NcpBase::SetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT },
{ SPINEL_PROP_THREAD_ROUTER_ROLE_ENABLED, &NcpBase::SetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED },
{ SPINEL_PROP_STREAM_NET_INSECURE, &NcpBase::SetPropertyHandler_STREAM_NET_INSECURE },
{ SPINEL_PROP_STREAM_NET, &NcpBase::SetPropertyHandler_STREAM_NET },
{ SPINEL_PROP_IPV6_ML_PREFIX, &NcpBase::SetPropertyHandler_IPV6_ML_PREFIX },
{ SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD, &NcpBase::SetPropertyHandler_IPV6_ICMP_PING_OFFLOAD },
{ SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU, &NcpBase::SetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU },
{ SPINEL_PROP_MAC_WHITELIST, &NcpBase::SetPropertyHandler_MAC_WHITELIST },
{ SPINEL_PROP_MAC_WHITELIST_ENABLED, &NcpBase::SetPropertyHandler_MAC_WHITELIST_ENABLED },
#if OPENTHREAD_ENABLE_RAW_LINK_API
{ SPINEL_PROP_MAC_SRC_MATCH_ENABLED, &NcpBase::SetPropertyHandler_MAC_SRC_MATCH_ENABLED },
{ SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, &NcpBase::SetPropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES },
{ SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, &NcpBase::SetPropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES },
#endif
{ SPINEL_PROP_THREAD_MODE, &NcpBase::SetPropertyHandler_THREAD_MODE },
{ SPINEL_PROP_THREAD_CHILD_COUNT_MAX, &NcpBase::SetPropertyHandler_THREAD_CHILD_COUNT_MAX },
{ SPINEL_PROP_THREAD_CHILD_TIMEOUT, &NcpBase::SetPropertyHandler_THREAD_CHILD_TIMEOUT },
{ SPINEL_PROP_THREAD_ROUTER_UPGRADE_THRESHOLD, &NcpBase::SetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD },
{ SPINEL_PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD, &NcpBase::SetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD },
{ SPINEL_PROP_THREAD_CONTEXT_REUSE_DELAY, &NcpBase::SetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY },
{ SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING, &NcpBase::SetPropertyHandler_NET_REQUIRE_JOIN_EXISTING },
{ SPINEL_PROP_THREAD_ROUTER_SELECTION_JITTER, &NcpBase::SetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER },
{ SPINEL_PROP_THREAD_PREFERRED_ROUTER_ID, &NcpBase::SetPropertyHandler_THREAD_PREFERRED_ROUTER_ID },
{ SPINEL_PROP_DEBUG_NCP_LOG_LEVEL, &NcpBase::SetPropertyHandler_DEBUG_NCP_LOG_LEVEL },
#if OPENTHREAD_ENABLE_JAM_DETECTION
{ SPINEL_PROP_JAM_DETECT_ENABLE, &NcpBase::SetPropertyHandler_JAM_DETECT_ENABLE },
{ SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD, &NcpBase::SetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD },
{ SPINEL_PROP_JAM_DETECT_WINDOW, &NcpBase::SetPropertyHandler_JAM_DETECT_WINDOW },
{ SPINEL_PROP_JAM_DETECT_BUSY, &NcpBase::SetPropertyHandler_JAM_DETECT_BUSY },
#endif
#if OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
{ SPINEL_PROP_THREAD_BA_PROXY_ENABLED, &NcpBase::SetPropertyHandler_BA_PROXY_ENABLED },
{ SPINEL_PROP_THREAD_BA_PROXY_STREAM, &NcpBase::SetPropertyHandler_THREAD_BA_PROXY_STREAM },
#endif // OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_DIAG
{ SPINEL_PROP_NEST_STREAM_MFG, &NcpBase::SetPropertyHandler_NEST_STREAM_MFG },
#endif
#if OPENTHREAD_ENABLE_LEGACY
{ SPINEL_PROP_NEST_LEGACY_ULA_PREFIX, &NcpBase::SetPropertyHandler_NEST_LEGACY_ULA_PREFIX },
#endif
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
{ SPINEL_PROP_THREAD_COMMISSIONER_ENABLED, &NcpBase::SetPropertyHandler_THREAD_COMMISSIONER_ENABLED },
#endif
};
const NcpBase::InsertPropertyHandlerEntry NcpBase::mInsertPropertyHandlerTable[] =
{
#if OPENTHREAD_ENABLE_RAW_LINK_API
{ SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, &NcpBase::InsertPropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES },
{ SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, &NcpBase::InsertPropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES },
#endif
{ SPINEL_PROP_IPV6_ADDRESS_TABLE, &NcpBase::InsertPropertyHandler_IPV6_ADDRESS_TABLE },
{ SPINEL_PROP_THREAD_LOCAL_ROUTES, &NcpBase::InsertPropertyHandler_THREAD_LOCAL_ROUTES },
{ SPINEL_PROP_THREAD_ON_MESH_NETS, &NcpBase::InsertPropertyHandler_THREAD_ON_MESH_NETS },
{ SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::InsertPropertyHandler_THREAD_ASSISTING_PORTS },
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
{ SPINEL_PROP_THREAD_JOINERS, &NcpBase::NcpBase::InsertPropertyHandler_THREAD_JOINERS },
#endif
{ SPINEL_PROP_CNTR_RESET, &NcpBase::SetPropertyHandler_CNTR_RESET },
{ SPINEL_PROP_MAC_WHITELIST, &NcpBase::InsertPropertyHandler_MAC_WHITELIST },
};
const NcpBase::RemovePropertyHandlerEntry NcpBase::mRemovePropertyHandlerTable[] =
{
#if OPENTHREAD_ENABLE_RAW_LINK_API
{ SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, &NcpBase::RemovePropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES },
{ SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, &NcpBase::RemovePropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES },
#endif
{ SPINEL_PROP_IPV6_ADDRESS_TABLE, &NcpBase::RemovePropertyHandler_IPV6_ADDRESS_TABLE },
{ SPINEL_PROP_THREAD_LOCAL_ROUTES, &NcpBase::RemovePropertyHandler_THREAD_LOCAL_ROUTES },
{ SPINEL_PROP_THREAD_ON_MESH_NETS, &NcpBase::RemovePropertyHandler_THREAD_ON_MESH_NETS },
{ SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::RemovePropertyHandler_THREAD_ASSISTING_PORTS },
{ SPINEL_PROP_MAC_WHITELIST, &NcpBase::RemovePropertyHandler_MAC_WHITELIST },
{ SPINEL_PROP_THREAD_ACTIVE_ROUTER_IDS, &NcpBase::RemovePropertyHandler_THREAD_ACTIVE_ROUTER_IDS },
};
// ----------------------------------------------------------------------------
// MARK: Utility Functions
// ----------------------------------------------------------------------------
static spinel_status_t ThreadErrorToSpinelStatus(ThreadError error)
{
spinel_status_t ret;
switch (error)
{
case kThreadError_None:
ret = SPINEL_STATUS_OK;
break;
case kThreadError_Failed:
ret = SPINEL_STATUS_FAILURE;
break;
case kThreadError_Drop:
ret = SPINEL_STATUS_DROPPED;
break;
case kThreadError_NoBufs:
ret = SPINEL_STATUS_NOMEM;
break;
case kThreadError_Busy:
ret = SPINEL_STATUS_BUSY;
break;
case kThreadError_Parse:
ret = SPINEL_STATUS_PARSE_ERROR;
break;
case kThreadError_InvalidArgs:
ret = SPINEL_STATUS_INVALID_ARGUMENT;
break;
case kThreadError_NotImplemented:
ret = SPINEL_STATUS_UNIMPLEMENTED;
break;
case kThreadError_InvalidState:
ret = SPINEL_STATUS_INVALID_STATE;
break;
case kThreadError_NoAck:
ret = SPINEL_STATUS_NO_ACK;
break;
case kThreadError_ChannelAccessFailure:
ret = SPINEL_STATUS_CCA_FAILURE;
break;
case kThreadError_Already:
ret = SPINEL_STATUS_ALREADY;
break;
case kThreadError_NotFound:
ret = SPINEL_STATUS_ITEM_NOT_FOUND;
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 + error);
break;
}
return ret;
}
static spinel_status_t ResetReasonToSpinelStatus(otPlatResetReason reason)
{
spinel_status_t ret;
switch (reason)
{
case kPlatResetReason_PowerOn:
ret = SPINEL_STATUS_RESET_POWER_ON;
break;
case kPlatResetReason_External:
ret = SPINEL_STATUS_RESET_EXTERNAL;
break;
case kPlatResetReason_Software:
ret = SPINEL_STATUS_RESET_SOFTWARE;
break;
case kPlatResetReason_Fault:
ret = SPINEL_STATUS_RESET_FAULT;
break;
case kPlatResetReason_Crash:
ret = SPINEL_STATUS_RESET_CRASH;
break;
case kPlatResetReason_Assert:
ret = SPINEL_STATUS_RESET_ASSERT;
break;
case kPlatResetReason_Watchdog:
ret = SPINEL_STATUS_RESET_WATCHDOG;
break;
case kPlatResetReason_Other:
ret = SPINEL_STATUS_RESET_OTHER;
break;
default:
ret = SPINEL_STATUS_RESET_UNKNOWN;
break;
}
return ret;
}
static uint8_t BorderRouterConfigToFlagByte(const otBorderRouterConfig &config)
{
uint8_t flags(0);
if (config.mPreferred)
{
flags |= SPINEL_NET_FLAG_PREFERRED;
}
if (config.mSlaac)
{
flags |= SPINEL_NET_FLAG_SLAAC;
}
if (config.mDhcp)
{
flags |= SPINEL_NET_FLAG_DHCP;
}
if (config.mDefaultRoute)
{
flags |= SPINEL_NET_FLAG_DEFAULT_ROUTE;
}
if (config.mConfigure)
{
flags |= SPINEL_NET_FLAG_CONFIGURE;
}
if (config.mOnMesh)
{
flags |= SPINEL_NET_FLAG_ON_MESH;
}
flags |= (config.mPreference << SPINEL_NET_FLAG_PREFERENCE_OFFSET);
return flags;
}
// ----------------------------------------------------------------------------
// MARK: Class Boilerplate
// ----------------------------------------------------------------------------
NcpBase *NcpBase::sNcpInstance = NULL;
NcpBase::NcpBase(otInstance *aInstance):
mInstance(aInstance),
mTxFrameBuffer(mTxBuffer, sizeof(mTxBuffer)),
mLastStatus(SPINEL_STATUS_OK),
mSupportedChannelMask(kPhySupportedChannelMask),
mChannelMask(kPhySupportedChannelMask),
mScanPeriod(200), // ms
mUpdateChangedPropsTask(aInstance->mIp6.mTaskletScheduler, &NcpBase::UpdateChangedProps, this),
mChangedFlags(NCP_PLAT_RESET_REASON),
mShouldSignalEndOfScan(false),
#if OPENTHREAD_ENABLE_JAM_DETECTION
mShouldSignalJamStateChange(false),
#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;
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
// ----------------------------------------------------------------------------
ThreadError NcpBase::OutboundFrameBegin(void)
{
return mTxFrameBuffer.InFrameBegin();
}
ThreadError NcpBase::OutboundFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength)
{
return mTxFrameBuffer.InFrameFeedData(aDataBuffer, aDataBufferLength);
}
ThreadError NcpBase::OutboundFrameFeedMessage(otMessage *aMessage)
{
return mTxFrameBuffer.InFrameFeedMessage(aMessage);
}
ThreadError NcpBase::OutboundFrameEnd(void)
{
return mTxFrameBuffer.InFrameEnd();
}
#if OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
void NcpBase::HandleBorderAgentProxyStream(otMessage *aMessage, uint16_t aLocator, uint16_t aPort, void *aContext)
{
static_cast<NcpBase *>(aContext)->HandleBorderAgentProxyStream(aMessage, aLocator, aPort);
}
void NcpBase::HandleBorderAgentProxyStream(otMessage *aMessage, uint16_t aLocator, uint16_t aPort)
{
ThreadError errorCode = kThreadError_None;
uint16_t length = otMessageGetLength(aMessage);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(
errorCode = 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_BA_PROXY_STREAM,
length
));
SuccessOrExit(errorCode = OutboundFrameFeedMessage(aMessage));
SuccessOrExit(errorCode = 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(errorCode = OutboundFrameSend());
exit:
if (aMessage != NULL)
{
otMessageFree(aMessage);
}
if (errorCode != kThreadError_None)
{
SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_DROPPED);
}
}
#endif // OPENTHREAD_ENABLE_BORDER_AGENT_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)
{
ThreadError errorCode = kThreadError_None;
bool isSecure = otMessageIsLinkSecurityEnabled(aMessage);
uint16_t length = otMessageGetLength(aMessage);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(
errorCode = 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(errorCode = 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(errorCode = OutboundFrameSend());
exit:
if (aMessage != NULL)
{
otMessageFree(aMessage);
}
if (errorCode != kThreadError_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 RadioPacket *aFrame, void *aContext)
{
static_cast<NcpBase *>(aContext)->HandleRawFrame(aFrame);
}
void NcpBase::HandleRawFrame(const RadioPacket *aFrame)
{
ThreadError errorCode = kThreadError_None;
uint16_t flags = 0;
if (!mIsRawStreamEnabled)
{
goto exit;
}
SuccessOrExit(errorCode = OutboundFrameBegin());
if (aFrame->mDidTX)
{
flags |= SPINEL_MD_FLAG_TX;
}
// Append frame header and frame length
SuccessOrExit(
errorCode = 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(
errorCode = OutboundFrameFeedData(
aFrame->mPsdu,
aFrame->mLength
)
);
// Append metadata (rssi, etc)
SuccessOrExit(
errorCode = OutboundFrameFeedPacked(
SPINEL_DATATYPE_INT8_S
SPINEL_DATATYPE_INT8_S
SPINEL_DATATYPE_UINT16_S
SPINEL_DATATYPE_STRUCT_S( // PHY-data
"" // Empty for now
)
SPINEL_DATATYPE_STRUCT_S( // Vendor-data
"" // Empty for now
),
aFrame->mPower, // TX Power
-128, // Noise Floor (Currently unused)
flags // Flags
// Skip PHY and Vendor data for now
)
);
SuccessOrExit(errorCode = 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 *result)
{
ThreadError errorCode;
if (result)
{
uint8_t flags = static_cast<uint8_t>(result->mVersion << SPINEL_BEACON_THREAD_FLAG_VERSION_SHIFT);
if (result->mIsJoinable)
{
flags |= SPINEL_BEACON_THREAD_FLAG_JOINABLE;
}
if (result->mIsNative)
{
flags |= SPINEL_BEACON_THREAD_FLAG_NATIVE;
}
NcpBase::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_V1_S
),
result->mChannel,
result->mRssi,
result->mExtAddress.m8, // laddr
0xFFFF, // saddr, Not given
result->mPanId,
result->mLqi,
SPINEL_PROTOCOL_TYPE_THREAD,
flags,
result->mNetworkName.m8,
result->mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE
);
}
else
{
// We are finished with the scan, so send out
// a property update indicating such.
errorCode = 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 inidciator message now (no
// buffer space), we set `mShouldSignalEndOfScan` to true to send
// it out when buffer space becomes available.
if (errorCode != kThreadError_None)
{
mShouldSignalEndOfScan = true;
}
}
}
void NcpBase::HandleEnergyScanResult_Jump(otEnergyScanResult *aResult, void *aContext)
{
static_cast<NcpBase *>(aContext)->HandleEnergyScanResult(aResult);
}
void NcpBase::HandleEnergyScanResult(otEnergyScanResult *aResult)
{
ThreadError errorCode;
if (aResult)
{
NcpBase::SendPropertyUpdate(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_INSERTED,
SPINEL_PROP_MAC_ENERGY_SCAN_RESULT,
"Cc",
aResult->mChannel,
aResult->mMaxRssi
);
}
else
{
// We are finished with the scan, so send out
// a property update indicating such.
errorCode = 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 (errorCode != kThreadError_None)
{
mShouldSignalEndOfScan = true;
}
}
}
// ----------------------------------------------------------------------------
// MARK: Raw Link-Layer Datapath Glue
// ----------------------------------------------------------------------------
#if OPENTHREAD_ENABLE_RAW_LINK_API
void NcpBase::LinkRawReceiveDone(otInstance *, RadioPacket *aPacket, ThreadError aError)
{
sNcpInstance->LinkRawReceiveDone(aPacket, aError);
}
void NcpBase::LinkRawReceiveDone(RadioPacket *aPacket, ThreadError aError)
{
ThreadError errorCode = kThreadError_None;
uint16_t flags = 0;
SuccessOrExit(errorCode = OutboundFrameBegin());
if (aPacket->mDidTX)
{
flags |= SPINEL_MD_FLAG_TX;
}
// Append frame header and frame length
SuccessOrExit(
errorCode = 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 == kThreadError_None) ? aPacket->mLength : 0
)
);
if (aError == kThreadError_None)
{
// Append the frame contents
SuccessOrExit(
errorCode = OutboundFrameFeedData(
aPacket->mPsdu,
aPacket->mLength
)
);
}
// Append metadata (rssi, etc)
SuccessOrExit(
errorCode = 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
),
aPacket->mPower, // TX Power
-128, // Noise Floor (Currently unused)
flags, // Flags
aPacket->mChannel, // Receive channel
aPacket->mLqi, // Link quality indicator
aError // Receive error
)
);
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
return;
}
void NcpBase::LinkRawTransmitDone(otInstance *, RadioPacket *aPacket, bool aFramePending, ThreadError aError)
{
sNcpInstance->LinkRawTransmitDone(aPacket, aFramePending, aError);
}
void NcpBase::LinkRawTransmitDone(RadioPacket *, bool aFramePending, ThreadError 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 flags, void *context)
{
NcpBase *obj = static_cast<NcpBase *>(context);
obj->mChangedFlags |= flags;
obj->mUpdateChangedPropsTask.Post();
}
void NcpBase::UpdateChangedProps(void *context)
{
NcpBase *obj = static_cast<NcpBase *>(context);
obj->UpdateChangedProps();
}
void NcpBase::UpdateChangedProps(void)
{
while (mChangedFlags != 0)
{
if ((mChangedFlags & NCP_PLAT_RESET_REASON) != 0)
{
SuccessOrExit(SendLastStatus(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
ResetReasonToSpinelStatus(otPlatGetResetReason(mInstance))
));
mChangedFlags &= ~static_cast<uint32_t>(NCP_PLAT_RESET_REASON);
}
else if ((mChangedFlags & OT_IP6_LL_ADDR_CHANGED) != 0)
{
SuccessOrExit(HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_IPV6_LL_ADDR
));
mChangedFlags &= ~static_cast<uint32_t>(OT_IP6_LL_ADDR_CHANGED);
}
else if ((mChangedFlags & OT_IP6_ML_ADDR_CHANGED) != 0)
{
SuccessOrExit(HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_IPV6_ML_ADDR
));
mChangedFlags &= ~static_cast<uint32_t>(OT_IP6_ML_ADDR_CHANGED);
}
else if ((mChangedFlags & OT_NET_ROLE) != 0)
{
if (mRequireJoinExistingNetwork)
{
switch (otThreadGetDeviceRole(mInstance))
{
case kDeviceRoleDetached:
case kDeviceRoleDisabled:
break;
default:
mRequireJoinExistingNetwork = false;
break;
}
if ( (otThreadGetDeviceRole(mInstance) == kDeviceRoleLeader)
&& otThreadIsSingleton(mInstance)
#if OPENTHREAD_ENABLE_LEGACY
&& !mLegacyNodeDidJoin
#endif
) {
mChangedFlags &= ~static_cast<uint32_t>(OT_NET_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_NET_ROLE);
}
else if ((mChangedFlags & OT_NET_PARTITION_ID) != 0)
{
SuccessOrExit(HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_NET_PARTITION_ID
));
mChangedFlags &= ~static_cast<uint32_t>(OT_NET_PARTITION_ID);
}
else if ((mChangedFlags & OT_NET_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_NET_KEY_SEQUENCE_COUNTER);
}
else if ((mChangedFlags & (OT_IP6_ADDRESS_ADDED | OT_IP6_ADDRESS_REMOVED)) != 0)
{
SuccessOrExit(HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_IPV6_ADDRESS_TABLE
));
mChangedFlags &= ~static_cast<uint32_t>(OT_IP6_ADDRESS_ADDED | OT_IP6_ADDRESS_REMOVED);
}
else if ((mChangedFlags & (OT_THREAD_CHILD_ADDED | OT_THREAD_CHILD_REMOVED)) != 0)
{
SuccessOrExit(HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_THREAD_CHILD_TABLE
));
mChangedFlags &= ~static_cast<uint32_t>(OT_THREAD_CHILD_ADDED | OT_THREAD_CHILD_REMOVED);
}
else if ((mChangedFlags & OT_THREAD_NETDATA_UPDATED) != 0)
{
SuccessOrExit(HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_THREAD_LEADER_NETWORK_DATA
));
mChangedFlags &= ~static_cast<uint32_t>(OT_THREAD_NETDATA_UPDATED);
// If the network data is updated, after successfully sending (or queuing) the
// network data spinel message, we add `NCP_ON_MESH_NETS_CHANGED_BIT_FLAG` to
// the `mChangedFlags` so that we separately send the list of on-mesh prefixes.
mChangedFlags |= NCP_ON_MESH_NETS_CHANGED_BIT_FLAG;
}
else if ((mChangedFlags & NCP_ON_MESH_NETS_CHANGED_BIT_FLAG) != 0)
{
SuccessOrExit(HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_THREAD_ON_MESH_NETS
));
mChangedFlags &= ~static_cast<uint32_t>(NCP_ON_MESH_NETS_CHANGED_BIT_FLAG);
}
else if ((mChangedFlags & (OT_IP6_RLOC_ADDED | OT_IP6_RLOC_REMOVED)) != 0)
{
mChangedFlags &= ~static_cast<uint32_t>(OT_IP6_RLOC_ADDED | OT_IP6_RLOC_REMOVED);
}
}
exit:
return;
}
// ----------------------------------------------------------------------------
// MARK: Serial Traffic Glue
// ----------------------------------------------------------------------------
ThreadError NcpBase::OutboundFrameSend(void)
{
ThreadError errorCode;
SuccessOrExit(errorCode = OutboundFrameEnd());
mTxSpinelFrameCounter++;
exit:
return errorCode;
}
void NcpBase::HandleReceive(const uint8_t *buf, uint16_t bufLength)
{
uint8_t header = 0;
unsigned int command = 0;
spinel_ssize_t parsedLength;
const uint8_t *arg_ptr = NULL;
unsigned int arg_len = 0;
ThreadError errorCode = kThreadError_None;
spinel_tid_t tid = 0;
parsedLength = spinel_datatype_unpack(buf, bufLength, SPINEL_DATATYPE_COMMAND_S SPINEL_DATATYPE_DATA_S, &header, &command, &arg_ptr, &arg_len);
tid = SPINEL_HEADER_GET_TID(header);
if (parsedLength == bufLength)
{
errorCode = HandleCommand(header, command, arg_ptr, static_cast<uint16_t>(arg_len));
// Check if we may have missed a `tid` in the sequence.
if ((mNextExpectedTid != 0) && (tid != mNextExpectedTid))
{
mRxSpinelOutOfOrderTidCounter++;
}
mNextExpectedTid = SPINEL_GET_NEXT_TID(tid);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
if (errorCode == kThreadError_NoBufs)
{
// 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::HandleSpaceAvailableInTxBuffer(void)
{
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
UpdateChangedProps();
exit:
return;
}
void NcpBase::IncrementFrameErrorCounter(void)
{
mFramingErrorCounter++;
}
// ----------------------------------------------------------------------------
// MARK: Inbound Command Handlers
// ----------------------------------------------------------------------------
ThreadError NcpBase::HandleCommand(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len)
{
unsigned i;
ThreadError errorCode = kThreadError_None;
// Skip if this isn't a spinel frame
VerifyOrExit((SPINEL_HEADER_FLAG & header) == SPINEL_HEADER_FLAG, errorCode = kThreadError_InvalidArgs);
// We only support IID zero for now.
VerifyOrExit(
SPINEL_HEADER_GET_IID(header) == 0,
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_INTERFACE)
);
for (i = 0; i < sizeof(mCommandHandlerTable) / sizeof(mCommandHandlerTable[0]); i++)
{
if (mCommandHandlerTable[i].mCommand == command)
{
break;
}
}
if (i < sizeof(mCommandHandlerTable) / sizeof(mCommandHandlerTable[0]))
{
errorCode = (this->*mCommandHandlerTable[i].mHandler)(header, command, arg_ptr, arg_len);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_COMMAND);
}
exit:
return errorCode;
}
ThreadError NcpBase::HandleCommandPropertyGet(uint8_t header, spinel_prop_key_t key)
{
unsigned i;
ThreadError errorCode = kThreadError_None;
for (i = 0; i < sizeof(mGetPropertyHandlerTable) / sizeof(mGetPropertyHandlerTable[0]); i++)
{
if (mGetPropertyHandlerTable[i].mPropKey == key)
{
break;
}
}
if (i < sizeof(mGetPropertyHandlerTable) / sizeof(mGetPropertyHandlerTable[0]))
{
errorCode = (this->*mGetPropertyHandlerTable[i].mHandler)(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PROP_NOT_FOUND);
}
return errorCode;
}
ThreadError NcpBase::HandleCommandPropertySet(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
unsigned i;
ThreadError errorCode = kThreadError_None;
for (i = 0; i < sizeof(mSetPropertyHandlerTable) / sizeof(mSetPropertyHandlerTable[0]); i++)
{
if (mSetPropertyHandlerTable[i].mPropKey == key)
{
break;
}
}
if (i < sizeof(mSetPropertyHandlerTable) / sizeof(mSetPropertyHandlerTable[0]))
{
errorCode = (this->*mSetPropertyHandlerTable[i].mHandler)(header, key, value_ptr, value_len);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PROP_NOT_FOUND);
}
return errorCode;
}
ThreadError NcpBase::HandleCommandPropertyInsert(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
unsigned i;
ThreadError errorCode = kThreadError_None;
for (i = 0; i < sizeof(mInsertPropertyHandlerTable) / sizeof(mInsertPropertyHandlerTable[0]); i++)
{
if (mInsertPropertyHandlerTable[i].mPropKey == key)
{
break;
}
}
if (i < sizeof(mInsertPropertyHandlerTable) / sizeof(mInsertPropertyHandlerTable[0]))
{
errorCode = (this->*mInsertPropertyHandlerTable[i].mHandler)(header, key, value_ptr, value_len);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PROP_NOT_FOUND);
}
return errorCode;
}
ThreadError NcpBase::HandleCommandPropertyRemove(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
unsigned i;
ThreadError errorCode = kThreadError_None;
for (i = 0; i < sizeof(mRemovePropertyHandlerTable) / sizeof(mRemovePropertyHandlerTable[0]); i++)
{
if (mRemovePropertyHandlerTable[i].mPropKey == key)
{
break;
}
}
if (i < sizeof(mRemovePropertyHandlerTable) / sizeof(mRemovePropertyHandlerTable[0]))
{
errorCode = (this->*mRemovePropertyHandlerTable[i].mHandler)(header, key, value_ptr, value_len);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PROP_NOT_FOUND);
}
return errorCode;
}
// ----------------------------------------------------------------------------
// MARK: Outbound Command Handlers
// ----------------------------------------------------------------------------
ThreadError NcpBase::SendLastStatus(uint8_t header, spinel_status_t lastStatus)
{
if (SPINEL_HEADER_GET_IID(header) == 0)
{
mLastStatus = lastStatus;
}
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_LAST_STATUS,
SPINEL_DATATYPE_UINT_PACKED_S,
lastStatus
);
}
ThreadError NcpBase::SendPropertyUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key,
const char *pack_format, ...)
{
ThreadError errorCode = kThreadError_None;
va_list args;
va_start(args, pack_format);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, command, key));
SuccessOrExit(errorCode = OutboundFrameFeedVPacked(pack_format, args));
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
va_end(args);
return errorCode;
}
ThreadError NcpBase::SendPropertyUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
ThreadError errorCode = kThreadError_None;
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, command, key));
SuccessOrExit(errorCode = OutboundFrameFeedData(value_ptr, value_len));
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
return errorCode;
}
ThreadError NcpBase::SendPropertyUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key, otMessage *aMessage)
{
ThreadError errorCode = kThreadError_None;
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, command, key));
SuccessOrExit(errorCode = OutboundFrameFeedMessage(aMessage));
// Set the aMessage pointer to NULL, to indicate that it does not need to be freed at the exit.
// The message 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(errorCode = OutboundFrameSend());
exit:
if (aMessage != NULL)
{
otMessageFree(aMessage);
}
return errorCode;
}
ThreadError NcpBase::OutboundFrameFeedVPacked(const char *pack_format, va_list args)
{
uint8_t buf[96];
ThreadError errorCode = kThreadError_NoBufs;
spinel_ssize_t packed_len;
packed_len = spinel_datatype_vpack(buf, sizeof(buf), pack_format, args);
if ((packed_len > 0) && (packed_len <= static_cast<spinel_ssize_t>(sizeof(buf))))
{
errorCode = OutboundFrameFeedData(buf, static_cast<uint16_t>(packed_len));
}
return errorCode;
}
ThreadError NcpBase::OutboundFrameFeedPacked(const char *pack_format, ...)
{
ThreadError errorCode;
va_list args;
va_start(args, pack_format);
errorCode = OutboundFrameFeedVPacked(pack_format, args);
va_end(args);
return errorCode;
}
// ----------------------------------------------------------------------------
// MARK: Individual Command Handlers
// ----------------------------------------------------------------------------
ThreadError NcpBase::CommandHandler_NOOP(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len)
{
(void)command;
(void)arg_ptr;
(void)arg_len;
return SendLastStatus(header, SPINEL_STATUS_OK);
}
ThreadError NcpBase::CommandHandler_RESET(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
uint16_t arg_len)
{
ThreadError errorCode = kThreadError_None;
// We aren't using any of the arguments to this function.
(void)header;
(void)command;
(void)arg_ptr;
(void)arg_len;
// 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);
errorCode = SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_RESET_SOFTWARE);
if (errorCode != kThreadError_None)
{
mChangedFlags |= NCP_PLAT_RESET_REASON;
mUpdateChangedPropsTask.Post();
}
return errorCode;
}
ThreadError NcpBase::CommandHandler_PROP_VALUE_GET(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
uint16_t arg_len)
{
unsigned int propKey = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, SPINEL_DATATYPE_UINT_PACKED_S, &propKey);
if (parsedLength > 0)
{
errorCode = HandleCommandPropertyGet(header, static_cast<spinel_prop_key_t>(propKey));
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
(void)command;
return errorCode;
}
ThreadError NcpBase::CommandHandler_PROP_VALUE_SET(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
uint16_t arg_len)
{
unsigned int propKey = 0;
spinel_ssize_t parsedLength;
const uint8_t *value_ptr;
unsigned int value_len;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_DATA_S, &propKey, &value_ptr, &value_len);
if (parsedLength == arg_len)
{
errorCode = HandleCommandPropertySet(header, static_cast<spinel_prop_key_t>(propKey), value_ptr,
static_cast<uint16_t>(value_len));
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
(void)command;
return errorCode;
}
ThreadError NcpBase::CommandHandler_PROP_VALUE_INSERT(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
uint16_t arg_len)
{
unsigned int propKey = 0;
spinel_ssize_t parsedLength;
const uint8_t *value_ptr;
unsigned int value_len;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_DATA_S, &propKey, &value_ptr, &value_len);
if (parsedLength == arg_len)
{
errorCode = HandleCommandPropertyInsert(header, static_cast<spinel_prop_key_t>(propKey), value_ptr,
static_cast<uint16_t>(value_len));
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
(void)command;
return errorCode;
}
ThreadError NcpBase::CommandHandler_PROP_VALUE_REMOVE(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
uint16_t arg_len)
{
unsigned int propKey = 0;
spinel_ssize_t parsedLength;
const uint8_t *value_ptr;
unsigned int value_len;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_DATA_S, &propKey, &value_ptr, &value_len);
if (parsedLength == arg_len)
{
errorCode = HandleCommandPropertyRemove(header, static_cast<spinel_prop_key_t>(propKey), value_ptr,
static_cast<uint16_t>(value_len));
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
(void)command;
return errorCode;
}
ThreadError NcpBase::CommandHandler_NET_SAVE(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
uint16_t arg_len)
{
(void)command;
(void)arg_ptr;
(void)arg_len;
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
}
ThreadError NcpBase::CommandHandler_NET_CLEAR(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
uint16_t arg_len)
{
(void)command;
(void)arg_ptr;
(void)arg_len;
return SendLastStatus(header, ThreadErrorToSpinelStatus(otInstanceErasePersistentInfo(mInstance)));
}
ThreadError NcpBase::CommandHandler_NET_RECALL(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
uint16_t arg_len)
{
(void)command;
(void)arg_ptr;
(void)arg_len;
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
}
// ----------------------------------------------------------------------------
// MARK: Individual Property Getters
// ----------------------------------------------------------------------------
ThreadError NcpBase::GetPropertyHandler_LAST_STATUS(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(header, SPINEL_CMD_PROP_VALUE_IS, key, SPINEL_DATATYPE_UINT_PACKED_S, mLastStatus);
}
ThreadError NcpBase::GetPropertyHandler_PROTOCOL_VERSION(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S,
SPINEL_PROTOCOL_VERSION_THREAD_MAJOR,
SPINEL_PROTOCOL_VERSION_THREAD_MINOR
);
}
ThreadError NcpBase::GetPropertyHandler_INTERFACE_TYPE(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT_PACKED_S,
SPINEL_PROTOCOL_TYPE_THREAD
);
}
ThreadError NcpBase::GetPropertyHandler_VENDOR_ID(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT_PACKED_S,
0 // Vendor ID. Zero for unknown.
);
}
ThreadError NcpBase::GetPropertyHandler_CAPS(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
// Begin adding capabilities //////////////////////////////////////////////
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_NET_THREAD_1_0));
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_COUNTERS));
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_MAC_WHITELIST));
#if OPENTHREAD_ENABLE_RAW_LINK_API
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_MAC_RAW));
#endif
#if OPENTHREAD_ENABLE_JAM_DETECTION
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_JAM_DETECT));
#endif
// TODO: Somehow get the following capability from the radio.
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S,
SPINEL_CAP_802_15_4_2450MHZ_OQPSK));
#if OPENTHREAD_CONFIG_MAX_CHILDREN > 0
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_ROLE_ROUTER));
#endif
#if OPENTHREAD_ENABLE_LEGACY
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_NEST_LEGACY_INTERFACE));
#endif
#if OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_THREAD_BA_PROXY));
#endif
// End adding capabilities /////////////////////////////////////////////////
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_NCP_VERSION(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UTF8_S,
otGetVersionString()
);
}
ThreadError NcpBase::GetPropertyHandler_INTERFACE_COUNT(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
1 // Only one interface for now
);
}
ThreadError NcpBase::GetPropertyHandler_POWER_STATE(uint8_t header, spinel_prop_key_t key)
{
// Always online at the moment
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
SPINEL_POWER_STATE_ONLINE
);
}
ThreadError NcpBase::GetPropertyHandler_HWADDR(uint8_t header, spinel_prop_key_t key)
{
otExtAddress hwAddr;
otLinkGetFactoryAssignedIeeeEui64(mInstance, &hwAddr);
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_EUI64_S,
hwAddr.m8
);
}
ThreadError NcpBase::GetPropertyHandler_LOCK(uint8_t header, spinel_prop_key_t key)
{
// TODO: Implement property lock (Needs API!)
(void)key;
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
}
ThreadError NcpBase::GetPropertyHandler_PHY_ENABLED(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
#if OPENTHREAD_ENABLE_RAW_LINK_API
otLinkRawIsEnabled(mInstance)
#else
false
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
);
}
ThreadError NcpBase::GetPropertyHandler_PHY_FREQ(uint8_t header, spinel_prop_key_t key)
{
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(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT32_S,
freq_khz
);
}
ThreadError NcpBase::GetPropertyHandler_PHY_CHAN_SUPPORTED(uint8_t header, spinel_prop_key_t key)
{
return GetPropertyHandler_ChannelMaskHelper(header, key, mSupportedChannelMask);
}
ThreadError NcpBase::GetPropertyHandler_PHY_CHAN(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
otLinkGetChannel(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_PHY_RSSI(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_INT8_S,
otPlatRadioGetRssi(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_PHY_TX_POWER(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_INT8_S,
otLinkGetMaxTransmitPower(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_MAC_SCAN_STATE(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
#if OPENTHREAD_ENABLE_RAW_LINK_API
if (otLinkRawIsEnabled(mInstance))
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
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))
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
SPINEL_SCAN_STATE_BEACON
);
}
else if (otLinkIsEnergyScanInProgress(mInstance))
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
SPINEL_SCAN_STATE_ENERGY
);
}
else
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
SPINEL_SCAN_STATE_IDLE
);
}
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_MAC_SCAN_PERIOD(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT16_S,
mScanPeriod
);
}
ThreadError NcpBase::GetPropertyHandler_ChannelMaskHelper(uint8_t header, spinel_prop_key_t key, uint32_t channel_mask)
{
ThreadError errorCode = kThreadError_None;
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
for (int i = 0; i < 32; i++)
{
if (0 != (channel_mask & (1 << i)))
{
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT8_S, i));
}
}
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_MAC_SCAN_MASK(uint8_t header, spinel_prop_key_t key)
{
return GetPropertyHandler_ChannelMaskHelper(header, key, mChannelMask);
}
ThreadError NcpBase::GetPropertyHandler_MAC_15_4_PANID(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT16_S,
otLinkGetPanId(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_MAC_PROMISCUOUS_MODE(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_INT8_S,
otPlatRadioGetPromiscuous(mInstance)
? SPINEL_MAC_PROMISCUOUS_MODE_FULL
: SPINEL_MAC_PROMISCUOUS_MODE_OFF
);
}
ThreadError NcpBase::GetPropertyHandler_MAC_15_4_LADDR(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_EUI64_S,
otLinkGetExtendedAddress(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_MAC_15_4_SADDR(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT16_S,
otLinkGetShortAddress(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_MAC_EXTENDED_ADDR(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_EUI64_S,
otLinkGetExtendedAddress(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_MAC_RAW_STREAM_ENABLED(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
mIsRawStreamEnabled
);
}
ThreadError NcpBase::GetPropertyHandler_NET_SAVED(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
otDatasetIsCommissioned(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_NET_IF_UP(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
otIp6IsEnabled(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_NET_STACK_UP(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
otThreadGetDeviceRole(mInstance) != kDeviceRoleDisabled
);
}
ThreadError NcpBase::GetPropertyHandler_NET_ROLE(uint8_t header, spinel_prop_key_t key)
{
spinel_net_role_t role(SPINEL_NET_ROLE_DETACHED);
switch (otThreadGetDeviceRole(mInstance))
{
case kDeviceRoleOffline:
case kDeviceRoleDisabled:
case kDeviceRoleDetached:
role = SPINEL_NET_ROLE_DETACHED;
break;
case kDeviceRoleChild:
role = SPINEL_NET_ROLE_CHILD;
break;
case kDeviceRoleRouter:
role = SPINEL_NET_ROLE_ROUTER;
break;
case kDeviceRoleLeader:
role = SPINEL_NET_ROLE_LEADER;
break;
}
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
role
);
}
ThreadError NcpBase::GetPropertyHandler_NET_NETWORK_NAME(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UTF8_S,
otThreadGetNetworkName(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_NET_XPANID(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_DATA_S,
otThreadGetExtendedPanId(mInstance),
sizeof(spinel_net_xpanid_t)
);
}
ThreadError NcpBase::GetPropertyHandler_NET_MASTER_KEY(uint8_t header, spinel_prop_key_t key)
{
const uint8_t *ptr(NULL);
uint8_t len(0);
ptr = otThreadGetMasterKey(mInstance, &len);
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_DATA_S,
ptr,
len
);
}
ThreadError NcpBase::GetPropertyHandler_NET_KEY_SEQUENCE_COUNTER(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT32_S,
otThreadGetKeySequenceCounter(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_NET_PARTITION_ID(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT32_S,
otThreadGetPartitionId(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_NET_KEY_SWITCH_GUARDTIME(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT32_S,
otThreadGetKeySwitchGuardTime(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA_VERSION(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
otNetDataGetVersion(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA_VERSION(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
otNetDataGetStableVersion(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
uint8_t network_data[255];
uint8_t network_data_len = 255;
otNetDataGetLocal(
mInstance,
false, // Stable?
network_data,
&network_data_len
);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
SuccessOrExit(errorCode = OutboundFrameFeedData(network_data, network_data_len));
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
uint8_t network_data[255];
uint8_t network_data_len = 255;
otNetDataGetLocal(
mInstance,
true, // Stable?
network_data,
&network_data_len
);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
SuccessOrExit(errorCode = OutboundFrameFeedData(network_data, network_data_len));
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_THREAD_LEADER_NETWORK_DATA(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
uint8_t network_data[255];
uint8_t network_data_len = 255;
otNetDataGetLeader(
mInstance,
false, // Stable?
network_data,
&network_data_len
);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
SuccessOrExit(errorCode = OutboundFrameFeedData(network_data, network_data_len));
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_THREAD_STABLE_LEADER_NETWORK_DATA(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
uint8_t network_data[255];
uint8_t network_data_len = 255;
otNetDataGetLeader(
mInstance,
true, // Stable?
network_data,
&network_data_len
);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
SuccessOrExit(errorCode = OutboundFrameFeedData(network_data, network_data_len));
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_THREAD_LEADER_RID(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
otThreadGetLeaderRouterId(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
otThreadGetLocalLeaderWeight(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_LEADER_WEIGHT(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
otThreadGetLeaderWeight(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_LEADER_ADDR(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
otIp6Address address;
errorCode = otThreadGetLeaderRloc(mInstance, &address);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_IPv6ADDR_S,
&address
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_THREAD_PARENT(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
otRouterInfo parentInfo;
errorCode = otThreadGetParentInfo(mInstance, &parentInfo);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT16_S,
parentInfo.mExtAddress.m8,
parentInfo.mRloc16
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
otChildInfo childInfo;
uint8_t maxChildren;
uint8_t modeFlags;
mDisableStreamWrite = true;
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
maxChildren = otThreadGetMaxAllowedChildren(mInstance);
for (uint8_t index = 0; index < maxChildren; index++)
{
errorCode = otThreadGetChildInfoByIndex(mInstance, index, &childInfo);
if (errorCode != kThreadError_None)
{
continue;
}
modeFlags = 0;
if (childInfo.mRxOnWhenIdle)
{
modeFlags |= kThreadMode_RxOnWhenIdle;
}
if (childInfo.mSecureDataRequest)
{
modeFlags |= kThreadMode_SecureDataRequest;
}
if (childInfo.mFullFunction)
{
modeFlags |= kThreadMode_FullFunctionDevice;
}
if (childInfo.mFullNetworkData)
{
modeFlags |= kThreadMode_FullNetworkData;
}
SuccessOrExit(
errorCode = 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(errorCode = OutboundFrameSend());
exit:
mDisableStreamWrite = false;
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_THREAD_NEIGHBOR_TABLE(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
otNeighborInfoIterator iter = OT_NEIGHBOR_INFO_ITERATOR_INIT;
otNeighborInfo neighInfo;
uint8_t modeFlags;
mDisableStreamWrite = true;
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
while (otThreadGetNextNeighborInfo(mInstance, &iter, &neighInfo) == kThreadError_None)
{
modeFlags = 0;
if (neighInfo.mRxOnWhenIdle)
{
modeFlags |= kThreadMode_RxOnWhenIdle;
}
if (neighInfo.mSecureDataRequest)
{
modeFlags |= kThreadMode_SecureDataRequest;
}
if (neighInfo.mFullFunction)
{
modeFlags |= kThreadMode_FullFunctionDevice;
}
if (neighInfo.mFullNetworkData)
{
modeFlags |= kThreadMode_FullNetworkData;
}
SuccessOrExit(
errorCode = 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(errorCode = OutboundFrameSend());
exit:
mDisableStreamWrite = false;
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
uint8_t num_entries = 0;
const uint16_t *ports = otIp6GetUnsecurePorts(mInstance, &num_entries);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
for (; num_entries != 0; ports++, num_entries--)
{
SuccessOrExit(errorCode = OutboundFrameFeedPacked("S", *ports));
}
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
mAllowLocalNetworkDataChange
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
otThreadIsRouterRoleEnabled(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_ON_MESH_NETS(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
otBorderRouterConfig border_router_config;
uint8_t flags;
mDisableStreamWrite = true;
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
// Fill from non-local network data first
for (otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT ;;)
{
errorCode = otNetDataGetNextPrefixInfo(mInstance, false, &iter, &border_router_config);
if (errorCode != kThreadError_None)
{
break;
}
flags = BorderRouterConfigToFlagByte(border_router_config);
SuccessOrExit(errorCode = 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
),
&border_router_config.mPrefix,
64,
border_router_config.mStable,
flags,
true
));
}
// Fill from local network data last
for (otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT ;;)
{
errorCode = otNetDataGetNextPrefixInfo(mInstance, true, &iter, &border_router_config);
if (errorCode != kThreadError_None)
{
break;
}
flags = BorderRouterConfigToFlagByte(border_router_config);
SuccessOrExit(errorCode = 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
),
&border_router_config.mPrefix,
64,
border_router_config.mStable,
flags,
false
));
}
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
mDisableStreamWrite = false;
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_IPV6_ML_PREFIX(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
const uint8_t *ml_prefix = otThreadGetMeshLocalPrefix(mInstance);
if (ml_prefix)
{
otIp6Address addr;
memcpy(addr.mFields.m8, ml_prefix, 8);
// Zero out the last 8 bytes.
memset(addr.mFields.m8 + 8, 0, 8);
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_UINT8_S,
&addr,
64
);
}
else
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_VOID_S
);
}
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_IPV6_ML_ADDR(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
const otIp6Address *ml64 = otThreadGetMeshLocalEid(mInstance);
if (ml64)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_IPv6ADDR_S,
ml64
);
}
else
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_VOID_S
);
}
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_IPV6_LL_ADDR(uint8_t header, spinel_prop_key_t key)
{
// TODO!
(void)key;
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
}
ThreadError NcpBase::GetPropertyHandler_IPV6_ADDRESS_TABLE(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
mDisableStreamWrite = true;
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
for (const otNetifAddress *address = otIp6GetUnicastAddresses(mInstance); address; address = address->mNext)
{
SuccessOrExit(errorCode = OutboundFrameFeedPacked(
SPINEL_DATATYPE_STRUCT_S("6CLL"),
&address->mAddress,
address->mPrefixLength,
address->mPreferred ? 0xffffffff : 0,
address->mValid ? 0xffffffff : 0
));
}
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
mDisableStreamWrite = false;
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_IPV6_ROUTE_TABLE(uint8_t header, spinel_prop_key_t key)
{
// TODO: Implement get route table
(void)key;
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
}
ThreadError NcpBase::GetPropertyHandler_IPV6_ICMP_PING_OFFLOAD(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
otIcmp6IsEchoEnabled(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU(uint8_t header, spinel_prop_key_t key)
{
// Note reverse logic: passthru enabled = filter disabled
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
!otIp6IsReceiveFilterEnabled(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_LOCAL_ROUTES(uint8_t header, spinel_prop_key_t key)
{
// TODO: Implement get external route table
(void)key;
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
}
ThreadError NcpBase::GetPropertyHandler_STREAM_NET(uint8_t header, spinel_prop_key_t key)
{
// TODO: Implement explicit data poll.
(void)key;
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
}
#if OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
ThreadError NcpBase::GetPropertyHandler_BA_PROXY_ENABLED(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
otBorderAgentProxyIsEnabled(mInstance)
);
}
#endif // OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_JAM_DETECTION
ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_ENABLE(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
otJamDetectionIsEnabled(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_JAM_DETECTED(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
otJamDetectionGetState(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_INT8_S,
otJamDetectionGetRssiThreshold(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_WINDOW(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
otJamDetectionGetWindow(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_BUSY(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
otJamDetectionGetBusyPeriod(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_HISTORY_BITMAP(uint8_t header, spinel_prop_key_t key)
{
uint64_t historyBitmap = otJamDetectionGetHistoryBitmap(mInstance);
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_UINT32_S,
static_cast<uint32_t>(historyBitmap & 0xffffffff),
static_cast<uint32_t>(historyBitmap >> 32)
);
}
#endif // OPENTHREAD_ENABLE_JAM_DETECTION
ThreadError NcpBase::GetPropertyHandler_MAC_CNTR(uint8_t header, spinel_prop_key_t key)
{
uint32_t value;
const otMacCounters *macCounters;
ThreadError errorCode = kThreadError_None;
macCounters = otLinkGetCounters(mInstance);
assert(macCounters != NULL);
switch (key)
{
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:
errorCode = SendLastStatus(header, SPINEL_STATUS_INTERNAL_ERROR);
goto bail;
break;
}
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT32_S,
value
);
bail:
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_NCP_CNTR(uint8_t header, spinel_prop_key_t key)
{
uint32_t value;
ThreadError errorCode = kThreadError_None;
switch (key)
{
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:
errorCode = SendLastStatus(header, SPINEL_STATUS_INTERNAL_ERROR);
goto bail;
break;
}
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT32_S,
value
);
bail:
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_MSG_BUFFER_COUNTERS(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
otBufferInfo bufferInfo;
otMessageGetBufferInfo(mInstance, &bufferInfo);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
SuccessOrExit(errorCode = OutboundFrameFeedPacked("SSSSSSSSSSSSSSSS",
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.mCoapClientMessages,
bufferInfo.mCoapClientBuffers
));
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_DEBUG_TEST_ASSERT(uint8_t header, spinel_prop_key_t key)
{
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.
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
false
);
}
ThreadError NcpBase::GetPropertyHandler_DEBUG_NCP_LOG_LEVEL(uint8_t header, spinel_prop_key_t key)
{
uint8_t logLevel = 0;
switch (otGetDynamicLogLevel(mInstance))
{
case kLogLevelNone:
logLevel = SPINEL_NCP_LOG_LEVEL_EMERG;
break;
case kLogLevelCrit:
logLevel = SPINEL_NCP_LOG_LEVEL_CRIT;
break;
case kLogLevelWarn:
logLevel = SPINEL_NCP_LOG_LEVEL_WARN;
break;
case kLogLevelInfo:
logLevel = SPINEL_NCP_LOG_LEVEL_INFO;
break;
case kLogLevelDebg:
logLevel = SPINEL_NCP_LOG_LEVEL_DEBUG;
break;
}
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
logLevel
);
}
ThreadError NcpBase::GetPropertyHandler_MAC_WHITELIST(uint8_t header, spinel_prop_key_t key)
{
otMacWhitelistEntry entry;
ThreadError errorCode = kThreadError_None;
mDisableStreamWrite = true;
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
for (uint8_t i = 0; (i != 255) && (errorCode == kThreadError_None); i++)
{
errorCode = otLinkGetWhitelistEntry(mInstance, i, &entry);
if (errorCode != kThreadError_None)
{
break;
}
if (entry.mValid)
{
if (!entry.mFixedRssi)
{
entry.mRssi = RSSI_OVERRIDE_DISABLED;
}
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_STRUCT_S("Ec"), entry.mExtAddress.m8, entry.mRssi));
}
}
SuccessOrExit(errorCode = OutboundFrameSend());
exit:
mDisableStreamWrite = false;
return errorCode;
}
ThreadError NcpBase::GetPropertyHandler_MAC_WHITELIST_ENABLED(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
otLinkIsWhitelistEnabled(mInstance)
);
}
#if OPENTHREAD_FTD
ThreadError NcpBase::GetPropertyHandler_NET_PSKC(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_DATA_S,
otThreadGetPSKc(mInstance),
sizeof(spinel_net_pskc_t)
);
}
#endif // OPENTHREAD_FTD
ThreadError NcpBase::GetPropertyHandler_THREAD_MODE(uint8_t header, spinel_prop_key_t key)
{
uint8_t numeric_mode(0);
otLinkModeConfig mode_config(otThreadGetLinkMode(mInstance));
if (mode_config.mRxOnWhenIdle)
{
numeric_mode |= kThreadMode_RxOnWhenIdle;
}
if (mode_config.mSecureDataRequests)
{
numeric_mode |= kThreadMode_SecureDataRequest;
}
if (mode_config.mDeviceType)
{
numeric_mode |= kThreadMode_FullFunctionDevice;
}
if (mode_config.mNetworkData)
{
numeric_mode |= kThreadMode_FullNetworkData;
}
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
numeric_mode
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_CHILD_COUNT_MAX(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
otThreadGetMaxAllowedChildren(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_CHILD_TIMEOUT(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT32_S,
otThreadGetChildTimeout(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_RLOC16(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT16_S,
otThreadGetRloc16(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
otThreadGetRouterUpgradeThreshold(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
otThreadGetRouterDowngradeThreshold(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
otThreadGetRouterSelectionJitter(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT32_S,
otThreadGetContextIdReuseDelay(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
otThreadGetNetworkIdTimeout(mInstance)
);
}
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
ThreadError NcpBase::GetPropertyHandler_THREAD_COMMISSIONER_ENABLED(uint8_t header, spinel_prop_key_t key)
{
bool isEnabled = false;
if (otCommissionerGetState(mInstance) == kCommissionerStateActive)
isEnabled = true;
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
isEnabled
);
}
#endif
ThreadError NcpBase::GetPropertyHandler_NET_REQUIRE_JOIN_EXISTING(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
mRequireJoinExistingNetwork
);
}
#if OPENTHREAD_ENABLE_LEGACY
ThreadError NcpBase::GetPropertyHandler_NEST_LEGACY_ULA_PREFIX(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_DATA_S,
mLegacyUlaPrefix,
sizeof(mLegacyUlaPrefix)
);
}
#endif // OPENTHREAD_ENABLE_LEGACY
// ----------------------------------------------------------------------------
// MARK: Individual Property Setters
// ----------------------------------------------------------------------------
ThreadError NcpBase::SetPropertyHandler_POWER_STATE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
// TODO: Implement POWER_STATE
(void)key;
(void)value_ptr;
(void)value_len;
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
}
#if OPENTHREAD_ENABLE_RAW_LINK_API
ThreadError NcpBase::SetPropertyHandler_PHY_ENABLED(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
bool value = false;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&value
);
if (parsedLength > 0)
{
if (value == false)
{
// If we have raw stream enabled stop receiving
if (mIsRawStreamEnabled)
{
otLinkRawSleep(mInstance);
}
errorCode = otLinkRawSetEnable(mInstance, false);
}
else
{
errorCode = otLinkRawSetEnable(mInstance, true);
// If we have raw stream enabled already, start receiving
if (errorCode == kThreadError_None && mIsRawStreamEnabled)
{
errorCode = otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone);
}
}
}
else
{
errorCode = kThreadError_Parse;
}
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
return errorCode;
}
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
ThreadError NcpBase::SetPropertyHandler_PHY_TX_POWER(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
int8_t value = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_INT8_S,
&value
);
if (parsedLength > 0)
{
otLinkSetMaxTransmitPower(mInstance, value);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_PHY_CHAN(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
unsigned int i = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT_PACKED_S,
&i
);
if (parsedLength > 0)
{
errorCode = otLinkSetChannel(mInstance, static_cast<uint8_t>(i));
#if OPENTHREAD_ENABLE_RAW_LINK_API
if (errorCode == kThreadError_None)
{
// 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>(i);
// Make sure we are update the receiving channel if raw link is enabled and we have raw
// stream enabled already
if (otLinkRawIsEnabled(mInstance) && mIsRawStreamEnabled)
{
errorCode = otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone);
}
}
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_MAC_PROMISCUOUS_MODE(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
uint8_t i = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&i
);
if (parsedLength > 0)
{
switch (i)
{
case SPINEL_MAC_PROMISCUOUS_MODE_OFF:
otPlatRadioSetPromiscuous(mInstance, false);
errorCode = kThreadError_None;
break;
case SPINEL_MAC_PROMISCUOUS_MODE_NETWORK:
case SPINEL_MAC_PROMISCUOUS_MODE_FULL:
otPlatRadioSetPromiscuous(mInstance, true);
errorCode = kThreadError_None;
break;
default:
errorCode = kThreadError_InvalidArgs;
break;
}
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_MAC_SCAN_MASK(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
ThreadError errorCode = kThreadError_None;
uint32_t new_mask(0);
for (; value_len != 0; value_len--, value_ptr++)
{
if ((value_ptr[0] > 31)
|| (mSupportedChannelMask & (1 << value_ptr[0])) == 0
)
{
errorCode = kThreadError_InvalidArgs;
break;
}
new_mask |= (1 << value_ptr[0]);
}
if (errorCode == kThreadError_None)
{
mChannelMask = new_mask;
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_MAC_SCAN_PERIOD(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
uint16_t tmp(mScanPeriod);
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT16_S,
&tmp
);
if (parsedLength > 0)
{
mScanPeriod = tmp;
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_NET_REQUIRE_JOIN_EXISTING(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
bool tmp(mRequireJoinExistingNetwork);
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&tmp
);
if (parsedLength > 0)
{
mRequireJoinExistingNetwork = tmp;
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
bool HasOnly1BitSet(uint32_t aValue)
{
return aValue != 0 && ((aValue & (aValue-1)) == 0);
}
uint8_t IndexOfMSB(uint32_t aValue)
{
uint8_t index = 0;
while (aValue >>= 1)
{
index++;
}
return index;
}
ThreadError NcpBase::SetPropertyHandler_MAC_SCAN_STATE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
uint8_t i = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&i
);
if (parsedLength > 0)
{
switch (i)
{
case SPINEL_SCAN_STATE_IDLE:
errorCode = kThreadError_None;
break;
case SPINEL_SCAN_STATE_BEACON:
#if OPENTHREAD_ENABLE_RAW_LINK_API
if (otLinkRawIsEnabled(mInstance))
{
errorCode = kThreadError_NotImplemented;
}
else
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
{
errorCode = otLinkActiveScan(
mInstance,
mChannelMask,
mScanPeriod,
&HandleActiveScanResult_Jump,
this
);
}
if (errorCode == kThreadError_None)
{
mShouldSignalEndOfScan = false;
}
break;
case SPINEL_SCAN_STATE_ENERGY:
#if OPENTHREAD_ENABLE_RAW_LINK_API
if (otLinkRawIsEnabled(mInstance))
{
// Make sure we aren't already scanning and that we have
// only 1 bit set for the channel mask.
if (mCurScanChannel == NCP_INVALID_SCAN_CHANNEL)
{
if (HasOnly1BitSet(mChannelMask))
{
uint8_t scanChannel = IndexOfMSB(mChannelMask);
mCurScanChannel = (int8_t)scanChannel;
errorCode = otLinkRawEnergyScan(
mInstance,
scanChannel,
mScanPeriod,
LinkRawEnergyScanDone
);
}
else
{
errorCode = kThreadError_InvalidArgs;
}
}
else
{
errorCode = kThreadError_InvalidState;
}
}
else
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
{
errorCode = otLinkEnergyScan(
mInstance,
mChannelMask,
mScanPeriod,
&HandleEnergyScanResult_Jump,
this
);
}
if (errorCode == kThreadError_None)
{
mShouldSignalEndOfScan = false;
}
break;
default:
errorCode = kThreadError_InvalidArgs;
break;
}
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_MAC_15_4_PANID(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
uint16_t tmp;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT16_S,
&tmp
);
if (parsedLength > 0)
{
errorCode = otLinkSetPanId(mInstance, tmp);
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_MAC_15_4_LADDR(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
otExtAddress *tmp;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_EUI64_S,
&tmp
);
if (parsedLength > 0)
{
errorCode = otLinkSetExtendedAddress(mInstance, tmp);
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_MAC_RAW_STREAM_ENABLED(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr,uint16_t value_len)
{
bool value = false;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&value
);
if (parsedLength > 0)
{
#if OPENTHREAD_ENABLE_RAW_LINK_API
if (otLinkRawIsEnabled(mInstance))
{
if (value)
{
errorCode = otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone);
}
else
{
errorCode = otLinkRawSleep(mInstance);
}
}
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
}
else
{
errorCode = kThreadError_Parse;
}
if (errorCode == kThreadError_None)
{
mIsRawStreamEnabled = value;
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
return errorCode;
}
#if OPENTHREAD_ENABLE_RAW_LINK_API
ThreadError NcpBase::SetPropertyHandler_MAC_15_4_SADDR(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
uint16_t tmp;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT16_S,
&tmp
);
if (parsedLength > 0)
{
errorCode = otLinkRawSetShortAddress(mInstance, tmp);
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_STREAM_RAW(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
ThreadError errorCode = kThreadError_None;
if (otLinkRawIsEnabled(mInstance))
{
spinel_ssize_t parsedLength(0);
uint8_t *frame_buffer(NULL);
unsigned int frame_len(0);
RadioPacket *packet = otLinkRawGetTransmitBuffer(mInstance);
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_DATA_WLEN_S
SPINEL_DATATYPE_UINT8_S
SPINEL_DATATYPE_INT8_S,
&frame_buffer,
&frame_len,
&packet->mChannel,
&packet->mPower
);
if (parsedLength > 0 && frame_len <= kMaxPHYPacketSize)
{
// Cache the transaction ID for async response
mCurTransmitTID = SPINEL_HEADER_GET_TID(header);
// Update packet buffer and length
packet->mLength = static_cast<uint8_t>(frame_len);
memcpy(packet->mPsdu, frame_buffer, packet->mLength);
// TODO: This should be later added in the STREAM_RAW argument to allow user to directly specify it.
packet->mMaxTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT;
// Pass packet to the radio layer. Note, this fails if we
// haven't enabled raw stream or are already transmitting.
errorCode = otLinkRawTransmit(mInstance, packet, &NcpBase::LinkRawTransmitDone);
}
else
{
errorCode = kThreadError_Parse;
}
}
else
{
errorCode = kThreadError_InvalidState;
}
if (errorCode == kThreadError_None)
{
// Don't do anything here yet. We will complete the transaction when we get a transmit done callback
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
(void)key;
return errorCode;
}
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
ThreadError NcpBase::SetPropertyHandler_NET_IF_UP(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
bool value = false;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&value
);
if (parsedLength > 0)
{
errorCode = otIp6SetEnabled(mInstance, value);
}
else
{
errorCode = kThreadError_Parse;
}
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_NET_STACK_UP(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
bool value = false;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&value
);
if (parsedLength > 0)
{
// If the value has changed...
if ((value != false) != (otThreadGetDeviceRole(mInstance) != kDeviceRoleDisabled))
{
if (value != false)
{
errorCode = otThreadSetEnabled(mInstance, true);
#if OPENTHREAD_ENABLE_LEGACY
mLegacyNodeDidJoin = false;
if (mLegacyHandlers != NULL)
{
if (mLegacyHandlers->mStartLegacy)
{
mLegacyHandlers->mStartLegacy();
}
}
#endif // OPENTHREAD_ENABLE_LEGACY
}
else
{
errorCode = otThreadSetEnabled(mInstance, false);
#if OPENTHREAD_ENABLE_LEGACY
mLegacyNodeDidJoin = false;
if (mLegacyHandlers != NULL)
{
if (mLegacyHandlers->mStopLegacy)
{
mLegacyHandlers->mStopLegacy();
}
}
#endif // OPENTHREAD_ENABLE_LEGACY
}
}
}
else
{
errorCode = kThreadError_Parse;
}
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_NET_ROLE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
unsigned int i(0);
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT_PACKED_S,
&i
);
if (parsedLength > 0)
{
switch (i)
{
case SPINEL_NET_ROLE_DETACHED:
errorCode = otThreadBecomeDetached(mInstance);
break;
case SPINEL_NET_ROLE_ROUTER:
errorCode = otThreadBecomeRouter(mInstance);
break;
case SPINEL_NET_ROLE_LEADER:
errorCode = otThreadBecomeLeader(mInstance);
break;
case SPINEL_NET_ROLE_CHILD:
errorCode = otThreadBecomeChild(mInstance, kMleAttachAnyPartition);
break;
}
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_NET_NETWORK_NAME(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr,
uint16_t value_len)
{
const char *string(NULL);
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UTF8_S,
&string
);
if ((parsedLength > 0) && (string != NULL))
{
errorCode = otThreadSetNetworkName(mInstance, string);
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_NET_XPANID(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
const uint8_t *ptr = NULL;
spinel_size_t len;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_DATA_S,
&ptr,
&len
);
if ((parsedLength > 0) && (len == sizeof(spinel_net_xpanid_t)))
{
otThreadSetExtendedPanId(mInstance, ptr);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_NET_MASTER_KEY(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
const uint8_t *ptr = NULL;
spinel_size_t len;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_DATA_S,
&ptr,
&len
);
if ((parsedLength > 0) && (len < 100))
{
errorCode = otThreadSetMasterKey(mInstance, ptr, static_cast<uint8_t>(len));
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_NET_KEY_SEQUENCE_COUNTER(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr,
uint16_t value_len)
{
unsigned int i(0);
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT32_S,
&i
);
if (parsedLength > 0)
{
otThreadSetKeySequenceCounter(mInstance, i);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_NET_KEY_SWITCH_GUARDTIME(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr,
uint16_t value_len)
{
unsigned int i(0);
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT32_S,
&i
);
if (parsedLength > 0)
{
otThreadSetKeySwitchGuardTime(mInstance, i);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
ThreadError errorCode = kThreadError_None;
uint8_t value = 0;
spinel_ssize_t parsedLength;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&value
);
if (parsedLength > 0)
{
otThreadSetLocalLeaderWeight(mInstance, value);
}
else
{
errorCode = kThreadError_Parse;
}
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_STREAM_NET_INSECURE(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
const uint8_t *frame_ptr(NULL);
unsigned int frame_len(0);
const uint8_t *meta_ptr(NULL);
unsigned int meta_len(0);
// STREAM_NET_INSECURE packets are not secured at layer 2.
otMessage *message = otIp6NewMessage(mInstance, false);
if (message == NULL)
{
errorCode = kThreadError_NoBufs;
}
else
{
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_S,
&frame_ptr,
&frame_len,
&meta_ptr,
&meta_len
);
// We ignore metadata for now.
// May later include TX power, allow retransmits, etc...
(void)meta_ptr;
(void)meta_len;
(void)parsedLength;
errorCode = otMessageAppend(message, frame_ptr, static_cast<uint16_t>(frame_len));
}
if (errorCode == kThreadError_None)
{
// Ensure the insecure message is forwarded using direct transmission.
otMessageSetDirectTransmission(message, true);
errorCode = otIp6Send(mInstance, message);
}
else if (message)
{
otMessageFree(message);
}
if (errorCode == kThreadError_None)
{
mInboundInsecureIpFrameCounter++;
if (SPINEL_HEADER_GET_TID(header) != 0)
{
// Only send a successful status update if
// there was a transaction id in the header.
errorCode = SendLastStatus(header, SPINEL_STATUS_OK);
}
}
else
{
mDroppedInboundIpFrameCounter++;
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
(void)key;
return errorCode;
}
#if OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
ThreadError NcpBase::SetPropertyHandler_THREAD_BA_PROXY_STREAM(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
const uint8_t *frame_ptr(NULL);
unsigned int frame_len(0);
uint16_t locator;
uint16_t port;
// THREAD_BA_PROXY_STREAM requires layer 2 security.
otMessage *message = otIp6NewMessage(mInstance, true);
if (message == NULL)
{
errorCode = kThreadError_NoBufs;
}
else
{
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_UINT16_S,
&frame_ptr,
&frame_len,
&locator,
&port
);
if (parsedLength > 0)
{
errorCode = otMessageAppend(message, frame_ptr, static_cast<uint16_t>(frame_len));
}
else
{
errorCode = kThreadError_Parse;
}
}
if (errorCode == kThreadError_None)
{
errorCode = otBorderAgentProxySend(mInstance, message, locator, port);
}
else if (message)
{
otMessageFree(message);
}
if (errorCode == kThreadError_None)
{
if (SPINEL_HEADER_GET_TID(header) != 0)
{
// Only send a successful status update if
// there was a transaction id in the header.
errorCode = SendLastStatus(header, SPINEL_STATUS_OK);
}
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
(void)key;
return errorCode;
}
#endif // OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
ThreadError NcpBase::SetPropertyHandler_STREAM_NET(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
const uint8_t *frame_ptr(NULL);
unsigned int frame_len(0);
const uint8_t *meta_ptr(NULL);
unsigned int meta_len(0);
// STREAM_NET requires layer 2 security.
otMessage *message = otIp6NewMessage(mInstance, true);
if (message == NULL)
{
errorCode = kThreadError_NoBufs;
}
else
{
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_S,
&frame_ptr,
&frame_len,
&meta_ptr,
&meta_len
);
// We ignore metadata for now.
// May later include TX power, allow retransmits, etc...
(void)meta_ptr;
(void)meta_len;
(void)parsedLength;
errorCode = otMessageAppend(message, frame_ptr, static_cast<uint16_t>(frame_len));
}
if (errorCode == kThreadError_None)
{
errorCode = otIp6Send(mInstance, message);
}
else if (message)
{
otMessageFree(message);
}
if (errorCode == kThreadError_None)
{
mInboundSecureIpFrameCounter++;
if (SPINEL_HEADER_GET_TID(header) != 0)
{
// Only send a successful status update if
// there was a transaction id in the header.
errorCode = SendLastStatus(header, SPINEL_STATUS_OK);
}
}
else
{
mDroppedInboundIpFrameCounter++;
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
(void)key;
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_IPV6_ML_PREFIX(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
ThreadError errorCode = kThreadError_None;
if (value_len >= 8)
{
errorCode = otThreadSetMeshLocalPrefix(mInstance, value_ptr);
HandleCommandPropertyGet(header, key);
}
else
{
errorCode = kThreadError_Parse;
}
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_IPV6_ICMP_PING_OFFLOAD(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
bool isEnabled(false);
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&isEnabled
);
if (parsedLength > 0)
{
otIcmp6SetEchoEnabled(mInstance, isEnabled);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
bool isEnabled(false);
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&isEnabled
);
if (parsedLength > 0)
{
// Note reverse logic: passthru enabled = filter disabled
otIp6SetReceiveFilterEnabled(mInstance, !isEnabled);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
ThreadError errorCode = kThreadError_None;
uint8_t num_entries = 0;
const uint16_t *ports = otIp6GetUnsecurePorts(mInstance, &num_entries);
spinel_ssize_t parsedLength = 1;
int ports_changed = 0;
// First, we need to remove all of the current assisting ports.
for (; num_entries != 0; ports++, num_entries--)
{
errorCode = otIp6RemoveUnsecurePort(mInstance, *ports);
if (errorCode != kThreadError_None)
{
break;
}
ports_changed++;
}
while ((errorCode == kThreadError_None)
&& (parsedLength > 0)
&& (value_len >= 2)
)
{
uint16_t port;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
"S",
&port
);
if (parsedLength > 0)
{
errorCode = otIp6AddUnsecurePort(mInstance, port);
}
else
{
errorCode = kThreadError_Parse;
}
if (errorCode != kThreadError_None)
{
break;
}
value_ptr += parsedLength;
value_len -= parsedLength;
ports_changed++;
}
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
if (ports_changed)
{
// 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, key);
}
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
bool value = false;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
bool should_register_with_leader = false;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&value
);
if (parsedLength > 0)
{
// Register any net data changes on transition from `true` to `false`.
should_register_with_leader = (mAllowLocalNetworkDataChange == true) && (value == false);
mAllowLocalNetworkDataChange = value;
}
else
{
errorCode = kThreadError_Parse;
}
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
if (should_register_with_leader)
{
otNetDataRegister(mInstance);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
bool isEnabled;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&isEnabled
);
if (parsedLength > 0)
{
otThreadSetRouterRoleEnabled(mInstance, isEnabled);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_CNTR_RESET(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
ThreadError errorCode = kThreadError_None;
uint8_t value = 0;
spinel_ssize_t parsedLength;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&value
);
if (parsedLength > 0)
{
if (value == 1)
{
// TODO: Implement counter reset!
errorCode = kThreadError_NotImplemented;
}
else
{
errorCode = kThreadError_InvalidArgs;
}
}
else
{
errorCode = kThreadError_Parse;
}
(void)key;
// 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(header, ThreadErrorToSpinelStatus(errorCode));
}
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
ThreadError NcpBase::SetPropertyHandler_THREAD_COMMISSIONER_ENABLED(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
bool value = false;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
(void)key;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&value
);
if (parsedLength > 0)
{
if (value == false)
{
errorCode = otCommissionerStop(mInstance);
}
else
{
errorCode = otCommissionerStart(mInstance);
}
}
else
{
errorCode = kThreadError_Parse;
}
return SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
#endif // OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
ThreadError NcpBase::SetPropertyHandler_MAC_WHITELIST(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
ThreadError errorCode = kThreadError_None;
spinel_ssize_t parsedLength = 1;
// First, clear the whitelist.
otLinkClearWhitelist(mInstance);
while ((errorCode == kThreadError_None)
&& (parsedLength > 0)
&& (value_len > 0)
)
{
otExtAddress *ext_addr = NULL;
int8_t rssi = RSSI_OVERRIDE_DISABLED;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_EUI64_S
SPINEL_DATATYPE_INT8_S
),
&ext_addr,
&rssi
);
if (parsedLength <= 0)
{
rssi = RSSI_OVERRIDE_DISABLED;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_STRUCT_S(
SPINEL_DATATYPE_EUI64_S
),
&ext_addr
);
}
if (parsedLength <= 0)
{
errorCode = kThreadError_Parse;
break;
}
if (rssi == RSSI_OVERRIDE_DISABLED)
{
errorCode = otLinkAddWhitelist(mInstance, ext_addr->m8);
}
else
{
errorCode = otLinkAddWhitelistRssi(mInstance, ext_addr->m8, rssi);
}
value_ptr += parsedLength;
value_len -= parsedLength;
}
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
// We had an error, but we may have actually changed
// the state of the whitelist---so we need to report
// those incomplete changes via an asynchronous
// change event.
HandleCommandPropertyGet(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, key);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_MAC_WHITELIST_ENABLED(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
bool isEnabled;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&isEnabled
);
if (parsedLength > 0)
{
otLinkSetWhitelistEnabled(mInstance, isEnabled);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
#if OPENTHREAD_ENABLE_RAW_LINK_API
ThreadError NcpBase::SetPropertyHandler_MAC_SRC_MATCH_ENABLED(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
bool isEnabled;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&isEnabled
);
if (parsedLength > 0)
{
errorCode = otLinkRawSrcMatchEnable(mInstance, isEnabled);
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
ThreadError errorCode = kThreadError_None;
spinel_status_t errorStatus = SPINEL_STATUS_OK;
const uint8_t *data = value_ptr;
uint16_t data_len = value_len;
// Clear the list first
errorCode = otLinkRawSrcMatchClearShortEntries(mInstance);
VerifyOrExit(errorCode == kThreadError_None,
errorStatus = ThreadErrorToSpinelStatus(errorCode));
// Loop through the addresses and add them
while (data_len >= sizeof(uint16_t))
{
spinel_ssize_t parsedLength;
uint16_t short_address;
parsedLength = spinel_datatype_unpack(
data,
data_len,
SPINEL_DATATYPE_UINT16_S,
&short_address
);
VerifyOrExit(parsedLength > 0, errorStatus = SPINEL_STATUS_PARSE_ERROR);
data += parsedLength;
data_len -= (uint16_t)parsedLength;
errorCode = otLinkRawSrcMatchAddShortEntry(mInstance, short_address);
VerifyOrExit(errorCode == kThreadError_None,
errorStatus = ThreadErrorToSpinelStatus(errorCode));
}
errorCode =
SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
value_ptr,
value_len
);
exit:
if (errorStatus != SPINEL_STATUS_OK)
{
errorCode = SendLastStatus(header, errorStatus);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
ThreadError errorCode = kThreadError_None;
spinel_status_t errorStatus = SPINEL_STATUS_OK;
const uint8_t *data = value_ptr;
uint16_t data_len = value_len;
// Clear the list first
errorCode = otLinkRawSrcMatchClearExtEntries(mInstance);
VerifyOrExit(errorCode == kThreadError_None,
errorStatus = ThreadErrorToSpinelStatus(errorCode));
// Loop through the addresses and add them
while (data_len >= sizeof(otExtAddress))
{
spinel_ssize_t parsedLength;
uint8_t *ext_address;
parsedLength = spinel_datatype_unpack(
data,
data_len,
SPINEL_DATATYPE_EUI64_S,
&ext_address
);
VerifyOrExit(parsedLength > 0, errorStatus = SPINEL_STATUS_PARSE_ERROR);
data += parsedLength;
data_len -= (uint16_t)parsedLength;
errorCode = otLinkRawSrcMatchAddExtEntry(mInstance, ext_address);
VerifyOrExit(errorCode == kThreadError_None,
errorStatus = ThreadErrorToSpinelStatus(errorCode));
}
errorCode =
SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
value_ptr,
value_len
);
exit:
if (errorStatus != SPINEL_STATUS_OK)
{
errorCode = SendLastStatus(header, errorStatus);
}
return errorCode;
}
#endif
#if OPENTHREAD_FTD
ThreadError NcpBase::SetPropertyHandler_NET_PSKC(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
const uint8_t *ptr = NULL;
spinel_size_t len;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_DATA_S,
&ptr,
&len
);
if ((parsedLength > 0) && (len == sizeof(spinel_net_pskc_t)))
{
errorCode = otThreadSetPSKc(mInstance, ptr);
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
if (errorCode)
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
#endif // OPENTHREAD_FTD
ThreadError NcpBase::SetPropertyHandler_THREAD_MODE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
uint8_t numeric_mode = 0;
otLinkModeConfig mode_config;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&numeric_mode
);
if (parsedLength > 0)
{
mode_config.mRxOnWhenIdle = ((numeric_mode & kThreadMode_RxOnWhenIdle) == kThreadMode_RxOnWhenIdle);
mode_config.mSecureDataRequests =
((numeric_mode & kThreadMode_SecureDataRequest) == kThreadMode_SecureDataRequest);
mode_config.mDeviceType = ((numeric_mode & kThreadMode_FullFunctionDevice) == kThreadMode_FullFunctionDevice);
mode_config.mNetworkData = ((numeric_mode & kThreadMode_FullNetworkData) == kThreadMode_FullNetworkData);
errorCode = otThreadSetLinkMode(mInstance, mode_config);
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_THREAD_CHILD_COUNT_MAX(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
uint8_t n = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&n
);
if (parsedLength > 0)
{
otThreadSetMaxAllowedChildren(mInstance, n);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_THREAD_CHILD_TIMEOUT(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
uint32_t i = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT32_S,
&i
);
if (parsedLength > 0)
{
otThreadSetChildTimeout(mInstance, i);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
uint8_t i = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&i
);
if (parsedLength > 0)
{
otThreadSetRouterUpgradeThreshold(mInstance, i);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
uint8_t i = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&i
);
if (parsedLength > 0)
{
otThreadSetRouterDowngradeThreshold(mInstance, i);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
uint8_t i = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&i
);
if (parsedLength > 0)
{
otThreadSetRouterSelectionJitter(mInstance, i);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_THREAD_PREFERRED_ROUTER_ID(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
uint8_t router_id = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&router_id
);
if (parsedLength > 0)
{
errorCode = otThreadSetPreferredRouterId(mInstance, router_id);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
router_id
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_DEBUG_NCP_LOG_LEVEL(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
uint8_t spinelNcpLogLevel = 0;
otLogLevel logLevel;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&spinelNcpLogLevel
);
if (parsedLength > 0)
{
switch (spinelNcpLogLevel)
{
case SPINEL_NCP_LOG_LEVEL_EMERG:
case SPINEL_NCP_LOG_LEVEL_ALERT:
logLevel = kLogLevelNone;
break;
case SPINEL_NCP_LOG_LEVEL_CRIT:
logLevel = kLogLevelCrit;
break;
case SPINEL_NCP_LOG_LEVEL_ERR:
case SPINEL_NCP_LOG_LEVEL_WARN:
logLevel = kLogLevelWarn;
break;
case SPINEL_NCP_LOG_LEVEL_NOTICE:
case SPINEL_NCP_LOG_LEVEL_INFO:
logLevel = kLogLevelInfo;
break;
case SPINEL_NCP_LOG_LEVEL_DEBUG:
logLevel = kLogLevelDebg;
break;
default:
errorCode = kThreadError_InvalidArgs;
break;
}
if (errorCode == kThreadError_None)
{
errorCode = otSetDynamicLogLevel(mInstance, logLevel);
}
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
if (errorCode == kThreadError_NotCapable)
{
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_COMMAND_FOR_PROP);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
uint32_t i = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT32_S,
&i
);
if (parsedLength > 0)
{
otThreadSetContextIdReuseDelay(mInstance, i);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
uint8_t i = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&i
);
if (parsedLength > 0)
{
otThreadSetNetworkIdTimeout(mInstance, i);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
#if OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
ThreadError NcpBase::SetPropertyHandler_BA_PROXY_ENABLED(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
bool isEnabled;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&isEnabled
);
if (parsedLength > 0)
{
if (isEnabled)
{
SuccessOrExit(errorCode = otBorderAgentProxyStart(mInstance, &NcpBase::HandleBorderAgentProxyStream, this));
}
else
{
SuccessOrExit(errorCode = otBorderAgentProxyStop(mInstance));
}
SuccessOrExit(errorCode = HandleCommandPropertyGet(header, key));
}
else
{
errorCode = kThreadError_Parse;
}
exit:
if (errorCode != kThreadError_None)
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
return errorCode;
}
#endif // OPENTHREAD_ENABLE_BORDER_AGENT_PROXY && OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_JAM_DETECTION
ThreadError NcpBase::SetPropertyHandler_JAM_DETECT_ENABLE(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
bool isEnabled;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_BOOL_S,
&isEnabled
);
if (parsedLength > 0)
{
if (isEnabled)
{
otJamDetectionStart(mInstance, &NcpBase::HandleJamStateChange_Jump, this);
}
else
{
otJamDetectionStop(mInstance);
}
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
int8_t value = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_INT8_S,
&value
);
if (parsedLength > 0)
{
errorCode = otJamDetectionSetRssiThreshold(mInstance, value);
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_JAM_DETECT_WINDOW(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
uint8_t value = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&value
);
if (parsedLength > 0)
{
errorCode = otJamDetectionSetWindow(mInstance, value);
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_JAM_DETECT_BUSY(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
uint8_t value = 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&value
);
if (parsedLength > 0)
{
errorCode = otJamDetectionSetBusyPeriod(mInstance, value);
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
void NcpBase::HandleJamStateChange_Jump(bool aJamState, void *aContext)
{
static_cast<NcpBase *>(aContext)->HandleJamStateChange(aJamState);
}
void NcpBase::HandleJamStateChange(bool aJamState)
{
ThreadError errorCode;
errorCode = 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 (errorCode != kThreadError_None)
{
mShouldSignalJamStateChange = true;
}
}
#endif // OPENTHREAD_ENABLE_JAM_DETECTION
#if OPENTHREAD_ENABLE_DIAG
ThreadError NcpBase::SetPropertyHandler_NEST_STREAM_MFG(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
char *string(NULL);
char *output(NULL);
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UTF8_S,
&string
);
if ((parsedLength > 0) && (string != NULL))
{
// all diagnostics related features are processed within diagnostics module
output = otDiagProcessCmdLine(string);
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
reinterpret_cast<uint8_t *>(output),
static_cast<uint16_t>(strlen(output) + 1)
);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
#endif
#if OPENTHREAD_ENABLE_LEGACY
ThreadError NcpBase::SetPropertyHandler_NEST_LEGACY_ULA_PREFIX(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
ThreadError errorCode = kThreadError_None;
const uint8_t *ptr = NULL;
spinel_size_t len;
spinel_ssize_t parsedLength;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_DATA_S,
&ptr,
&len
);
if ((parsedLength > 0) && (len <= sizeof(mLegacyUlaPrefix)))
{
memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix));
memcpy(mLegacyUlaPrefix, ptr, len);
if (mLegacyHandlers)
{
if (mLegacyHandlers->mSetLegacyUlaPrefix)
{
mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix);
}
}
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
#endif // OPENTHREAD_ENABLE_LEGACY
// ----------------------------------------------------------------------------
// MARK: Individual Property Inserters
// ----------------------------------------------------------------------------
#if OPENTHREAD_ENABLE_RAW_LINK_API
ThreadError NcpBase::InsertPropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
spinel_status_t errorStatus = SPINEL_STATUS_OK;
uint16_t short_address;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT16_S,
&short_address
);
VerifyOrExit(parsedLength > 0, errorStatus = SPINEL_STATUS_PARSE_ERROR);
errorCode = otLinkRawSrcMatchAddShortEntry(mInstance, short_address);
VerifyOrExit(errorCode == kThreadError_None,
errorStatus = ThreadErrorToSpinelStatus(errorCode));
errorCode =
SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_INSERTED,
key,
value_ptr,
value_len
);
exit:
if (errorStatus != SPINEL_STATUS_OK)
{
errorCode = SendLastStatus(header, errorStatus);
}
return errorCode;
}
ThreadError NcpBase::InsertPropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
spinel_status_t errorStatus = SPINEL_STATUS_OK;
uint8_t *ext_address = NULL;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_EUI64_S,
&ext_address
);
VerifyOrExit(parsedLength > 0, errorStatus = SPINEL_STATUS_PARSE_ERROR);
errorCode = otLinkRawSrcMatchAddExtEntry(mInstance, ext_address);
VerifyOrExit(errorCode == kThreadError_None,
errorStatus = ThreadErrorToSpinelStatus(errorCode));
errorCode =
SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_INSERTED,
key,
value_ptr,
value_len
);
exit:
if (errorStatus != SPINEL_STATUS_OK)
{
errorCode = SendLastStatus(header, errorStatus);
}
return errorCode;
}
#endif
ThreadError NcpBase::InsertPropertyHandler_IPV6_ADDRESS_TABLE(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
spinel_status_t errorStatus = SPINEL_STATUS_OK;
otNetifAddress netif_addr;
otIp6Address *addr_ptr;
uint32_t preferred_lifetime;
uint32_t valid_lifetime;
uint8_t prefix_len;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
"6CLL",
&addr_ptr,
&prefix_len,
&preferred_lifetime,
&valid_lifetime
);
VerifyOrExit(parsedLength > 0, errorStatus = SPINEL_STATUS_PARSE_ERROR);
netif_addr.mAddress = *addr_ptr;
netif_addr.mPrefixLength = prefix_len;
netif_addr.mPreferred = preferred_lifetime != 0;
netif_addr.mValid = valid_lifetime != 0;
errorCode = otIp6AddUnicastAddress(mInstance, &netif_addr);
VerifyOrExit(errorCode == kThreadError_None,
errorStatus = ThreadErrorToSpinelStatus(errorCode));
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_INSERTED,
key,
value_ptr,
value_len
);
errorStatus = SPINEL_STATUS_OK;
exit:
if (errorStatus != SPINEL_STATUS_OK)
{
errorCode = SendLastStatus(header, errorStatus);
}
return errorCode;
}
ThreadError NcpBase::InsertPropertyHandler_THREAD_LOCAL_ROUTES(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
const static int kPreferenceOffset = 6;
const static int kPreferenceMask = 3 << kPreferenceOffset;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
otExternalRouteConfig ext_route_config;
otIp6Address *addr_ptr;
bool stable = false;
uint8_t flags = 0;
memset(&ext_route_config, 0, sizeof(otExternalRouteConfig));
VerifyOrExit(
mAllowLocalNetworkDataChange == true,
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE)
);
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
"6CbC",
&addr_ptr,
&ext_route_config.mPrefix.mLength,
&stable,
&flags
);
if (parsedLength > 0)
{
ext_route_config.mPrefix.mPrefix = *addr_ptr;
ext_route_config.mStable = stable;
ext_route_config.mPreference = ((flags & kPreferenceMask) >> kPreferenceOffset);
errorCode = otNetDataAddRoute(mInstance, &ext_route_config);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_INSERTED,
key,
value_ptr,
value_len
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
exit:
return errorCode;
}
ThreadError NcpBase::InsertPropertyHandler_THREAD_ON_MESH_NETS(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
const static int kPreferenceOffset = 6;
const static int kPreferenceMask = 3 << kPreferenceOffset;
const static int kPreferredFlag = 1 << 5;
const static int kSlaacFlag = 1 << 4;
const static int kDhcpFlag = 1 << 3;
const static int kConfigureFlag = 1 << 2;
const static int kDefaultRouteFlag = 1 << 1;
const static int kOnMeshFlag = 1 << 0;
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
otBorderRouterConfig border_router_config;
otIp6Address *addr_ptr;
bool stable = false;
uint8_t flags = 0;
memset(&border_router_config, 0, sizeof(otBorderRouterConfig));
VerifyOrExit(
mAllowLocalNetworkDataChange == true,
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE)
);
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
"6CbC",
&addr_ptr,
&border_router_config.mPrefix.mLength,
&stable,
&flags
);
if (parsedLength > 0)
{
border_router_config.mPrefix.mPrefix = *addr_ptr;
border_router_config.mStable = stable;
border_router_config.mPreference = ((flags & kPreferenceMask) >> kPreferenceOffset);
border_router_config.mPreferred = ((flags & kPreferredFlag) == kPreferredFlag);
border_router_config.mSlaac = ((flags & kSlaacFlag) == kSlaacFlag);
border_router_config.mDhcp = ((flags & kDhcpFlag) == kDhcpFlag);
border_router_config.mConfigure = ((flags & kConfigureFlag) == kConfigureFlag);
border_router_config.mDefaultRoute = ((flags & kDefaultRouteFlag) == kDefaultRouteFlag);
border_router_config.mOnMesh = ((flags & kOnMeshFlag) == kOnMeshFlag);
errorCode = otNetDataAddPrefixInfo(mInstance, &border_router_config);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_INSERTED,
key,
value_ptr,
value_len
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
exit:
return errorCode;
}
ThreadError NcpBase::InsertPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
uint16_t port;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT16_S,
&port
);
if (parsedLength > 0)
{
errorCode = otIp6AddUnsecurePort(mInstance, port);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_INSERTED,
key,
value_ptr,
value_len
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::InsertPropertyHandler_MAC_WHITELIST(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
ThreadError errorCode = kThreadError_None;
spinel_ssize_t parsedLength;
otExtAddress *ext_addr = NULL;
int8_t rssi = RSSI_OVERRIDE_DISABLED;
if (value_len > static_cast<spinel_ssize_t>(sizeof(ext_addr)))
{
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_INT8_S,
&ext_addr,
&rssi
);
}
else
{
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_EUI64_S,
&ext_addr
);
}
if (parsedLength > 0)
{
if (rssi == RSSI_OVERRIDE_DISABLED)
{
errorCode = otLinkAddWhitelist(mInstance, ext_addr->m8);
}
else
{
errorCode = otLinkAddWhitelistRssi(mInstance, ext_addr->m8, rssi);
}
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_INSERTED,
key,
value_ptr,
value_len
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
#if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
ThreadError NcpBase::InsertPropertyHandler_THREAD_JOINERS(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
otExtAddress *ot_ext_address = NULL;
const char *aPSKd = NULL;
uint32_t joiner_timeout = 0;
VerifyOrExit(
mAllowLocalNetworkDataChange == true,
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE)
);
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
"ULE",
&aPSKd,
&joiner_timeout,
&ot_ext_address
);
if (parsedLength <= 0)
{
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
"UL",
&aPSKd,
&joiner_timeout
);
ot_ext_address = NULL;
}
if (parsedLength > 0)
{
errorCode = otCommissionerAddJoiner(mInstance, ot_ext_address, aPSKd, joiner_timeout);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_INSERTED,
key,
value_ptr,
value_len
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
exit:
return errorCode;
}
#endif // OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD
// ----------------------------------------------------------------------------
// MARK: Individual Property Removers
// ----------------------------------------------------------------------------
#if OPENTHREAD_ENABLE_RAW_LINK_API
ThreadError NcpBase::RemovePropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
uint16_t short_address;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT16_S,
&short_address
);
if (parsedLength > 0)
{
errorCode = otLinkRawSrcMatchClearShortEntry(mInstance, short_address);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_REMOVED,
key,
value_ptr,
value_len
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::RemovePropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
uint8_t *ext_address;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_EUI64_S,
&ext_address
);
if (parsedLength > 0)
{
errorCode = otLinkRawSrcMatchClearExtEntry(mInstance, ext_address);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_REMOVED,
key,
value_ptr,
value_len
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
#endif
ThreadError NcpBase::RemovePropertyHandler_IPV6_ADDRESS_TABLE(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
otIp6Address *addr_ptr;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
"6",
&addr_ptr
);
if (parsedLength > 0)
{
errorCode = otIp6RemoveUnicastAddress(mInstance, addr_ptr);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_REMOVED,
key,
value_ptr,
value_len
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::RemovePropertyHandler_THREAD_LOCAL_ROUTES(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
otIp6Prefix ip6_prefix;
memset(&ip6_prefix, 0, sizeof(otIp6Prefix));
otIp6Address *addr_ptr;
VerifyOrExit(
mAllowLocalNetworkDataChange == true,
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE)
);
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
"6C",
&addr_ptr,
&ip6_prefix.mLength
);
if (parsedLength > 0)
{
ip6_prefix.mPrefix = *addr_ptr;
errorCode = otNetDataRemoveRoute(mInstance, &ip6_prefix);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_REMOVED,
key,
value_ptr,
value_len
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
exit:
return errorCode;
}
ThreadError NcpBase::RemovePropertyHandler_THREAD_ON_MESH_NETS(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
otIp6Prefix ip6_prefix;
memset(&ip6_prefix, 0, sizeof(otIp6Prefix));
otIp6Address *addr_ptr;
VerifyOrExit(
mAllowLocalNetworkDataChange == true,
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE)
);
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
"6C",
&addr_ptr,
&ip6_prefix.mLength
);
if (parsedLength > 0)
{
ip6_prefix.mPrefix = *addr_ptr;
errorCode = otNetDataRemovePrefixInfo(mInstance, &ip6_prefix);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_REMOVED,
key,
value_ptr,
value_len
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
exit:
return errorCode;
}
ThreadError NcpBase::RemovePropertyHandler_THREAD_ASSISTING_PORTS(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
uint16_t port;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT16_S,
&port
);
if (parsedLength > 0)
{
errorCode = otIp6RemoveUnsecurePort(mInstance, port);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_REMOVED,
key,
value_ptr,
value_len
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::RemovePropertyHandler_THREAD_ACTIVE_ROUTER_IDS(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
spinel_ssize_t parsedLength;
ThreadError errorCode = kThreadError_None;
uint8_t router_id;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_UINT8_S,
&router_id
);
if (parsedLength > 0)
{
errorCode = otThreadReleaseRouterId(mInstance, router_id);
if (errorCode == kThreadError_None)
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_REMOVED,
key,
value_ptr,
value_len
);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
ThreadError NcpBase::RemovePropertyHandler_MAC_WHITELIST(uint8_t header, spinel_prop_key_t key,
const uint8_t *value_ptr, uint16_t value_len)
{
ThreadError errorCode = kThreadError_None;
spinel_ssize_t parsedLength;
otExtAddress *ext_addr_ptr = NULL;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_EUI64_S,
&ext_addr_ptr
);
if (parsedLength > 0)
{
otLinkRemoveWhitelist(mInstance, ext_addr_ptr->m8);
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_REMOVED,
key,
value_ptr,
value_len
);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
#if OPENTHREAD_ENABLE_LEGACY
void NcpBase::RegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers)
{
mLegacyHandlers = aHandlers;
bool isEnabled;
VerifyOrExit(mLegacyHandlers != NULL);
isEnabled = (otThreadGetDeviceRole(mInstance) != kDeviceRoleDisabled);
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
));
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
));
OutboundFrameSend();
exit:
return;
}
#endif // OPENTHREAD_ENABLE_LEGACY
ThreadError NcpBase::StreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen)
{
ThreadError errorCode = kThreadError_None;
if (aStreamId == 0)
{
aStreamId = SPINEL_PROP_STREAM_DEBUG;
}
if (!mDisableStreamWrite)
{
errorCode = 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)
);
}
else
{
errorCode = kThreadError_InvalidState;
}
return errorCode;
}
} // namespace ot
// ----------------------------------------------------------------------------
// MARK: Virtual Datastream I/O (Public API)
// ----------------------------------------------------------------------------
ThreadError otNcpStreamWrite(int aStreamId, const uint8_t* aDataPtr, int aDataLen)
{
ThreadError errorCode = kThreadError_InvalidState;
ot::NcpBase *ncp = ot::NcpBase::GetNcpInstance();
if (ncp != NULL)
{
errorCode = ncp->StreamWrite(aStreamId, aDataPtr, aDataLen);
}
return errorCode;
}
// ----------------------------------------------------------------------------
// 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
(void)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
(void)aUlaPrefix;
#endif
}
void otNcpHandleLegacyNodeDidJoin(const otExtAddress *aExtAddr)
{
#if OPENTHREAD_ENABLE_LEGACY
ot::NcpBase *ncp = ot::NcpBase::GetNcpInstance();
if (ncp != NULL)
{
ncp->HandleLegacyNodeDidJoin(aExtAddr);
}
#else
(void)aExtAddr;
#endif
}