blob: 00cebef8c49dcc6241d5e283392f224651aa5e48 [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 <assert.h>
#include <stdlib.h>
#include <common/code_utils.hpp>
#include <ncp/ncp.h>
#include <ncp/ncp_base.hpp>
#include <net/ip6.hpp>
#include <openthread.h>
#include <openthread-diag.h>
#if OPENTHREAD_ENABLE_JAM_DETECTION
#include <openthread-jam-detection.h>
#endif
#include <openthread-instance.h>
#include <stdarg.h>
#include <platform/radio.h>
#include <platform/misc.h>
namespace Thread
{
static NcpBase *sNcpContext = NULL;
#define NCP_PLAT_RESET_REASON (1U<<31)
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_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_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},
{ 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::NcpBase::GetPropertyHandler_THREAD_LOCAL_ROUTES },
{ SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::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 },
{ 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::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
{ 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_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 },
#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 },
{ SPINEL_PROP_PHY_ENABLED, &NcpBase::SetPropertyHandler_PHY_ENABLED },
{ 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_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},
{ 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 },
{ 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 },
#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_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
};
const NcpBase::InsertPropertyHandlerEntry NcpBase::mInsertPropertyHandlerTable[] =
{
{ SPINEL_PROP_IPV6_ADDRESS_TABLE, &NcpBase::NcpBase::InsertPropertyHandler_IPV6_ADDRESS_TABLE },
{ SPINEL_PROP_THREAD_LOCAL_ROUTES, &NcpBase::NcpBase::InsertPropertyHandler_THREAD_LOCAL_ROUTES },
{ SPINEL_PROP_THREAD_ON_MESH_NETS, &NcpBase::NcpBase::InsertPropertyHandler_THREAD_ON_MESH_NETS },
{ SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::NcpBase::InsertPropertyHandler_THREAD_ASSISTING_PORTS },
{ SPINEL_PROP_CNTR_RESET, &NcpBase::SetPropertyHandler_CNTR_RESET },
{ SPINEL_PROP_MAC_WHITELIST, &NcpBase::InsertPropertyHandler_MAC_WHITELIST },
};
const NcpBase::RemovePropertyHandlerEntry NcpBase::mRemovePropertyHandlerTable[] =
{
{ SPINEL_PROP_IPV6_ADDRESS_TABLE, &NcpBase::NcpBase::RemovePropertyHandler_IPV6_ADDRESS_TABLE },
{ SPINEL_PROP_THREAD_LOCAL_ROUTES, &NcpBase::NcpBase::RemovePropertyHandler_THREAD_LOCAL_ROUTES },
{ SPINEL_PROP_THREAD_ON_MESH_NETS, &NcpBase::NcpBase::RemovePropertyHandler_THREAD_ON_MESH_NETS },
{ SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::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(otInstance *aInstance):
mInstance(aInstance),
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),
mFramingErrorCounter(0),
mRxSpinelFrameCounter(0),
mRxSpinelOutOfOrderTidCounter(0),
mTxSpinelFrameCounter(0),
mInboundSecureIpFrameCounter(0),
mInboundInsecureIpFrameCounter(0),
mOutboundSecureIpFrameCounter(0),
mOutboundInsecureIpFrameCounter(0),
mDroppedOutboundIpFrameCounter(0),
mDroppedInboundIpFrameCounter(0)
{
assert(mInstance != NULL);
sNcpContext = this;
otSetStateChangedCallback(mInstance, &NcpBase::HandleNetifStateChanged, this);
otSetReceiveIp6DatagramCallback(mInstance, &NcpBase::HandleDatagramFromStack, this);
otSetLinkPcapCallback(mInstance, &NcpBase::HandleRawFrame, static_cast<void*>(this));
otSetIcmpEchoEnabled(mInstance, false);
mUpdateChangedPropsTask.Post();
#if OPENTHREAD_ENABLE_LEGACY
mLegacyNodeDidJoin = false;
mLegacyHandlers = NULL;
memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix));
#endif
}
// ----------------------------------------------------------------------------
// 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 = otIsMessageLinkSecurityEnabled(aMessage);
uint16_t length = otGetMessageLength(aMessage);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(
errorCode = OutboundFrameFeedPacked(
"CiiS",
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)
{
otFreeMessage(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(
"CiiS",
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(
"ccS",
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;
}
//chan,rssi,(laddr,saddr,panid,lqi),(proto,flags,networkid,xpanid) [icT(ESSC)T(iCUD.).]
NcpBase::SendPropertyUpdate(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_CMD_PROP_VALUE_INSERTED,
SPINEL_PROP_MAC_SCAN_BEACON,
"CcT(ESSC.)T(iCUD.).",
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: 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 (otGetDeviceRole(mInstance))
{
case kDeviceRoleDetached:
case kDeviceRoleDisabled:
break;
default:
mRequireJoinExistingNetwork = false;
break;
}
if ( (otGetDeviceRole(mInstance) == kDeviceRoleLeader)
&& otIsSingleton(mInstance)
#if OPENTHREAD_ENABLE_LEGACY
&& !mLegacyNodeDidJoin
#endif
) {
mChangedFlags &= ~static_cast<uint32_t>(OT_NET_PARTITION_ID);
otThreadStop(mInstance);
// 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_ON_MESH_NETS
));
SuccessOrExit(HandleCommandPropertyGet(
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
SPINEL_PROP_THREAD_LEADER_NETWORK_DATA
));
mChangedFlags &= ~static_cast<uint32_t>(OT_THREAD_NETDATA_UPDATED);
}
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, "CiD", &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,
otGetJamDetectionState(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("Cii", 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("Cii", 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("Cii", 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)
{
otFreeMessage(aMessage);
}
return errorCode;
}
ThreadError NcpBase::OutboundFrameFeedVPacked(const char *pack_format, va_list args)
{
uint8_t buf[64];
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.
otPlatformReset(mInstance);
// We only get to this point if the
// platform doesn't support resetting.
// In such a case we fake it.
otThreadStop(mInstance);
otInterfaceDown(mInstance);
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, "i", &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, "iD", &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, "iD", &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, "iD", &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(otPersistentInfoErase(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("Cii", 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_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
// 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;
otGetFactoryAssignedIeeeEui64(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)
{
// TODO: Implement PHY_ENBLED (Needs API!)
(void)key;
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
}
ThreadError NcpBase::GetPropertyHandler_PHY_FREQ(uint8_t header, spinel_prop_key_t key)
{
uint32_t freq_khz(0);
const uint8_t chan(otGetChannel(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,
otGetChannel(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_MAC_SCAN_STATE(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
if (otIsActiveScanInProgress(mInstance))
{
errorCode = SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_UINT8_S,
SPINEL_SCAN_STATE_BEACON
);
}
else if (otIsEnergyScanInProgress(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("Cii", 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,
otGetPanId(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,
otGetExtendedAddress(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,
otGetShortAddress(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,
otGetExtendedAddress(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_IF_UP(uint8_t header, spinel_prop_key_t key)
{
return SendPropertyUpdate(
header,
SPINEL_CMD_PROP_VALUE_IS,
key,
SPINEL_DATATYPE_BOOL_S,
otIsInterfaceUp(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,
otGetDeviceRole(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 (otGetDeviceRole(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,
otGetNetworkName(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,
otGetExtendedPanId(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 = otGetMasterKey(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,
otGetKeySequenceCounter(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,
otGetPartitionId(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,
otGetKeySwitchGuardTime(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,
otGetNetworkDataVersion(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,
otGetStableNetworkDataVersion(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;
otGetNetworkDataLocal(
mInstance,
false, // Stable?
network_data,
&network_data_len
);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", 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;
otGetNetworkDataLocal(
mInstance,
true, // Stable?
network_data,
&network_data_len
);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", 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;
otGetNetworkDataLeader(
mInstance,
false, // Stable?
network_data,
&network_data_len
);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", 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;
otGetNetworkDataLeader(
mInstance,
true, // Stable?
network_data,
&network_data_len
);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", 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,
otGetLeaderRouterId(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,
otGetLocalLeaderWeight(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,
otGetLeaderWeight(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_LEADER_ADDR(uint8_t header, spinel_prop_key_t key)
{
ThreadError errorCode = kThreadError_None;
otIp6Address address;
errorCode = otGetLeaderRloc(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 = otGetParentInfo(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 index;
uint8_t modeFlags;
mDisableStreamWrite = true;
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key));
index = 0;
while (otGetChildInfoByIndex(mInstance, index, &childInfo) == kThreadError_None)
{
if (childInfo.mTimeout > 0)
{
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(
"T("
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)
")",
childInfo.mExtAddress.m8,
childInfo.mRloc16,
childInfo.mTimeout,
childInfo.mAge,
childInfo.mNetworkDataVersion,
childInfo.mLinkQualityIn,
childInfo.mAverageRssi,
modeFlags
));
}
index++;
}
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("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key));
while (otGetNextNeighborInfo(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(
"T("
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
")",
neighInfo.mExtAddress.m8,
neighInfo.mRloc16,
neighInfo.mAge,
neighInfo.mLinkQualityIn,
neighInfo.mAverageRssi,
modeFlags,
neighInfo.mIsChild,
neighInfo.mLinkFrameCounter,
neighInfo.mMleFrameCounter
));
}
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 = otGetUnsecurePorts(mInstance, &num_entries);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", 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,
otIsRouterRoleEnabled(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("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key));
// Fill from non-local network data first
for (otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT ;;)
{
errorCode = otGetNextOnMeshPrefix(mInstance, false, &iter, &border_router_config);
if (errorCode != kThreadError_None)
{
break;
}
flags = BorderRouterConfigToFlagByte(border_router_config);
SuccessOrExit(errorCode = OutboundFrameFeedPacked(
"T("
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 = otGetNextOnMeshPrefix(mInstance, true, &iter, &border_router_config);
if (errorCode != kThreadError_None)
{
break;
}
flags = BorderRouterConfigToFlagByte(border_router_config);
SuccessOrExit(errorCode = OutboundFrameFeedPacked(
"T("
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 = otGetMeshLocalPrefix(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 = otGetMeshLocalEid(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("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key));
for (const otNetifAddress *address = otGetUnicastAddresses(mInstance); address; address = address->mNext)
{
SuccessOrExit(errorCode = OutboundFrameFeedPacked(
"T(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,
otIsIcmpEchoEnabled(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,
!otIsReceiveIp6DatagramFilterEnabled(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_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,
otIsJamDetectionEnabled(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,
otGetJamDetectionState(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,
otGetJamDetectionRssiThreshold(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,
otGetJamDetectionWindow(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,
otGetJamDetectionBusyPeriod(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_HISTORY_BITMAP(uint8_t header, spinel_prop_key_t key)
{
uint64_t historyBitmap = otGetJamDetectionHistoryBitmap(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 = otGetMacCounters(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_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;
otGetMessageBufferInfo(mInstance, &bufferInfo);
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key));
SuccessOrExit(errorCode = OutboundFrameFeedPacked("T(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_MAC_WHITELIST(uint8_t header, spinel_prop_key_t key)
{
otMacWhitelistEntry entry;
ThreadError errorCode = kThreadError_None;
mDisableStreamWrite = true;
SuccessOrExit(errorCode = OutboundFrameBegin());
SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key));
for (uint8_t i = 0; (i != 255) && (errorCode == kThreadError_None); i++)
{
errorCode = otGetMacWhitelistEntry(mInstance, i, &entry);
if (errorCode != kThreadError_None)
{
break;
}
if (entry.mValid)
{
if (!entry.mFixedRssi)
{
entry.mRssi = RSSI_OVERRIDE_DISABLED;
}
SuccessOrExit(errorCode = OutboundFrameFeedPacked("T(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,
otIsMacWhitelistEnabled(mInstance)
);
}
ThreadError NcpBase::GetPropertyHandler_THREAD_MODE(uint8_t header, spinel_prop_key_t key)
{
uint8_t numeric_mode(0);
otLinkModeConfig mode_config(otGetLinkMode(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,
otGetMaxAllowedChildren(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,
otGetChildTimeout(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,
otGetRloc16(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,
otGetRouterUpgradeThreshold(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,
otGetRouterDowngradeThreshold(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,
otGetRouterSelectionJitter(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,
otGetContextIdReuseDelay(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,
otGetNetworkIdTimeout(mInstance)
);
}
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);
}
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)
{
errorCode = otPlatRadioDisable(mInstance);
}
else
{
errorCode = otPlatRadioEnable(mInstance);
}
}
else
{
errorCode = kThreadError_Parse;
}
if (errorCode == kThreadError_None)
{
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
}
return errorCode;
}
ThreadError NcpBase::SetPropertyHandler_PHY_TX_POWER(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
uint16_t value_len)
{
// TODO: Implement PHY_TX_POWER
(void)key;
(void)value_ptr;
(void)value_len;
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
}
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 = otSetChannel(mInstance, static_cast<uint8_t>(i));
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;
}
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:
errorCode = otActiveScan(
mInstance,
mChannelMask,
mScanPeriod,
&HandleActiveScanResult_Jump,
this
);
if (errorCode == kThreadError_None)
{
mShouldSignalEndOfScan = false;
}
break;
case SPINEL_SCAN_STATE_ENERGY:
errorCode = otEnergyScan(
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 = otSetPanId(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)
{
mIsRawStreamEnabled = 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_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)
{
if (value == false)
{
errorCode = otInterfaceDown(mInstance);
}
else
{
errorCode = otInterfaceUp(mInstance);
}
}
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) != (otGetDeviceRole(mInstance) != kDeviceRoleDisabled))
{
if (value != false)
{
errorCode = otThreadStart(mInstance);
#if OPENTHREAD_ENABLE_LEGACY
mLegacyNodeDidJoin = false;
if (mLegacyHandlers != NULL)
{
if (mLegacyHandlers->mStartLegacy)
{
mLegacyHandlers->mStartLegacy();
}
}
#endif // OPENTHREAD_ENABLE_LEGACY
}
else
{
errorCode = otThreadStop(mInstance);
#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 = otBecomeDetached(mInstance);
break;
case SPINEL_NET_ROLE_ROUTER:
errorCode = otBecomeRouter(mInstance);
break;
case SPINEL_NET_ROLE_LEADER:
errorCode = otBecomeLeader(mInstance);
break;
case SPINEL_NET_ROLE_CHILD:
errorCode = otBecomeChild(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 = otSetNetworkName(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)))
{
otSetExtendedPanId(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 = otSetMasterKey(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)
{
otSetKeySequenceCounter(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)
{
otSetKeySwitchGuardTime(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)
{
otSetLocalLeaderWeight(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 = otNewIp6Message(mInstance, false);
if (message == NULL)
{
errorCode = kThreadError_NoBufs;
}
else
{
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_DATA_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 = otAppendMessage(message, frame_ptr, static_cast<uint16_t>(frame_len));
}
if (errorCode == kThreadError_None)
{
errorCode = otSendIp6Datagram(mInstance, message);
}
else if (message)
{
otFreeMessage(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;
}
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 = otNewIp6Message(mInstance, true);
if (message == NULL)
{
errorCode = kThreadError_NoBufs;
}
else
{
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
SPINEL_DATATYPE_DATA_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 = otAppendMessage(message, frame_ptr, static_cast<uint16_t>(frame_len));
}
if (errorCode == kThreadError_None)
{
errorCode = otSendIp6Datagram(mInstance, message);
}
else if (message)
{
otFreeMessage(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 = otSetMeshLocalPrefix(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)
{
otSetIcmpEchoEnabled(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
otSetReceiveIp6DatagramFilterEnabled(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 = otGetUnsecurePorts(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 = otRemoveUnsecurePort(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 = otAddUnsecurePort(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)
{
otSendServerData(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)
{
otSetRouterRoleEnabled(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));
}
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.
otClearMacWhitelist(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,
"T(Ec).",
&ext_addr,
&rssi
);
if (parsedLength <= 0)
{
rssi = RSSI_OVERRIDE_DISABLED;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
"T(E).",
&ext_addr
);
}
if (parsedLength <= 0)
{
errorCode = kThreadError_Parse;
break;
}
if (rssi == RSSI_OVERRIDE_DISABLED)
{
errorCode = otAddMacWhitelist(mInstance, ext_addr->m8);
}
else
{
errorCode = otAddMacWhitelistRssi(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)
{
if (isEnabled)
{
otEnableMacWhitelist(mInstance);
}
else
{
otDisableMacWhitelist(mInstance);
}
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
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 = otSetLinkMode(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)
{
otSetMaxAllowedChildren(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)
{
otSetChildTimeout(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)
{
otSetRouterUpgradeThreshold(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)
{
otSetRouterDowngradeThreshold(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)
{
otSetRouterSelectionJitter(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 = otSetPreferredRouterId(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_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)
{
otSetContextIdReuseDelay(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)
{
otSetNetworkIdTimeout(mInstance, i);
errorCode = HandleCommandPropertyGet(header, key);
}
else
{
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
}
return errorCode;
}
#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)
{
otStartJamDetection(mInstance, &NcpBase::HandleJamStateChange_Jump, this);
}
else
{
otStopJamDetection(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 = otSetJamDetectionRssiThreshold(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 = otSetJamDetectionWindow(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 = otSetJamDetectionBusyPeriod(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 = diagProcessCmdLine(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
// ----------------------------------------------------------------------------
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 = otAddUnicastAddress(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 = otAddExternalRoute(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 = otAddBorderRouter(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,
"S",
&port
);
if (parsedLength > 0)
{
errorCode = otAddUnsecurePort(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,
"Ec",
&ext_addr,
&rssi
);
}
else
{
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
"E",
&ext_addr
);
}
if (parsedLength > 0)
{
if (rssi == RSSI_OVERRIDE_DISABLED)
{
errorCode = otAddMacWhitelist(mInstance, ext_addr->m8);
}
else
{
errorCode = otAddMacWhitelistRssi(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;
}
// ----------------------------------------------------------------------------
// MARK: Individual Property Removers
// ----------------------------------------------------------------------------
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 = otRemoveUnicastAddress(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 = otRemoveExternalRoute(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 = otRemoveBorderRouter(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,
"S",
&port
);
if (parsedLength > 0)
{
errorCode = otRemoveUnsecurePort(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,
"C",
&router_id
);
if (parsedLength > 0)
{
errorCode = otReleaseRouterId(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;
parsedLength = spinel_datatype_unpack(
value_ptr,
value_len,
"E",
&ext_addr
);
if (parsedLength > 0)
{
otRemoveMacWhitelist(mInstance, ext_addr.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 = (otGetDeviceRole(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(
"Cii" 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(
"Cii" 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 Thread
// ----------------------------------------------------------------------------
// MARK: Virtual Datastream I/O (Public API)
// ----------------------------------------------------------------------------
ThreadError otNcpStreamWrite(int aStreamId, const uint8_t* aDataPtr, int aDataLen)
{
ThreadError errorCode = kThreadError_InvalidState;
if (Thread::sNcpContext)
{
errorCode = Thread::sNcpContext->StreamWrite(aStreamId, aDataPtr, aDataLen);
}
return errorCode;
}
// ----------------------------------------------------------------------------
// MARK: Legacy network APIs
// ----------------------------------------------------------------------------
void otNcpRegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers)
{
#if OPENTHREAD_ENABLE_LEGACY
Thread::sNcpContext->RegisterLegacyHandlers(aHandlers);
#else
(void)aHandlers;
#endif
}
void otNcpHandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix)
{
#if OPENTHREAD_ENABLE_LEGACY
Thread::sNcpContext->HandleDidReceiveNewLegacyUlaPrefix(aUlaPrefix);
#else
(void)aUlaPrefix;
#endif
}
void otNcpHandleLegacyNodeDidJoin(const otExtAddress *aExtAddr)
{
#if OPENTHREAD_ENABLE_LEGACY
Thread::sNcpContext->HandleLegacyNodeDidJoin(aExtAddr);
#else
(void)aExtAddr;
#endif
}