mirror of
https://github.com/espressif/openthread.git
synced 2026-07-11 06:40:19 +00:00
8951a336fe
* Logging and Raw Link Layer Cleanup * make pretty
6688 lines
203 KiB
C++
6688 lines
203 KiB
C++
/*
|
|
* Copyright (c) 2016, The OpenThread Authors.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* This file implements a Spinel interface to the OpenThread stack.
|
|
*/
|
|
|
|
#ifdef OPENTHREAD_CONFIG_FILE
|
|
#include OPENTHREAD_CONFIG_FILE
|
|
#else
|
|
#include <openthread-config.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "openthread/openthread.h"
|
|
#include "openthread/ncp.h"
|
|
#include "openthread/diag.h"
|
|
#include "openthread/icmp6.h"
|
|
|
|
#if OPENTHREAD_ENABLE_JAM_DETECTION
|
|
#include "openthread/jam_detection.h"
|
|
#endif
|
|
|
|
#if OPENTHREAD_ENABLE_COMMISSIONER
|
|
#include "meshcop/commissioner.hpp"
|
|
#endif
|
|
|
|
#include "openthread/platform/radio.h"
|
|
#include "openthread/platform/misc.h"
|
|
|
|
#include <common/code_utils.hpp>
|
|
#include <common/debug.hpp>
|
|
#include <ncp/ncp_base.hpp>
|
|
#include <net/ip6.hpp>
|
|
#include <openthread-instance.h>
|
|
#include <stdarg.h>
|
|
|
|
namespace Thread
|
|
{
|
|
|
|
static NcpBase *sNcpContext = NULL;
|
|
|
|
#define NCP_INVALID_SCAN_CHANNEL (-1)
|
|
#define NCP_PLAT_RESET_REASON (1U<<31)
|
|
#define NCP_ON_MESH_NETS_CHANGED_BIT_FLAG (1U<<30)
|
|
|
|
enum
|
|
{
|
|
kThreadMode_RxOnWhenIdle = (1 << 3),
|
|
kThreadMode_SecureDataRequest = (1 << 2),
|
|
kThreadMode_FullFunctionDevice = (1 << 1),
|
|
kThreadMode_FullNetworkData = (1 << 0),
|
|
};
|
|
|
|
#define RSSI_OVERRIDE_DISABLED 127 // Used for PROP_MAC_WHITELIST
|
|
|
|
#define IGNORE_RETURN_VALUE(s) do { if (s){} } while (0)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Command/Property Jump Tables
|
|
// ----------------------------------------------------------------------------
|
|
|
|
const NcpBase::CommandHandlerEntry NcpBase::mCommandHandlerTable[] =
|
|
{
|
|
{ SPINEL_CMD_NOOP, &NcpBase::CommandHandler_NOOP },
|
|
{ SPINEL_CMD_RESET, &NcpBase::CommandHandler_RESET },
|
|
{ SPINEL_CMD_PROP_VALUE_GET, &NcpBase::CommandHandler_PROP_VALUE_GET },
|
|
{ SPINEL_CMD_PROP_VALUE_SET, &NcpBase::CommandHandler_PROP_VALUE_SET },
|
|
{ SPINEL_CMD_PROP_VALUE_INSERT, &NcpBase::CommandHandler_PROP_VALUE_INSERT },
|
|
{ SPINEL_CMD_PROP_VALUE_REMOVE, &NcpBase::CommandHandler_PROP_VALUE_REMOVE },
|
|
{ SPINEL_CMD_NET_SAVE, &NcpBase::CommandHandler_NET_SAVE },
|
|
{ SPINEL_CMD_NET_CLEAR, &NcpBase::CommandHandler_NET_CLEAR },
|
|
{ SPINEL_CMD_NET_RECALL, &NcpBase::CommandHandler_NET_RECALL },
|
|
};
|
|
|
|
const NcpBase::GetPropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] =
|
|
{
|
|
{ SPINEL_PROP_LAST_STATUS, &NcpBase::GetPropertyHandler_LAST_STATUS },
|
|
{ SPINEL_PROP_PROTOCOL_VERSION, &NcpBase::GetPropertyHandler_PROTOCOL_VERSION },
|
|
{ SPINEL_PROP_INTERFACE_TYPE, &NcpBase::GetPropertyHandler_INTERFACE_TYPE },
|
|
{ SPINEL_PROP_VENDOR_ID, &NcpBase::GetPropertyHandler_VENDOR_ID },
|
|
{ SPINEL_PROP_CAPS, &NcpBase::GetPropertyHandler_CAPS },
|
|
{ SPINEL_PROP_NCP_VERSION, &NcpBase::GetPropertyHandler_NCP_VERSION },
|
|
{ SPINEL_PROP_INTERFACE_COUNT, &NcpBase::GetPropertyHandler_INTERFACE_COUNT },
|
|
{ SPINEL_PROP_POWER_STATE, &NcpBase::GetPropertyHandler_POWER_STATE },
|
|
{ SPINEL_PROP_HWADDR, &NcpBase::GetPropertyHandler_HWADDR },
|
|
{ SPINEL_PROP_LOCK, &NcpBase::GetPropertyHandler_LOCK },
|
|
|
|
{ SPINEL_PROP_PHY_ENABLED, &NcpBase::GetPropertyHandler_PHY_ENABLED },
|
|
{ SPINEL_PROP_PHY_FREQ, &NcpBase::GetPropertyHandler_PHY_FREQ },
|
|
{ SPINEL_PROP_PHY_CHAN_SUPPORTED, &NcpBase::GetPropertyHandler_PHY_CHAN_SUPPORTED },
|
|
{ SPINEL_PROP_PHY_CHAN, &NcpBase::GetPropertyHandler_PHY_CHAN },
|
|
{ SPINEL_PROP_PHY_RSSI, &NcpBase::GetPropertyHandler_PHY_RSSI },
|
|
{ SPINEL_PROP_PHY_TX_POWER, &NcpBase::GetPropertyHandler_PHY_TX_POWER },
|
|
|
|
{ SPINEL_PROP_MAC_SCAN_STATE, &NcpBase::GetPropertyHandler_MAC_SCAN_STATE },
|
|
{ SPINEL_PROP_MAC_SCAN_MASK, &NcpBase::GetPropertyHandler_MAC_SCAN_MASK },
|
|
{ SPINEL_PROP_MAC_SCAN_PERIOD, &NcpBase::GetPropertyHandler_MAC_SCAN_PERIOD },
|
|
{ SPINEL_PROP_MAC_15_4_PANID, &NcpBase::GetPropertyHandler_MAC_15_4_PANID },
|
|
{ SPINEL_PROP_MAC_15_4_LADDR, &NcpBase::GetPropertyHandler_MAC_15_4_LADDR },
|
|
{ SPINEL_PROP_MAC_15_4_SADDR, &NcpBase::GetPropertyHandler_MAC_15_4_SADDR },
|
|
{ SPINEL_PROP_MAC_RAW_STREAM_ENABLED, &NcpBase::GetPropertyHandler_MAC_RAW_STREAM_ENABLED },
|
|
{ SPINEL_PROP_MAC_PROMISCUOUS_MODE, &NcpBase::GetPropertyHandler_MAC_PROMISCUOUS_MODE },
|
|
{ SPINEL_PROP_MAC_EXTENDED_ADDR, &NcpBase::GetPropertyHandler_MAC_EXTENDED_ADDR },
|
|
|
|
{ SPINEL_PROP_NET_SAVED, &NcpBase::GetPropertyHandler_NET_SAVED },
|
|
{ SPINEL_PROP_NET_IF_UP, &NcpBase::GetPropertyHandler_NET_IF_UP },
|
|
{ SPINEL_PROP_NET_STACK_UP, &NcpBase::GetPropertyHandler_NET_STACK_UP },
|
|
{ SPINEL_PROP_NET_ROLE, &NcpBase::GetPropertyHandler_NET_ROLE },
|
|
{ SPINEL_PROP_NET_NETWORK_NAME, &NcpBase::GetPropertyHandler_NET_NETWORK_NAME },
|
|
{ SPINEL_PROP_NET_XPANID, &NcpBase::GetPropertyHandler_NET_XPANID },
|
|
{ SPINEL_PROP_NET_MASTER_KEY, &NcpBase::GetPropertyHandler_NET_MASTER_KEY },
|
|
{ SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER, &NcpBase::GetPropertyHandler_NET_KEY_SEQUENCE_COUNTER },
|
|
{ SPINEL_PROP_NET_PARTITION_ID, &NcpBase::GetPropertyHandler_NET_PARTITION_ID },
|
|
{ SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME, &NcpBase::GetPropertyHandler_NET_KEY_SWITCH_GUARDTIME},
|
|
|
|
{ SPINEL_PROP_THREAD_LEADER_ADDR, &NcpBase::GetPropertyHandler_THREAD_LEADER_ADDR },
|
|
{ SPINEL_PROP_THREAD_PARENT, &NcpBase::GetPropertyHandler_THREAD_PARENT },
|
|
{ SPINEL_PROP_THREAD_CHILD_TABLE, &NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE },
|
|
{ SPINEL_PROP_THREAD_NEIGHBOR_TABLE, &NcpBase::GetPropertyHandler_THREAD_NEIGHBOR_TABLE },
|
|
{ SPINEL_PROP_THREAD_LEADER_RID, &NcpBase::GetPropertyHandler_THREAD_LEADER_RID },
|
|
{ SPINEL_PROP_THREAD_LEADER_WEIGHT, &NcpBase::GetPropertyHandler_THREAD_LEADER_WEIGHT },
|
|
{ SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT, &NcpBase::GetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT },
|
|
{ SPINEL_PROP_THREAD_NETWORK_DATA, &NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA },
|
|
{ SPINEL_PROP_THREAD_NETWORK_DATA_VERSION, &NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA_VERSION },
|
|
{ SPINEL_PROP_THREAD_STABLE_NETWORK_DATA, &NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA },
|
|
{ SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION, &NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA_VERSION },
|
|
{ SPINEL_PROP_THREAD_LEADER_NETWORK_DATA, &NcpBase::GetPropertyHandler_THREAD_LEADER_NETWORK_DATA },
|
|
{ SPINEL_PROP_THREAD_STABLE_LEADER_NETWORK_DATA, &NcpBase::GetPropertyHandler_THREAD_STABLE_LEADER_NETWORK_DATA },
|
|
{ SPINEL_PROP_THREAD_LOCAL_ROUTES, &NcpBase::GetPropertyHandler_THREAD_LOCAL_ROUTES },
|
|
{ SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::GetPropertyHandler_THREAD_ASSISTING_PORTS },
|
|
{ SPINEL_PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE, &NcpBase::GetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE },
|
|
{ SPINEL_PROP_THREAD_ROUTER_ROLE_ENABLED, &NcpBase::GetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED },
|
|
|
|
#if OPENTHREAD_ENABLE_COMMISSIONER
|
|
{ SPINEL_PROP_THREAD_COMMISSIONER_ENABLED, &NcpBase::GetPropertyHandler_THREAD_COMMISSIONER_ENABLED },
|
|
#endif
|
|
|
|
{ SPINEL_PROP_MAC_WHITELIST, &NcpBase::GetPropertyHandler_MAC_WHITELIST },
|
|
{ SPINEL_PROP_MAC_WHITELIST_ENABLED, &NcpBase::GetPropertyHandler_MAC_WHITELIST_ENABLED },
|
|
{ SPINEL_PROP_THREAD_MODE, &NcpBase::GetPropertyHandler_THREAD_MODE },
|
|
{ SPINEL_PROP_THREAD_CHILD_COUNT_MAX, &NcpBase::GetPropertyHandler_THREAD_CHILD_COUNT_MAX },
|
|
{ SPINEL_PROP_THREAD_CHILD_TIMEOUT, &NcpBase::GetPropertyHandler_THREAD_CHILD_TIMEOUT },
|
|
{ SPINEL_PROP_THREAD_RLOC16, &NcpBase::GetPropertyHandler_THREAD_RLOC16 },
|
|
{ SPINEL_PROP_THREAD_ROUTER_UPGRADE_THRESHOLD, &NcpBase::GetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD },
|
|
{ SPINEL_PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD, &NcpBase::GetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD },
|
|
{ SPINEL_PROP_THREAD_CONTEXT_REUSE_DELAY, &NcpBase::GetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY },
|
|
{ SPINEL_PROP_THREAD_NETWORK_ID_TIMEOUT, &NcpBase::GetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT },
|
|
{ SPINEL_PROP_THREAD_ON_MESH_NETS, &NcpBase::GetPropertyHandler_THREAD_ON_MESH_NETS },
|
|
{ SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING, &NcpBase::GetPropertyHandler_NET_REQUIRE_JOIN_EXISTING },
|
|
{ SPINEL_PROP_THREAD_ROUTER_SELECTION_JITTER, &NcpBase::GetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER },
|
|
|
|
{ SPINEL_PROP_IPV6_ML_PREFIX, &NcpBase::GetPropertyHandler_IPV6_ML_PREFIX },
|
|
{ SPINEL_PROP_IPV6_ML_ADDR, &NcpBase::GetPropertyHandler_IPV6_ML_ADDR },
|
|
{ SPINEL_PROP_IPV6_LL_ADDR, &NcpBase::GetPropertyHandler_IPV6_LL_ADDR },
|
|
{ SPINEL_PROP_IPV6_ADDRESS_TABLE, &NcpBase::GetPropertyHandler_IPV6_ADDRESS_TABLE },
|
|
{ SPINEL_PROP_IPV6_ROUTE_TABLE, &NcpBase::GetPropertyHandler_IPV6_ROUTE_TABLE },
|
|
{ SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD, &NcpBase::GetPropertyHandler_IPV6_ICMP_PING_OFFLOAD },
|
|
{ SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU, &NcpBase::GetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU },
|
|
|
|
{ SPINEL_PROP_STREAM_NET, &NcpBase::GetPropertyHandler_STREAM_NET },
|
|
|
|
#if OPENTHREAD_ENABLE_JAM_DETECTION
|
|
{ SPINEL_PROP_JAM_DETECT_ENABLE, &NcpBase::GetPropertyHandler_JAM_DETECT_ENABLE },
|
|
{ SPINEL_PROP_JAM_DETECTED, &NcpBase::GetPropertyHandler_JAM_DETECTED },
|
|
{ SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD, &NcpBase::GetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD },
|
|
{ SPINEL_PROP_JAM_DETECT_WINDOW, &NcpBase::GetPropertyHandler_JAM_DETECT_WINDOW },
|
|
{ SPINEL_PROP_JAM_DETECT_BUSY, &NcpBase::GetPropertyHandler_JAM_DETECT_BUSY },
|
|
{ SPINEL_PROP_JAM_DETECT_HISTORY_BITMAP, &NcpBase::GetPropertyHandler_JAM_DETECT_HISTORY_BITMAP },
|
|
#endif
|
|
|
|
{ SPINEL_PROP_CNTR_TX_PKT_TOTAL, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_PKT_ACK_REQ, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_PKT_ACKED, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_PKT_NO_ACK_REQ, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_PKT_DATA, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_PKT_DATA_POLL, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_PKT_BEACON, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_PKT_BEACON_REQ, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_PKT_OTHER, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_PKT_RETRY, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_PKT_UNICAST, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_PKT_BROADCAST, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_ERR_CCA, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_ERR_ABORT, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_PKT_TOTAL, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_PKT_DATA, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_PKT_DATA_POLL, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_PKT_BEACON, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_PKT_BEACON_REQ, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_PKT_OTHER, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_PKT_FILT_WL, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_PKT_FILT_DA, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_PKT_UNICAST, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_PKT_BROADCAST, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_ERR_EMPTY, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_ERR_UKWN_NBR, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_ERR_NVLD_SADDR, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_ERR_SECURITY, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_ERR_BAD_FCS, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_ERR_OTHER, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_PKT_DUP, &NcpBase::GetPropertyHandler_MAC_CNTR },
|
|
|
|
{ SPINEL_PROP_CNTR_TX_IP_SEC_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_IP_INSEC_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_IP_DROPPED, &NcpBase::GetPropertyHandler_NCP_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_IP_SEC_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_IP_INSEC_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_IP_DROPPED, &NcpBase::GetPropertyHandler_NCP_CNTR },
|
|
{ SPINEL_PROP_CNTR_TX_SPINEL_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_SPINEL_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR },
|
|
{ SPINEL_PROP_CNTR_RX_SPINEL_ERR, &NcpBase::GetPropertyHandler_NCP_CNTR },
|
|
|
|
{ SPINEL_PROP_MSG_BUFFER_COUNTERS, &NcpBase::GetPropertyHandler_MSG_BUFFER_COUNTERS },
|
|
{ SPINEL_PROP_DEBUG_TEST_ASSERT, &NcpBase::GetPropertyHandler_DEBUG_TEST_ASSERT },
|
|
{ SPINEL_PROP_DEBUG_NCP_LOG_LEVEL, &NcpBase::GetPropertyHandler_DEBUG_NCP_LOG_LEVEL },
|
|
|
|
#if OPENTHREAD_ENABLE_LEGACY
|
|
{ SPINEL_PROP_NEST_LEGACY_ULA_PREFIX, &NcpBase::GetPropertyHandler_NEST_LEGACY_ULA_PREFIX },
|
|
#endif
|
|
};
|
|
|
|
const NcpBase::SetPropertyHandlerEntry NcpBase::mSetPropertyHandlerTable[] =
|
|
{
|
|
{ SPINEL_PROP_POWER_STATE, &NcpBase::SetPropertyHandler_POWER_STATE },
|
|
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
{ SPINEL_PROP_PHY_ENABLED, &NcpBase::SetPropertyHandler_PHY_ENABLED },
|
|
{ SPINEL_PROP_MAC_15_4_SADDR, &NcpBase::SetPropertyHandler_MAC_15_4_SADDR },
|
|
{ SPINEL_PROP_STREAM_RAW, &NcpBase::SetPropertyHandler_STREAM_RAW },
|
|
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
|
|
{ SPINEL_PROP_PHY_TX_POWER, &NcpBase::SetPropertyHandler_PHY_TX_POWER },
|
|
{ SPINEL_PROP_PHY_CHAN, &NcpBase::SetPropertyHandler_PHY_CHAN },
|
|
{ SPINEL_PROP_MAC_PROMISCUOUS_MODE, &NcpBase::SetPropertyHandler_MAC_PROMISCUOUS_MODE },
|
|
|
|
{ SPINEL_PROP_MAC_SCAN_MASK, &NcpBase::SetPropertyHandler_MAC_SCAN_MASK },
|
|
{ SPINEL_PROP_MAC_SCAN_STATE, &NcpBase::SetPropertyHandler_MAC_SCAN_STATE },
|
|
{ SPINEL_PROP_MAC_SCAN_PERIOD, &NcpBase::SetPropertyHandler_MAC_SCAN_PERIOD },
|
|
{ SPINEL_PROP_MAC_15_4_PANID, &NcpBase::SetPropertyHandler_MAC_15_4_PANID },
|
|
{ SPINEL_PROP_MAC_15_4_LADDR, &NcpBase::SetPropertyHandler_MAC_15_4_LADDR },
|
|
{ SPINEL_PROP_MAC_RAW_STREAM_ENABLED, &NcpBase::SetPropertyHandler_MAC_RAW_STREAM_ENABLED },
|
|
|
|
{ SPINEL_PROP_NET_IF_UP, &NcpBase::SetPropertyHandler_NET_IF_UP },
|
|
{ SPINEL_PROP_NET_STACK_UP, &NcpBase::SetPropertyHandler_NET_STACK_UP },
|
|
{ SPINEL_PROP_NET_ROLE, &NcpBase::SetPropertyHandler_NET_ROLE },
|
|
{ SPINEL_PROP_NET_NETWORK_NAME, &NcpBase::SetPropertyHandler_NET_NETWORK_NAME },
|
|
{ SPINEL_PROP_NET_XPANID, &NcpBase::SetPropertyHandler_NET_XPANID },
|
|
{ SPINEL_PROP_NET_MASTER_KEY, &NcpBase::SetPropertyHandler_NET_MASTER_KEY },
|
|
{ SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER, &NcpBase::SetPropertyHandler_NET_KEY_SEQUENCE_COUNTER },
|
|
{ SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME, &NcpBase::SetPropertyHandler_NET_KEY_SWITCH_GUARDTIME},
|
|
|
|
{ SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT, &NcpBase::SetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT },
|
|
{ SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::SetPropertyHandler_THREAD_ASSISTING_PORTS },
|
|
{ SPINEL_PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE, &NcpBase::SetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE },
|
|
{ SPINEL_PROP_THREAD_NETWORK_ID_TIMEOUT, &NcpBase::SetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT },
|
|
{ SPINEL_PROP_THREAD_ROUTER_ROLE_ENABLED, &NcpBase::SetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED },
|
|
|
|
{ SPINEL_PROP_STREAM_NET_INSECURE, &NcpBase::SetPropertyHandler_STREAM_NET_INSECURE },
|
|
{ SPINEL_PROP_STREAM_NET, &NcpBase::SetPropertyHandler_STREAM_NET },
|
|
|
|
{ SPINEL_PROP_IPV6_ML_PREFIX, &NcpBase::SetPropertyHandler_IPV6_ML_PREFIX },
|
|
{ SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD, &NcpBase::SetPropertyHandler_IPV6_ICMP_PING_OFFLOAD },
|
|
{ SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU, &NcpBase::SetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU },
|
|
|
|
{ SPINEL_PROP_MAC_WHITELIST, &NcpBase::SetPropertyHandler_MAC_WHITELIST },
|
|
{ SPINEL_PROP_MAC_WHITELIST_ENABLED, &NcpBase::SetPropertyHandler_MAC_WHITELIST_ENABLED },
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
{ SPINEL_PROP_MAC_SRC_MATCH_ENABLED, &NcpBase::SetPropertyHandler_MAC_SRC_MATCH_ENABLED },
|
|
{ SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, &NcpBase::SetPropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES },
|
|
{ SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, &NcpBase::SetPropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES },
|
|
#endif
|
|
{ SPINEL_PROP_THREAD_MODE, &NcpBase::SetPropertyHandler_THREAD_MODE },
|
|
{ SPINEL_PROP_THREAD_CHILD_COUNT_MAX, &NcpBase::SetPropertyHandler_THREAD_CHILD_COUNT_MAX },
|
|
{ SPINEL_PROP_THREAD_CHILD_TIMEOUT, &NcpBase::SetPropertyHandler_THREAD_CHILD_TIMEOUT },
|
|
{ SPINEL_PROP_THREAD_ROUTER_UPGRADE_THRESHOLD, &NcpBase::SetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD },
|
|
{ SPINEL_PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD, &NcpBase::SetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD },
|
|
{ SPINEL_PROP_THREAD_CONTEXT_REUSE_DELAY, &NcpBase::SetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY },
|
|
{ SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING, &NcpBase::SetPropertyHandler_NET_REQUIRE_JOIN_EXISTING },
|
|
{ SPINEL_PROP_THREAD_ROUTER_SELECTION_JITTER, &NcpBase::SetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER },
|
|
{ SPINEL_PROP_THREAD_PREFERRED_ROUTER_ID, &NcpBase::SetPropertyHandler_THREAD_PREFERRED_ROUTER_ID },
|
|
{ SPINEL_PROP_DEBUG_NCP_LOG_LEVEL, &NcpBase::SetPropertyHandler_DEBUG_NCP_LOG_LEVEL },
|
|
|
|
#if OPENTHREAD_ENABLE_JAM_DETECTION
|
|
{ SPINEL_PROP_JAM_DETECT_ENABLE, &NcpBase::SetPropertyHandler_JAM_DETECT_ENABLE },
|
|
{ SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD, &NcpBase::SetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD },
|
|
{ SPINEL_PROP_JAM_DETECT_WINDOW, &NcpBase::SetPropertyHandler_JAM_DETECT_WINDOW },
|
|
{ SPINEL_PROP_JAM_DETECT_BUSY, &NcpBase::SetPropertyHandler_JAM_DETECT_BUSY },
|
|
#endif
|
|
|
|
#if OPENTHREAD_ENABLE_DIAG
|
|
{ SPINEL_PROP_NEST_STREAM_MFG, &NcpBase::SetPropertyHandler_NEST_STREAM_MFG },
|
|
#endif
|
|
|
|
#if OPENTHREAD_ENABLE_LEGACY
|
|
{ SPINEL_PROP_NEST_LEGACY_ULA_PREFIX, &NcpBase::SetPropertyHandler_NEST_LEGACY_ULA_PREFIX },
|
|
#endif
|
|
|
|
#if OPENTHREAD_ENABLE_COMMISSIONER
|
|
{ SPINEL_PROP_THREAD_COMMISSIONER_ENABLED, &NcpBase::SetPropertyHandler_THREAD_COMMISSIONER_ENABLED },
|
|
#endif
|
|
};
|
|
|
|
const NcpBase::InsertPropertyHandlerEntry NcpBase::mInsertPropertyHandlerTable[] =
|
|
{
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
{ SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, &NcpBase::InsertPropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES },
|
|
{ SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, &NcpBase::InsertPropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES },
|
|
#endif
|
|
{ SPINEL_PROP_IPV6_ADDRESS_TABLE, &NcpBase::InsertPropertyHandler_IPV6_ADDRESS_TABLE },
|
|
{ SPINEL_PROP_THREAD_LOCAL_ROUTES, &NcpBase::InsertPropertyHandler_THREAD_LOCAL_ROUTES },
|
|
{ SPINEL_PROP_THREAD_ON_MESH_NETS, &NcpBase::InsertPropertyHandler_THREAD_ON_MESH_NETS },
|
|
{ SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::InsertPropertyHandler_THREAD_ASSISTING_PORTS },
|
|
|
|
#if OPENTHREAD_ENABLE_COMMISSIONER
|
|
{ SPINEL_PROP_THREAD_JOINERS, &NcpBase::NcpBase::InsertPropertyHandler_THREAD_JOINERS },
|
|
#endif
|
|
|
|
{ SPINEL_PROP_CNTR_RESET, &NcpBase::SetPropertyHandler_CNTR_RESET },
|
|
|
|
{ SPINEL_PROP_MAC_WHITELIST, &NcpBase::InsertPropertyHandler_MAC_WHITELIST },
|
|
};
|
|
|
|
const NcpBase::RemovePropertyHandlerEntry NcpBase::mRemovePropertyHandlerTable[] =
|
|
{
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
{ SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, &NcpBase::RemovePropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES },
|
|
{ SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, &NcpBase::RemovePropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES },
|
|
#endif
|
|
{ SPINEL_PROP_IPV6_ADDRESS_TABLE, &NcpBase::RemovePropertyHandler_IPV6_ADDRESS_TABLE },
|
|
{ SPINEL_PROP_THREAD_LOCAL_ROUTES, &NcpBase::RemovePropertyHandler_THREAD_LOCAL_ROUTES },
|
|
{ SPINEL_PROP_THREAD_ON_MESH_NETS, &NcpBase::RemovePropertyHandler_THREAD_ON_MESH_NETS },
|
|
{ SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::RemovePropertyHandler_THREAD_ASSISTING_PORTS },
|
|
{ SPINEL_PROP_MAC_WHITELIST, &NcpBase::RemovePropertyHandler_MAC_WHITELIST },
|
|
{ SPINEL_PROP_THREAD_ACTIVE_ROUTER_IDS, &NcpBase::RemovePropertyHandler_THREAD_ACTIVE_ROUTER_IDS },
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Utility Functions
|
|
// ----------------------------------------------------------------------------
|
|
|
|
static spinel_status_t ThreadErrorToSpinelStatus(ThreadError error)
|
|
{
|
|
spinel_status_t ret;
|
|
|
|
switch (error)
|
|
{
|
|
case kThreadError_None:
|
|
ret = SPINEL_STATUS_OK;
|
|
break;
|
|
|
|
case kThreadError_Failed:
|
|
ret = SPINEL_STATUS_FAILURE;
|
|
break;
|
|
|
|
case kThreadError_Drop:
|
|
ret = SPINEL_STATUS_DROPPED;
|
|
break;
|
|
|
|
case kThreadError_NoBufs:
|
|
ret = SPINEL_STATUS_NOMEM;
|
|
break;
|
|
|
|
case kThreadError_Busy:
|
|
ret = SPINEL_STATUS_BUSY;
|
|
break;
|
|
|
|
case kThreadError_Parse:
|
|
ret = SPINEL_STATUS_PARSE_ERROR;
|
|
break;
|
|
|
|
case kThreadError_InvalidArgs:
|
|
ret = SPINEL_STATUS_INVALID_ARGUMENT;
|
|
break;
|
|
|
|
case kThreadError_NotImplemented:
|
|
ret = SPINEL_STATUS_UNIMPLEMENTED;
|
|
break;
|
|
|
|
case kThreadError_InvalidState:
|
|
ret = SPINEL_STATUS_INVALID_STATE;
|
|
break;
|
|
|
|
case kThreadError_NoAck:
|
|
ret = SPINEL_STATUS_NO_ACK;
|
|
break;
|
|
|
|
case kThreadError_ChannelAccessFailure:
|
|
ret = SPINEL_STATUS_CCA_FAILURE;
|
|
break;
|
|
|
|
case kThreadError_Already:
|
|
ret = SPINEL_STATUS_ALREADY;
|
|
break;
|
|
|
|
case kThreadError_NotFound:
|
|
ret = SPINEL_STATUS_ITEM_NOT_FOUND;
|
|
break;
|
|
|
|
|
|
default:
|
|
// Unknown error code. Wrap it as a Spinel status and return that.
|
|
ret = static_cast<spinel_status_t>(SPINEL_STATUS_STACK_NATIVE__BEGIN + error);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static spinel_status_t ResetReasonToSpinelStatus(otPlatResetReason reason)
|
|
{
|
|
spinel_status_t ret;
|
|
switch (reason)
|
|
{
|
|
case kPlatResetReason_PowerOn:
|
|
ret = SPINEL_STATUS_RESET_POWER_ON;
|
|
break;
|
|
case kPlatResetReason_External:
|
|
ret = SPINEL_STATUS_RESET_EXTERNAL;
|
|
break;
|
|
case kPlatResetReason_Software:
|
|
ret = SPINEL_STATUS_RESET_SOFTWARE;
|
|
break;
|
|
case kPlatResetReason_Fault:
|
|
ret = SPINEL_STATUS_RESET_FAULT;
|
|
break;
|
|
case kPlatResetReason_Crash:
|
|
ret = SPINEL_STATUS_RESET_CRASH;
|
|
break;
|
|
case kPlatResetReason_Assert:
|
|
ret = SPINEL_STATUS_RESET_ASSERT;
|
|
break;
|
|
case kPlatResetReason_Watchdog:
|
|
ret = SPINEL_STATUS_RESET_WATCHDOG;
|
|
break;
|
|
case kPlatResetReason_Other:
|
|
ret = SPINEL_STATUS_RESET_OTHER;
|
|
break;
|
|
default:
|
|
ret = SPINEL_STATUS_RESET_UNKNOWN;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static uint8_t BorderRouterConfigToFlagByte(const otBorderRouterConfig &config)
|
|
{
|
|
uint8_t flags(0);
|
|
|
|
if (config.mPreferred)
|
|
{
|
|
flags |= SPINEL_NET_FLAG_PREFERRED;
|
|
}
|
|
|
|
if (config.mSlaac)
|
|
{
|
|
flags |= SPINEL_NET_FLAG_SLAAC;
|
|
}
|
|
|
|
if (config.mDhcp)
|
|
{
|
|
flags |= SPINEL_NET_FLAG_DHCP;
|
|
}
|
|
|
|
if (config.mDefaultRoute)
|
|
{
|
|
flags |= SPINEL_NET_FLAG_DEFAULT_ROUTE;
|
|
}
|
|
|
|
if (config.mConfigure)
|
|
{
|
|
flags |= SPINEL_NET_FLAG_CONFIGURE;
|
|
}
|
|
|
|
if (config.mOnMesh)
|
|
{
|
|
flags |= SPINEL_NET_FLAG_ON_MESH;
|
|
}
|
|
|
|
flags |= (config.mPreference << SPINEL_NET_FLAG_PREFERENCE_OFFSET);
|
|
|
|
return flags;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Class Boilerplate
|
|
// ----------------------------------------------------------------------------
|
|
|
|
NcpBase::NcpBase(otInstance *aInstance):
|
|
mInstance(aInstance),
|
|
mTxFrameBuffer(mTxBuffer, sizeof(mTxBuffer)),
|
|
mLastStatus(SPINEL_STATUS_OK),
|
|
mSupportedChannelMask(kPhySupportedChannelMask),
|
|
mChannelMask(kPhySupportedChannelMask),
|
|
mScanPeriod(200), // ms
|
|
mUpdateChangedPropsTask(aInstance->mIp6.mTaskletScheduler, &NcpBase::UpdateChangedProps, this),
|
|
mChangedFlags(NCP_PLAT_RESET_REASON),
|
|
mShouldSignalEndOfScan(false),
|
|
#if OPENTHREAD_ENABLE_JAM_DETECTION
|
|
mShouldSignalJamStateChange(false),
|
|
#endif
|
|
mDroppedReplyTid(0),
|
|
mDroppedReplyTidBitSet(0),
|
|
mNextExpectedTid(0),
|
|
mAllowLocalNetworkDataChange(false),
|
|
mRequireJoinExistingNetwork(false),
|
|
mIsRawStreamEnabled(false),
|
|
mDisableStreamWrite(false),
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
mCurTransmitTID(0),
|
|
mCurReceiveChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL),
|
|
mCurScanChannel(NCP_INVALID_SCAN_CHANNEL),
|
|
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
|
|
|
|
mFramingErrorCounter(0),
|
|
mRxSpinelFrameCounter(0),
|
|
mRxSpinelOutOfOrderTidCounter(0),
|
|
mTxSpinelFrameCounter(0),
|
|
mInboundSecureIpFrameCounter(0),
|
|
mInboundInsecureIpFrameCounter(0),
|
|
mOutboundSecureIpFrameCounter(0),
|
|
mOutboundInsecureIpFrameCounter(0),
|
|
mDroppedOutboundIpFrameCounter(0),
|
|
mDroppedInboundIpFrameCounter(0)
|
|
{
|
|
assert(mInstance != NULL);
|
|
|
|
sNcpContext = this;
|
|
|
|
otSetStateChangedCallback(mInstance, &NcpBase::HandleNetifStateChanged, this);
|
|
otIp6SetReceiveCallback(mInstance, &NcpBase::HandleDatagramFromStack, this);
|
|
otIp6SetReceiveFilterEnabled(mInstance, true);
|
|
otLinkSetPcapCallback(mInstance, &NcpBase::HandleRawFrame, static_cast<void*>(this));
|
|
otIcmp6SetEchoEnabled(mInstance, false);
|
|
|
|
mUpdateChangedPropsTask.Post();
|
|
|
|
#if OPENTHREAD_ENABLE_LEGACY
|
|
mLegacyNodeDidJoin = false;
|
|
mLegacyHandlers = NULL;
|
|
memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix));
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Outbound Frame methods
|
|
// ----------------------------------------------------------------------------
|
|
|
|
ThreadError NcpBase::OutboundFrameBegin(void)
|
|
{
|
|
return mTxFrameBuffer.InFrameBegin();
|
|
}
|
|
|
|
ThreadError NcpBase::OutboundFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength)
|
|
{
|
|
return mTxFrameBuffer.InFrameFeedData(aDataBuffer, aDataBufferLength);
|
|
}
|
|
|
|
ThreadError NcpBase::OutboundFrameFeedMessage(otMessage *aMessage)
|
|
{
|
|
return mTxFrameBuffer.InFrameFeedMessage(aMessage);
|
|
}
|
|
|
|
ThreadError NcpBase::OutboundFrameEnd(void)
|
|
{
|
|
return mTxFrameBuffer.InFrameEnd();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Outbound Datagram Handling
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void NcpBase::HandleDatagramFromStack(otMessage *aMessage, void *aContext)
|
|
{
|
|
static_cast<NcpBase *>(aContext)->HandleDatagramFromStack(aMessage);
|
|
}
|
|
|
|
void NcpBase::HandleDatagramFromStack(otMessage *aMessage)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
bool isSecure = otMessageIsLinkSecurityEnabled(aMessage);
|
|
uint16_t length = otMessageGetLength(aMessage);
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
|
|
SuccessOrExit(
|
|
errorCode = OutboundFrameFeedPacked(
|
|
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S,
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
isSecure
|
|
? SPINEL_PROP_STREAM_NET
|
|
: SPINEL_PROP_STREAM_NET_INSECURE,
|
|
length
|
|
));
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameFeedMessage(aMessage));
|
|
|
|
// Set the aMessage pointer to NULL, to indicate that it does not need to be freed at the exit.
|
|
// The aMessage is now owned by the OutboundFrame and will be freed when the frame is either successfully sent and
|
|
// then removed, or if the frame gets discarded.
|
|
aMessage = NULL;
|
|
|
|
// Append any metadata (rssi, lqi, channel, etc) here!
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
|
|
if (aMessage != NULL)
|
|
{
|
|
otMessageFree(aMessage);
|
|
}
|
|
|
|
if (errorCode != kThreadError_None)
|
|
{
|
|
SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_DROPPED);
|
|
mDroppedOutboundIpFrameCounter++;
|
|
}
|
|
else
|
|
{
|
|
if (isSecure)
|
|
{
|
|
mOutboundSecureIpFrameCounter++;
|
|
}
|
|
else
|
|
{
|
|
mOutboundInsecureIpFrameCounter++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Raw frame handling
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void NcpBase::HandleRawFrame(const RadioPacket *aFrame, void *aContext)
|
|
{
|
|
static_cast<NcpBase *>(aContext)->HandleRawFrame(aFrame);
|
|
}
|
|
|
|
void NcpBase::HandleRawFrame(const RadioPacket *aFrame)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint16_t flags = 0;
|
|
|
|
if (!mIsRawStreamEnabled)
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
|
|
if (aFrame->mDidTX)
|
|
{
|
|
flags |= SPINEL_MD_FLAG_TX;
|
|
}
|
|
|
|
// Append frame header and frame length
|
|
SuccessOrExit(
|
|
errorCode = OutboundFrameFeedPacked(
|
|
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S,
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
SPINEL_PROP_STREAM_RAW,
|
|
aFrame->mLength
|
|
)
|
|
);
|
|
|
|
// Append the frame contents
|
|
SuccessOrExit(
|
|
errorCode = OutboundFrameFeedData(
|
|
aFrame->mPsdu,
|
|
aFrame->mLength
|
|
)
|
|
);
|
|
|
|
// Append metadata (rssi, etc)
|
|
SuccessOrExit(
|
|
errorCode = OutboundFrameFeedPacked(
|
|
SPINEL_DATATYPE_INT8_S
|
|
SPINEL_DATATYPE_INT8_S
|
|
SPINEL_DATATYPE_UINT16_S
|
|
SPINEL_DATATYPE_STRUCT_S( // PHY-data
|
|
"" // Empty for now
|
|
)
|
|
SPINEL_DATATYPE_STRUCT_S( // Vendor-data
|
|
"" // Empty for now
|
|
),
|
|
aFrame->mPower, // TX Power
|
|
-128, // Noise Floor (Currently unused)
|
|
flags // Flags
|
|
|
|
// Skip PHY and Vendor data for now
|
|
)
|
|
);
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Scan Results Glue
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void NcpBase::HandleActiveScanResult_Jump(otActiveScanResult *aResult, void *aContext)
|
|
{
|
|
static_cast<NcpBase *>(aContext)->HandleActiveScanResult(aResult);
|
|
}
|
|
|
|
void NcpBase::HandleActiveScanResult(otActiveScanResult *result)
|
|
{
|
|
ThreadError errorCode;
|
|
|
|
if (result)
|
|
{
|
|
uint8_t flags = static_cast<uint8_t>(result->mVersion << SPINEL_BEACON_THREAD_FLAG_VERSION_SHIFT);
|
|
|
|
if (result->mIsJoinable)
|
|
{
|
|
flags |= SPINEL_BEACON_THREAD_FLAG_JOINABLE;
|
|
}
|
|
|
|
if (result->mIsNative)
|
|
{
|
|
flags |= SPINEL_BEACON_THREAD_FLAG_NATIVE;
|
|
}
|
|
|
|
NcpBase::SendPropertyUpdate(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_INSERTED,
|
|
SPINEL_PROP_MAC_SCAN_BEACON,
|
|
SPINEL_DATATYPE_MAC_SCAN_RESULT_S(
|
|
SPINEL_802_15_4_DATATYPE_MAC_SCAN_RESULT_V1_S,
|
|
SPINEL_NET_DATATYPE_MAC_SCAN_RESULT_V1_S
|
|
),
|
|
result->mChannel,
|
|
result->mRssi,
|
|
result->mExtAddress.m8, // laddr
|
|
0xFFFF, // saddr, Not given
|
|
result->mPanId,
|
|
result->mLqi,
|
|
SPINEL_PROTOCOL_TYPE_THREAD,
|
|
flags,
|
|
result->mNetworkName.m8,
|
|
result->mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// We are finished with the scan, so send out
|
|
// a property update indicating such.
|
|
errorCode = SendPropertyUpdate(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
SPINEL_PROP_MAC_SCAN_STATE,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
SPINEL_SCAN_STATE_IDLE
|
|
);
|
|
|
|
// If we could not send the end of scan inidciator message now (no
|
|
// buffer space), we set `mShouldSignalEndOfScan` to true to send
|
|
// it out when buffer space becomes available.
|
|
if (errorCode != kThreadError_None)
|
|
{
|
|
mShouldSignalEndOfScan = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void NcpBase::HandleEnergyScanResult_Jump(otEnergyScanResult *aResult, void *aContext)
|
|
{
|
|
static_cast<NcpBase *>(aContext)->HandleEnergyScanResult(aResult);
|
|
}
|
|
|
|
void NcpBase::HandleEnergyScanResult(otEnergyScanResult *aResult)
|
|
{
|
|
ThreadError errorCode;
|
|
|
|
if (aResult)
|
|
{
|
|
NcpBase::SendPropertyUpdate(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_INSERTED,
|
|
SPINEL_PROP_MAC_ENERGY_SCAN_RESULT,
|
|
"Cc",
|
|
aResult->mChannel,
|
|
aResult->mMaxRssi
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// We are finished with the scan, so send out
|
|
// a property update indicating such.
|
|
errorCode = SendPropertyUpdate(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
SPINEL_PROP_MAC_SCAN_STATE,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
SPINEL_SCAN_STATE_IDLE
|
|
);
|
|
|
|
// If we could not send the end of scan indicator message now (no
|
|
// buffer space), we set `mShouldSignalEndOfScan` to true to send
|
|
// it out when buffer space becomes available.
|
|
if (errorCode != kThreadError_None)
|
|
{
|
|
mShouldSignalEndOfScan = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Raw Link-Layer Datapath Glue
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
|
|
void NcpBase::LinkRawReceiveDone(otInstance *, RadioPacket *aPacket, ThreadError aError)
|
|
{
|
|
sNcpContext->LinkRawReceiveDone(aPacket, aError);
|
|
}
|
|
|
|
void NcpBase::LinkRawReceiveDone(RadioPacket *aPacket, ThreadError aError)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint16_t flags = 0;
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
|
|
if (aPacket->mDidTX)
|
|
{
|
|
flags |= SPINEL_MD_FLAG_TX;
|
|
}
|
|
|
|
// Append frame header and frame length
|
|
SuccessOrExit(
|
|
errorCode = OutboundFrameFeedPacked(
|
|
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S,
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
SPINEL_PROP_STREAM_RAW,
|
|
(aError == kThreadError_None) ? aPacket->mLength : 0
|
|
)
|
|
);
|
|
|
|
if (aError == kThreadError_None)
|
|
{
|
|
// Append the frame contents
|
|
SuccessOrExit(
|
|
errorCode = OutboundFrameFeedData(
|
|
aPacket->mPsdu,
|
|
aPacket->mLength
|
|
)
|
|
);
|
|
}
|
|
|
|
// Append metadata (rssi, etc)
|
|
SuccessOrExit(
|
|
errorCode = OutboundFrameFeedPacked(
|
|
SPINEL_DATATYPE_INT8_S
|
|
SPINEL_DATATYPE_INT8_S
|
|
SPINEL_DATATYPE_UINT16_S
|
|
SPINEL_DATATYPE_STRUCT_S( // PHY-data
|
|
SPINEL_DATATYPE_UINT8_S // 802.15.4 channel
|
|
SPINEL_DATATYPE_UINT8_S // 802.15.4 LQI
|
|
)
|
|
SPINEL_DATATYPE_STRUCT_S( // Vendor-data
|
|
SPINEL_DATATYPE_UINT_PACKED_S
|
|
),
|
|
aPacket->mPower, // TX Power
|
|
-128, // Noise Floor (Currently unused)
|
|
flags, // Flags
|
|
aPacket->mChannel, // Receive channel
|
|
aPacket->mLqi, // Link quality indicator
|
|
aError // Receive error
|
|
)
|
|
);
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void NcpBase::LinkRawTransmitDone(otInstance *, RadioPacket *aPacket, bool aFramePending, ThreadError aError)
|
|
{
|
|
sNcpContext->LinkRawTransmitDone(aPacket, aFramePending, aError);
|
|
}
|
|
|
|
void NcpBase::LinkRawTransmitDone(RadioPacket *, bool aFramePending, ThreadError aError)
|
|
{
|
|
if (mCurTransmitTID)
|
|
{
|
|
SendPropertyUpdate(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | mCurTransmitTID,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
SPINEL_PROP_LAST_STATUS,
|
|
SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_BOOL_S,
|
|
aError,
|
|
aFramePending
|
|
);
|
|
|
|
// Clear cached transmit TID
|
|
mCurTransmitTID = 0;
|
|
}
|
|
}
|
|
|
|
void NcpBase::LinkRawEnergyScanDone(otInstance *, int8_t aEnergyScanMaxRssi)
|
|
{
|
|
sNcpContext->LinkRawEnergyScanDone(aEnergyScanMaxRssi);
|
|
}
|
|
|
|
void NcpBase::LinkRawEnergyScanDone(int8_t aEnergyScanMaxRssi)
|
|
{
|
|
SendPropertyUpdate(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
SPINEL_PROP_MAC_ENERGY_SCAN_RESULT,
|
|
SPINEL_DATATYPE_UINT8_S
|
|
SPINEL_DATATYPE_INT8_S,
|
|
mCurScanChannel,
|
|
aEnergyScanMaxRssi
|
|
);
|
|
|
|
// Clear current scan channel
|
|
mCurScanChannel = NCP_INVALID_SCAN_CHANNEL;
|
|
|
|
// Make sure we are back listening on the original receive channel,
|
|
// since the energy scan could have been on a different channel.
|
|
otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone);
|
|
|
|
// We are finished with the scan, so send out
|
|
// a property update indicating such.
|
|
SendPropertyUpdate(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
SPINEL_PROP_MAC_SCAN_STATE,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
SPINEL_SCAN_STATE_IDLE
|
|
);
|
|
}
|
|
|
|
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Address Table Changed Glue
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void NcpBase::HandleNetifStateChanged(uint32_t flags, void *context)
|
|
{
|
|
NcpBase *obj = static_cast<NcpBase *>(context);
|
|
|
|
obj->mChangedFlags |= flags;
|
|
|
|
obj->mUpdateChangedPropsTask.Post();
|
|
}
|
|
|
|
void NcpBase::UpdateChangedProps(void *context)
|
|
{
|
|
NcpBase *obj = static_cast<NcpBase *>(context);
|
|
obj->UpdateChangedProps();
|
|
}
|
|
|
|
void NcpBase::UpdateChangedProps(void)
|
|
{
|
|
while (mChangedFlags != 0)
|
|
{
|
|
if ((mChangedFlags & NCP_PLAT_RESET_REASON) != 0)
|
|
{
|
|
SuccessOrExit(SendLastStatus(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
ResetReasonToSpinelStatus(otPlatGetResetReason(mInstance))
|
|
));
|
|
mChangedFlags &= ~static_cast<uint32_t>(NCP_PLAT_RESET_REASON);
|
|
}
|
|
else if ((mChangedFlags & OT_IP6_LL_ADDR_CHANGED) != 0)
|
|
{
|
|
SuccessOrExit(HandleCommandPropertyGet(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_PROP_IPV6_LL_ADDR
|
|
));
|
|
mChangedFlags &= ~static_cast<uint32_t>(OT_IP6_LL_ADDR_CHANGED);
|
|
}
|
|
else if ((mChangedFlags & OT_IP6_ML_ADDR_CHANGED) != 0)
|
|
{
|
|
SuccessOrExit(HandleCommandPropertyGet(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_PROP_IPV6_ML_ADDR
|
|
));
|
|
mChangedFlags &= ~static_cast<uint32_t>(OT_IP6_ML_ADDR_CHANGED);
|
|
}
|
|
else if ((mChangedFlags & OT_NET_ROLE) != 0)
|
|
{
|
|
if (mRequireJoinExistingNetwork)
|
|
{
|
|
switch (otThreadGetDeviceRole(mInstance))
|
|
{
|
|
case kDeviceRoleDetached:
|
|
case kDeviceRoleDisabled:
|
|
break;
|
|
|
|
default:
|
|
mRequireJoinExistingNetwork = false;
|
|
break;
|
|
}
|
|
|
|
if ( (otThreadGetDeviceRole(mInstance) == kDeviceRoleLeader)
|
|
&& otThreadIsSingleton(mInstance)
|
|
#if OPENTHREAD_ENABLE_LEGACY
|
|
&& !mLegacyNodeDidJoin
|
|
#endif
|
|
) {
|
|
mChangedFlags &= ~static_cast<uint32_t>(OT_NET_PARTITION_ID);
|
|
otThreadSetEnabled(mInstance, false);
|
|
|
|
// TODO: It would be nice to be able to indicate
|
|
// something more specific than SPINEL_STATUS_JOIN_FAILURE
|
|
// here, but it isn't clear how that would work
|
|
// with the current OpenThread API.
|
|
|
|
SuccessOrExit(SendLastStatus(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_STATUS_JOIN_FAILURE
|
|
));
|
|
|
|
SuccessOrExit(HandleCommandPropertyGet(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_PROP_NET_STACK_UP
|
|
));
|
|
}
|
|
|
|
SuccessOrExit(HandleCommandPropertyGet(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING
|
|
));
|
|
}
|
|
|
|
SuccessOrExit(HandleCommandPropertyGet(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_PROP_NET_ROLE
|
|
));
|
|
mChangedFlags &= ~static_cast<uint32_t>(OT_NET_ROLE);
|
|
}
|
|
else if ((mChangedFlags & OT_NET_PARTITION_ID) != 0)
|
|
{
|
|
SuccessOrExit(HandleCommandPropertyGet(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_PROP_NET_PARTITION_ID
|
|
));
|
|
mChangedFlags &= ~static_cast<uint32_t>(OT_NET_PARTITION_ID);
|
|
}
|
|
else if ((mChangedFlags & OT_NET_KEY_SEQUENCE_COUNTER) != 0)
|
|
{
|
|
SuccessOrExit(HandleCommandPropertyGet(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER
|
|
));
|
|
mChangedFlags &= ~static_cast<uint32_t>(OT_NET_KEY_SEQUENCE_COUNTER);
|
|
}
|
|
else if ((mChangedFlags & (OT_IP6_ADDRESS_ADDED | OT_IP6_ADDRESS_REMOVED)) != 0)
|
|
{
|
|
SuccessOrExit(HandleCommandPropertyGet(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_PROP_IPV6_ADDRESS_TABLE
|
|
));
|
|
mChangedFlags &= ~static_cast<uint32_t>(OT_IP6_ADDRESS_ADDED | OT_IP6_ADDRESS_REMOVED);
|
|
}
|
|
else if ((mChangedFlags & (OT_THREAD_CHILD_ADDED | OT_THREAD_CHILD_REMOVED)) != 0)
|
|
{
|
|
SuccessOrExit(HandleCommandPropertyGet(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_PROP_THREAD_CHILD_TABLE
|
|
));
|
|
mChangedFlags &= ~static_cast<uint32_t>(OT_THREAD_CHILD_ADDED | OT_THREAD_CHILD_REMOVED);
|
|
}
|
|
else if ((mChangedFlags & OT_THREAD_NETDATA_UPDATED) != 0)
|
|
{
|
|
SuccessOrExit(HandleCommandPropertyGet(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_PROP_THREAD_LEADER_NETWORK_DATA
|
|
));
|
|
|
|
mChangedFlags &= ~static_cast<uint32_t>(OT_THREAD_NETDATA_UPDATED);
|
|
|
|
// If the network data is updated, after successfully sending (or queuing) the
|
|
// network data spinel message, we add `NCP_ON_MESH_NETS_CHANGED_BIT_FLAG` to
|
|
// the `mChangedFlags` so that we separately send the list of on-mesh prefixes.
|
|
|
|
mChangedFlags |= NCP_ON_MESH_NETS_CHANGED_BIT_FLAG;
|
|
}
|
|
else if ((mChangedFlags & NCP_ON_MESH_NETS_CHANGED_BIT_FLAG) != 0)
|
|
{
|
|
SuccessOrExit(HandleCommandPropertyGet(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_PROP_THREAD_ON_MESH_NETS
|
|
));
|
|
|
|
mChangedFlags &= ~static_cast<uint32_t>(NCP_ON_MESH_NETS_CHANGED_BIT_FLAG);
|
|
}
|
|
else if ((mChangedFlags & (OT_IP6_RLOC_ADDED | OT_IP6_RLOC_REMOVED)) != 0)
|
|
{
|
|
mChangedFlags &= ~static_cast<uint32_t>(OT_IP6_RLOC_ADDED | OT_IP6_RLOC_REMOVED);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Serial Traffic Glue
|
|
// ----------------------------------------------------------------------------
|
|
|
|
ThreadError NcpBase::OutboundFrameSend(void)
|
|
{
|
|
ThreadError errorCode;
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameEnd());
|
|
|
|
mTxSpinelFrameCounter++;
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
void NcpBase::HandleReceive(const uint8_t *buf, uint16_t bufLength)
|
|
{
|
|
uint8_t header = 0;
|
|
unsigned int command = 0;
|
|
spinel_ssize_t parsedLength;
|
|
const uint8_t *arg_ptr = NULL;
|
|
unsigned int arg_len = 0;
|
|
ThreadError errorCode = kThreadError_None;
|
|
spinel_tid_t tid = 0;
|
|
|
|
parsedLength = spinel_datatype_unpack(buf, bufLength, SPINEL_DATATYPE_COMMAND_S SPINEL_DATATYPE_DATA_S, &header, &command, &arg_ptr, &arg_len);
|
|
tid = SPINEL_HEADER_GET_TID(header);
|
|
|
|
if (parsedLength == bufLength)
|
|
{
|
|
errorCode = HandleCommand(header, command, arg_ptr, static_cast<uint16_t>(arg_len));
|
|
|
|
// Check if we may have missed a `tid` in the sequence.
|
|
if ((mNextExpectedTid != 0) && (tid != mNextExpectedTid))
|
|
{
|
|
mRxSpinelOutOfOrderTidCounter++;
|
|
}
|
|
|
|
mNextExpectedTid = SPINEL_GET_NEXT_TID(tid);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
if (errorCode == kThreadError_NoBufs)
|
|
{
|
|
// If we cannot send a response due to buffer space not being
|
|
// available, we remember the TID of command so to send an
|
|
// error status when buffer space becomes available later.
|
|
|
|
// Valid TID range is 1-15 (zero being used as special case
|
|
// where no reply is expected). TIDs for dropped reply are
|
|
// stored in two variables: `mDroppedReplyTidBitSet` which
|
|
// is a bit set (bits 1-15 correspond to TID values 1-15).
|
|
// The first/next dropped TID value in the set is stored in
|
|
// `mDroppedReplyTid` (with value zero indicating that there
|
|
// is no dropped reply).
|
|
|
|
if (tid != 0)
|
|
{
|
|
if (mDroppedReplyTid == 0)
|
|
{
|
|
mDroppedReplyTid = tid;
|
|
}
|
|
|
|
mDroppedReplyTidBitSet |= (1 << tid);
|
|
}
|
|
}
|
|
|
|
mRxSpinelFrameCounter++;
|
|
}
|
|
|
|
void NcpBase::HandleSpaceAvailableInTxBuffer(void)
|
|
{
|
|
while (mDroppedReplyTid != 0)
|
|
{
|
|
SuccessOrExit(
|
|
SendLastStatus(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | mDroppedReplyTid,
|
|
SPINEL_STATUS_NOMEM
|
|
)
|
|
);
|
|
|
|
mDroppedReplyTidBitSet &= ~(1 << mDroppedReplyTid);
|
|
|
|
if (mDroppedReplyTidBitSet == 0)
|
|
{
|
|
mDroppedReplyTid = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
do
|
|
{
|
|
mDroppedReplyTid = SPINEL_GET_NEXT_TID(mDroppedReplyTid);
|
|
}
|
|
while ((mDroppedReplyTidBitSet & (1 << mDroppedReplyTid)) == 0);
|
|
}
|
|
|
|
if (mShouldSignalEndOfScan)
|
|
{
|
|
SuccessOrExit(
|
|
SendPropertyUpdate(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
SPINEL_PROP_MAC_SCAN_STATE,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
SPINEL_SCAN_STATE_IDLE
|
|
));
|
|
|
|
mShouldSignalEndOfScan = false;
|
|
}
|
|
|
|
#if OPENTHREAD_ENABLE_JAM_DETECTION
|
|
if (mShouldSignalJamStateChange)
|
|
{
|
|
SuccessOrExit(
|
|
SendPropertyUpdate(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
SPINEL_PROP_JAM_DETECTED,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
otJamDetectionGetState(mInstance)
|
|
));
|
|
|
|
mShouldSignalJamStateChange = false;
|
|
}
|
|
#endif // OPENTHREAD_ENABLE_JAM_DETECTION
|
|
|
|
UpdateChangedProps();
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void NcpBase::IncrementFrameErrorCounter(void)
|
|
{
|
|
mFramingErrorCounter++;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Inbound Command Handlers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
ThreadError NcpBase::HandleCommand(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len)
|
|
{
|
|
unsigned i;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
// Skip if this isn't a spinel frame
|
|
VerifyOrExit((SPINEL_HEADER_FLAG & header) == SPINEL_HEADER_FLAG, errorCode = kThreadError_InvalidArgs);
|
|
|
|
// We only support IID zero for now.
|
|
VerifyOrExit(
|
|
SPINEL_HEADER_GET_IID(header) == 0,
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_INTERFACE)
|
|
);
|
|
|
|
for (i = 0; i < sizeof(mCommandHandlerTable) / sizeof(mCommandHandlerTable[0]); i++)
|
|
{
|
|
if (mCommandHandlerTable[i].mCommand == command)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < sizeof(mCommandHandlerTable) / sizeof(mCommandHandlerTable[0]))
|
|
{
|
|
errorCode = (this->*mCommandHandlerTable[i].mHandler)(header, command, arg_ptr, arg_len);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_COMMAND);
|
|
}
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::HandleCommandPropertyGet(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
unsigned i;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
for (i = 0; i < sizeof(mGetPropertyHandlerTable) / sizeof(mGetPropertyHandlerTable[0]); i++)
|
|
{
|
|
if (mGetPropertyHandlerTable[i].mPropKey == key)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < sizeof(mGetPropertyHandlerTable) / sizeof(mGetPropertyHandlerTable[0]))
|
|
{
|
|
errorCode = (this->*mGetPropertyHandlerTable[i].mHandler)(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PROP_NOT_FOUND);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::HandleCommandPropertySet(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
unsigned i;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
for (i = 0; i < sizeof(mSetPropertyHandlerTable) / sizeof(mSetPropertyHandlerTable[0]); i++)
|
|
{
|
|
if (mSetPropertyHandlerTable[i].mPropKey == key)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < sizeof(mSetPropertyHandlerTable) / sizeof(mSetPropertyHandlerTable[0]))
|
|
{
|
|
errorCode = (this->*mSetPropertyHandlerTable[i].mHandler)(header, key, value_ptr, value_len);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PROP_NOT_FOUND);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::HandleCommandPropertyInsert(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
unsigned i;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
for (i = 0; i < sizeof(mInsertPropertyHandlerTable) / sizeof(mInsertPropertyHandlerTable[0]); i++)
|
|
{
|
|
if (mInsertPropertyHandlerTable[i].mPropKey == key)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < sizeof(mInsertPropertyHandlerTable) / sizeof(mInsertPropertyHandlerTable[0]))
|
|
{
|
|
errorCode = (this->*mInsertPropertyHandlerTable[i].mHandler)(header, key, value_ptr, value_len);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PROP_NOT_FOUND);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::HandleCommandPropertyRemove(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
unsigned i;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
for (i = 0; i < sizeof(mRemovePropertyHandlerTable) / sizeof(mRemovePropertyHandlerTable[0]); i++)
|
|
{
|
|
if (mRemovePropertyHandlerTable[i].mPropKey == key)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < sizeof(mRemovePropertyHandlerTable) / sizeof(mRemovePropertyHandlerTable[0]))
|
|
{
|
|
errorCode = (this->*mRemovePropertyHandlerTable[i].mHandler)(header, key, value_ptr, value_len);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PROP_NOT_FOUND);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Outbound Command Handlers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
ThreadError NcpBase::SendLastStatus(uint8_t header, spinel_status_t lastStatus)
|
|
{
|
|
if (SPINEL_HEADER_GET_IID(header) == 0)
|
|
{
|
|
mLastStatus = lastStatus;
|
|
}
|
|
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
SPINEL_PROP_LAST_STATUS,
|
|
SPINEL_DATATYPE_UINT_PACKED_S,
|
|
lastStatus
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::SendPropertyUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key,
|
|
const char *pack_format, ...)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
va_list args;
|
|
|
|
va_start(args, pack_format);
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, command, key));
|
|
SuccessOrExit(errorCode = OutboundFrameFeedVPacked(pack_format, args));
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
va_end(args);
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SendPropertyUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, command, key));
|
|
SuccessOrExit(errorCode = OutboundFrameFeedData(value_ptr, value_len));
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SendPropertyUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key, otMessage *aMessage)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, command, key));
|
|
SuccessOrExit(errorCode = OutboundFrameFeedMessage(aMessage));
|
|
|
|
// Set the aMessage pointer to NULL, to indicate that it does not need to be freed at the exit.
|
|
// The message is now owned by the OutboundFrame and will be freed when the frame is either successfully sent and
|
|
// then removed, or if the frame gets discarded.
|
|
aMessage = NULL;
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
|
|
if (aMessage != NULL)
|
|
{
|
|
otMessageFree(aMessage);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::OutboundFrameFeedVPacked(const char *pack_format, va_list args)
|
|
{
|
|
uint8_t buf[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.
|
|
otInstanceReset(mInstance);
|
|
|
|
// We only get to this point if the
|
|
// platform doesn't support resetting.
|
|
// In such a case we fake it.
|
|
|
|
otThreadSetEnabled(mInstance, false);
|
|
otIp6SetEnabled(mInstance, false);
|
|
|
|
errorCode = SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_RESET_SOFTWARE);
|
|
|
|
if (errorCode != kThreadError_None)
|
|
{
|
|
mChangedFlags |= NCP_PLAT_RESET_REASON;
|
|
mUpdateChangedPropsTask.Post();
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::CommandHandler_PROP_VALUE_GET(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
|
|
uint16_t arg_len)
|
|
{
|
|
unsigned int propKey = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, SPINEL_DATATYPE_UINT_PACKED_S, &propKey);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, static_cast<spinel_prop_key_t>(propKey));
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
(void)command;
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::CommandHandler_PROP_VALUE_SET(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
|
|
uint16_t arg_len)
|
|
{
|
|
unsigned int propKey = 0;
|
|
spinel_ssize_t parsedLength;
|
|
const uint8_t *value_ptr;
|
|
unsigned int value_len;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_DATA_S, &propKey, &value_ptr, &value_len);
|
|
|
|
if (parsedLength == arg_len)
|
|
{
|
|
errorCode = HandleCommandPropertySet(header, static_cast<spinel_prop_key_t>(propKey), value_ptr,
|
|
static_cast<uint16_t>(value_len));
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
(void)command;
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::CommandHandler_PROP_VALUE_INSERT(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
|
|
uint16_t arg_len)
|
|
{
|
|
unsigned int propKey = 0;
|
|
spinel_ssize_t parsedLength;
|
|
const uint8_t *value_ptr;
|
|
unsigned int value_len;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_DATA_S, &propKey, &value_ptr, &value_len);
|
|
|
|
if (parsedLength == arg_len)
|
|
{
|
|
errorCode = HandleCommandPropertyInsert(header, static_cast<spinel_prop_key_t>(propKey), value_ptr,
|
|
static_cast<uint16_t>(value_len));
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
(void)command;
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::CommandHandler_PROP_VALUE_REMOVE(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
|
|
uint16_t arg_len)
|
|
{
|
|
unsigned int propKey = 0;
|
|
spinel_ssize_t parsedLength;
|
|
const uint8_t *value_ptr;
|
|
unsigned int value_len;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_DATA_S, &propKey, &value_ptr, &value_len);
|
|
|
|
if (parsedLength == arg_len)
|
|
{
|
|
errorCode = HandleCommandPropertyRemove(header, static_cast<spinel_prop_key_t>(propKey), value_ptr,
|
|
static_cast<uint16_t>(value_len));
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
(void)command;
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::CommandHandler_NET_SAVE(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
|
|
uint16_t arg_len)
|
|
{
|
|
(void)command;
|
|
(void)arg_ptr;
|
|
(void)arg_len;
|
|
|
|
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
|
|
}
|
|
|
|
ThreadError NcpBase::CommandHandler_NET_CLEAR(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
|
|
uint16_t arg_len)
|
|
{
|
|
(void)command;
|
|
(void)arg_ptr;
|
|
(void)arg_len;
|
|
|
|
return SendLastStatus(header, ThreadErrorToSpinelStatus(otInstanceErasePersistentInfo(mInstance)));
|
|
}
|
|
|
|
ThreadError NcpBase::CommandHandler_NET_RECALL(uint8_t header, unsigned int command, const uint8_t *arg_ptr,
|
|
uint16_t arg_len)
|
|
{
|
|
(void)command;
|
|
(void)arg_ptr;
|
|
(void)arg_len;
|
|
|
|
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Individual Property Getters
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_LAST_STATUS(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(header, SPINEL_CMD_PROP_VALUE_IS, key, SPINEL_DATATYPE_UINT_PACKED_S, mLastStatus);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_PROTOCOL_VERSION(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S,
|
|
SPINEL_PROTOCOL_VERSION_THREAD_MAJOR,
|
|
SPINEL_PROTOCOL_VERSION_THREAD_MINOR
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_INTERFACE_TYPE(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT_PACKED_S,
|
|
SPINEL_PROTOCOL_TYPE_THREAD
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_VENDOR_ID(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT_PACKED_S,
|
|
0 // Vendor ID. Zero for unknown.
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_CAPS(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
|
|
|
|
// Begin adding capabilities //////////////////////////////////////////////
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_NET_THREAD_1_0));
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_COUNTERS));
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_MAC_WHITELIST));
|
|
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_MAC_RAW));
|
|
#endif
|
|
|
|
#if OPENTHREAD_ENABLE_JAM_DETECTION
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_JAM_DETECT));
|
|
#endif
|
|
|
|
// TODO: Somehow get the following capability from the radio.
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S,
|
|
SPINEL_CAP_802_15_4_2450MHZ_OQPSK));
|
|
|
|
#if OPENTHREAD_CONFIG_MAX_CHILDREN > 0
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_ROLE_ROUTER));
|
|
#endif
|
|
|
|
#if OPENTHREAD_ENABLE_LEGACY
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_NEST_LEGACY_INTERFACE));
|
|
#endif
|
|
|
|
// End adding capabilities /////////////////////////////////////////////////
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_NCP_VERSION(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UTF8_S,
|
|
otGetVersionString()
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_INTERFACE_COUNT(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
1 // Only one interface for now
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_POWER_STATE(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
// Always online at the moment
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
SPINEL_POWER_STATE_ONLINE
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_HWADDR(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
otExtAddress hwAddr;
|
|
otLinkGetFactoryAssignedIeeeEui64(mInstance, &hwAddr);
|
|
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_EUI64_S,
|
|
hwAddr.m8
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_LOCK(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
// TODO: Implement property lock (Needs API!)
|
|
(void)key;
|
|
|
|
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_PHY_ENABLED(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
otLinkRawIsEnabled(mInstance)
|
|
#else
|
|
false
|
|
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_PHY_FREQ(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
uint32_t freq_khz(0);
|
|
const uint8_t chan(otLinkGetChannel(mInstance));
|
|
|
|
if (chan == 0)
|
|
{
|
|
freq_khz = 868300;
|
|
}
|
|
else if (chan < 11)
|
|
{
|
|
freq_khz = 906000 - (2000 * 1) + 2000 * (chan);
|
|
}
|
|
else if (chan < 26)
|
|
{
|
|
freq_khz = 2405000 - (5000 * 11) + 5000 * (chan);
|
|
}
|
|
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT32_S,
|
|
freq_khz
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_PHY_CHAN_SUPPORTED(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return GetPropertyHandler_ChannelMaskHelper(header, key, mSupportedChannelMask);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_PHY_CHAN(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
otLinkGetChannel(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_PHY_RSSI(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_INT8_S,
|
|
otPlatRadioGetRssi(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_PHY_TX_POWER(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_INT8_S,
|
|
otLinkGetMaxTransmitPower(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_MAC_SCAN_STATE(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
if (otLinkRawIsEnabled(mInstance))
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
mCurScanChannel == NCP_INVALID_SCAN_CHANNEL ?
|
|
SPINEL_SCAN_STATE_IDLE :
|
|
SPINEL_SCAN_STATE_ENERGY
|
|
);
|
|
}
|
|
else
|
|
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
|
|
if (otLinkIsActiveScanInProgress(mInstance))
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
SPINEL_SCAN_STATE_BEACON
|
|
);
|
|
}
|
|
else if (otLinkIsEnergyScanInProgress(mInstance))
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
SPINEL_SCAN_STATE_ENERGY
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
SPINEL_SCAN_STATE_IDLE
|
|
);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_MAC_SCAN_PERIOD(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT16_S,
|
|
mScanPeriod
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_ChannelMaskHelper(uint8_t header, spinel_prop_key_t key, uint32_t channel_mask)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
if (0 != (channel_mask & (1 << i)))
|
|
{
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT8_S, i));
|
|
}
|
|
}
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_MAC_SCAN_MASK(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return GetPropertyHandler_ChannelMaskHelper(header, key, mChannelMask);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_MAC_15_4_PANID(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT16_S,
|
|
otLinkGetPanId(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_MAC_PROMISCUOUS_MODE(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_INT8_S,
|
|
otPlatRadioGetPromiscuous(mInstance)
|
|
? SPINEL_MAC_PROMISCUOUS_MODE_FULL
|
|
: SPINEL_MAC_PROMISCUOUS_MODE_OFF
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_MAC_15_4_LADDR(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_EUI64_S,
|
|
otLinkGetExtendedAddress(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_MAC_15_4_SADDR(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT16_S,
|
|
otLinkGetShortAddress(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_MAC_EXTENDED_ADDR(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_EUI64_S,
|
|
otLinkGetExtendedAddress(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_MAC_RAW_STREAM_ENABLED(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
mIsRawStreamEnabled
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_NET_SAVED(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
otDatasetIsCommissioned(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_NET_IF_UP(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
otIp6IsEnabled(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_NET_STACK_UP(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
otThreadGetDeviceRole(mInstance) != kDeviceRoleDisabled
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_NET_ROLE(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
spinel_net_role_t role(SPINEL_NET_ROLE_DETACHED);
|
|
|
|
switch (otThreadGetDeviceRole(mInstance))
|
|
{
|
|
case kDeviceRoleOffline:
|
|
case kDeviceRoleDisabled:
|
|
case kDeviceRoleDetached:
|
|
role = SPINEL_NET_ROLE_DETACHED;
|
|
break;
|
|
|
|
case kDeviceRoleChild:
|
|
role = SPINEL_NET_ROLE_CHILD;
|
|
break;
|
|
|
|
case kDeviceRoleRouter:
|
|
role = SPINEL_NET_ROLE_ROUTER;
|
|
break;
|
|
|
|
case kDeviceRoleLeader:
|
|
role = SPINEL_NET_ROLE_LEADER;
|
|
break;
|
|
}
|
|
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
role
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_NET_NETWORK_NAME(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UTF8_S,
|
|
otThreadGetNetworkName(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_NET_XPANID(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_DATA_S,
|
|
otThreadGetExtendedPanId(mInstance),
|
|
sizeof(spinel_net_xpanid_t)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_NET_MASTER_KEY(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
const uint8_t *ptr(NULL);
|
|
uint8_t len(0);
|
|
|
|
ptr = otThreadGetMasterKey(mInstance, &len);
|
|
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_DATA_S,
|
|
ptr,
|
|
len
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_NET_KEY_SEQUENCE_COUNTER(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT32_S,
|
|
otThreadGetKeySequenceCounter(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_NET_PARTITION_ID(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT32_S,
|
|
otThreadGetPartitionId(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_NET_KEY_SWITCH_GUARDTIME(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT32_S,
|
|
otThreadGetKeySwitchGuardTime(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA_VERSION(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
otNetDataGetVersion(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA_VERSION(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
otNetDataGetStableVersion(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint8_t network_data[255];
|
|
uint8_t network_data_len = 255;
|
|
|
|
otNetDataGetLocal(
|
|
mInstance,
|
|
false, // Stable?
|
|
network_data,
|
|
&network_data_len
|
|
);
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
|
|
SuccessOrExit(errorCode = OutboundFrameFeedData(network_data, network_data_len));
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint8_t network_data[255];
|
|
uint8_t network_data_len = 255;
|
|
|
|
otNetDataGetLocal(
|
|
mInstance,
|
|
true, // Stable?
|
|
network_data,
|
|
&network_data_len
|
|
);
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
|
|
SuccessOrExit(errorCode = OutboundFrameFeedData(network_data, network_data_len));
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_LEADER_NETWORK_DATA(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint8_t network_data[255];
|
|
uint8_t network_data_len = 255;
|
|
|
|
otNetDataGetLeader(
|
|
mInstance,
|
|
false, // Stable?
|
|
network_data,
|
|
&network_data_len
|
|
);
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
|
|
SuccessOrExit(errorCode = OutboundFrameFeedData(network_data, network_data_len));
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_STABLE_LEADER_NETWORK_DATA(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint8_t network_data[255];
|
|
uint8_t network_data_len = 255;
|
|
|
|
otNetDataGetLeader(
|
|
mInstance,
|
|
true, // Stable?
|
|
network_data,
|
|
&network_data_len
|
|
);
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
|
|
SuccessOrExit(errorCode = OutboundFrameFeedData(network_data, network_data_len));
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_LEADER_RID(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
otThreadGetLeaderRouterId(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
otThreadGetLocalLeaderWeight(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_LEADER_WEIGHT(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
otThreadGetLeaderWeight(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_LEADER_ADDR(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
otIp6Address address;
|
|
|
|
errorCode = otThreadGetLeaderRloc(mInstance, &address);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_IPv6ADDR_S,
|
|
&address
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_PARENT(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
otRouterInfo parentInfo;
|
|
|
|
errorCode = otThreadGetParentInfo(mInstance, &parentInfo);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT16_S,
|
|
parentInfo.mExtAddress.m8,
|
|
parentInfo.mRloc16
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
otChildInfo childInfo;
|
|
uint8_t maxChildren;
|
|
uint8_t modeFlags;
|
|
|
|
mDisableStreamWrite = true;
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
|
|
|
|
maxChildren = otThreadGetMaxAllowedChildren(mInstance);
|
|
|
|
for (uint8_t index = 0; index < maxChildren; index++)
|
|
{
|
|
errorCode = otThreadGetChildInfoByIndex(mInstance, index, &childInfo);
|
|
|
|
if (errorCode != kThreadError_None)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
modeFlags = 0;
|
|
|
|
if (childInfo.mRxOnWhenIdle)
|
|
{
|
|
modeFlags |= kThreadMode_RxOnWhenIdle;
|
|
}
|
|
|
|
if (childInfo.mSecureDataRequest)
|
|
{
|
|
modeFlags |= kThreadMode_SecureDataRequest;
|
|
}
|
|
|
|
if (childInfo.mFullFunction)
|
|
{
|
|
modeFlags |= kThreadMode_FullFunctionDevice;
|
|
}
|
|
|
|
if (childInfo.mFullNetworkData)
|
|
{
|
|
modeFlags |= kThreadMode_FullNetworkData;
|
|
}
|
|
|
|
SuccessOrExit(
|
|
errorCode = OutboundFrameFeedPacked(
|
|
SPINEL_DATATYPE_STRUCT_S(
|
|
SPINEL_DATATYPE_EUI64_S // EUI64 Address
|
|
SPINEL_DATATYPE_UINT16_S // Rloc16
|
|
SPINEL_DATATYPE_UINT32_S // Timeout
|
|
SPINEL_DATATYPE_UINT32_S // Age
|
|
SPINEL_DATATYPE_UINT8_S // Network Data Version
|
|
SPINEL_DATATYPE_UINT8_S // Link Quality In
|
|
SPINEL_DATATYPE_INT8_S // Average RSS
|
|
SPINEL_DATATYPE_UINT8_S // Mode (flags)
|
|
SPINEL_DATATYPE_INT8_S // Most recent RSS
|
|
),
|
|
childInfo.mExtAddress.m8,
|
|
childInfo.mRloc16,
|
|
childInfo.mTimeout,
|
|
childInfo.mAge,
|
|
childInfo.mNetworkDataVersion,
|
|
childInfo.mLinkQualityIn,
|
|
childInfo.mAverageRssi,
|
|
modeFlags,
|
|
childInfo.mLastRssi
|
|
));
|
|
}
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
mDisableStreamWrite = false;
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_NEIGHBOR_TABLE(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
otNeighborInfoIterator iter = OT_NEIGHBOR_INFO_ITERATOR_INIT;
|
|
otNeighborInfo neighInfo;
|
|
uint8_t modeFlags;
|
|
|
|
mDisableStreamWrite = true;
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
|
|
|
|
while (otThreadGetNextNeighborInfo(mInstance, &iter, &neighInfo) == kThreadError_None)
|
|
{
|
|
modeFlags = 0;
|
|
|
|
if (neighInfo.mRxOnWhenIdle)
|
|
{
|
|
modeFlags |= kThreadMode_RxOnWhenIdle;
|
|
}
|
|
|
|
if (neighInfo.mSecureDataRequest)
|
|
{
|
|
modeFlags |= kThreadMode_SecureDataRequest;
|
|
}
|
|
|
|
if (neighInfo.mFullFunction)
|
|
{
|
|
modeFlags |= kThreadMode_FullFunctionDevice;
|
|
}
|
|
|
|
if (neighInfo.mFullNetworkData)
|
|
{
|
|
modeFlags |= kThreadMode_FullNetworkData;
|
|
}
|
|
|
|
SuccessOrExit(
|
|
errorCode = OutboundFrameFeedPacked(
|
|
SPINEL_DATATYPE_STRUCT_S(
|
|
SPINEL_DATATYPE_EUI64_S // EUI64 Address
|
|
SPINEL_DATATYPE_UINT16_S // Rloc16
|
|
SPINEL_DATATYPE_UINT32_S // Age
|
|
SPINEL_DATATYPE_UINT8_S // Link Quality In
|
|
SPINEL_DATATYPE_INT8_S // Average RSS
|
|
SPINEL_DATATYPE_UINT8_S // Mode (flags)
|
|
SPINEL_DATATYPE_BOOL_S // Is Child
|
|
SPINEL_DATATYPE_UINT32_S // Link Frame Counter
|
|
SPINEL_DATATYPE_UINT32_S // MLE Frame Counter
|
|
SPINEL_DATATYPE_INT8_S // Most recent RSS
|
|
),
|
|
neighInfo.mExtAddress.m8,
|
|
neighInfo.mRloc16,
|
|
neighInfo.mAge,
|
|
neighInfo.mLinkQualityIn,
|
|
neighInfo.mAverageRssi,
|
|
modeFlags,
|
|
neighInfo.mIsChild,
|
|
neighInfo.mLinkFrameCounter,
|
|
neighInfo.mMleFrameCounter,
|
|
neighInfo.mLastRssi
|
|
));
|
|
}
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
mDisableStreamWrite = false;
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint8_t num_entries = 0;
|
|
const uint16_t *ports = otIp6GetUnsecurePorts(mInstance, &num_entries);
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
|
|
|
|
for (; num_entries != 0; ports++, num_entries--)
|
|
{
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked("S", *ports));
|
|
}
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
mAllowLocalNetworkDataChange
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
otThreadIsRouterRoleEnabled(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_ON_MESH_NETS(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
otBorderRouterConfig border_router_config;
|
|
uint8_t flags;
|
|
|
|
mDisableStreamWrite = true;
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
|
|
|
|
// Fill from non-local network data first
|
|
for (otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT ;;)
|
|
{
|
|
errorCode = otNetDataGetNextPrefixInfo(mInstance, false, &iter, &border_router_config);
|
|
|
|
if (errorCode != kThreadError_None)
|
|
{
|
|
break;
|
|
}
|
|
|
|
flags = BorderRouterConfigToFlagByte(border_router_config);
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(
|
|
SPINEL_DATATYPE_STRUCT_S(
|
|
SPINEL_DATATYPE_IPv6ADDR_S // IPv6 Prefix
|
|
SPINEL_DATATYPE_UINT8_S // Prefix Length (in bits)
|
|
SPINEL_DATATYPE_BOOL_S // isStable
|
|
SPINEL_DATATYPE_UINT8_S // Flags
|
|
SPINEL_DATATYPE_BOOL_S // isLocal
|
|
),
|
|
&border_router_config.mPrefix,
|
|
64,
|
|
border_router_config.mStable,
|
|
flags,
|
|
true
|
|
));
|
|
}
|
|
|
|
// Fill from local network data last
|
|
for (otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT ;;)
|
|
{
|
|
errorCode = otNetDataGetNextPrefixInfo(mInstance, true, &iter, &border_router_config);
|
|
|
|
if (errorCode != kThreadError_None)
|
|
{
|
|
break;
|
|
}
|
|
|
|
flags = BorderRouterConfigToFlagByte(border_router_config);
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(
|
|
SPINEL_DATATYPE_STRUCT_S(
|
|
SPINEL_DATATYPE_IPv6ADDR_S // IPv6 Prefix
|
|
SPINEL_DATATYPE_UINT8_S // Prefix Length (in bits)
|
|
SPINEL_DATATYPE_BOOL_S // isStable
|
|
SPINEL_DATATYPE_UINT8_S // Flags
|
|
SPINEL_DATATYPE_BOOL_S // isLocal
|
|
),
|
|
&border_router_config.mPrefix,
|
|
64,
|
|
border_router_config.mStable,
|
|
flags,
|
|
false
|
|
));
|
|
}
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
mDisableStreamWrite = false;
|
|
return errorCode;
|
|
}
|
|
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_IPV6_ML_PREFIX(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
const uint8_t *ml_prefix = otThreadGetMeshLocalPrefix(mInstance);
|
|
|
|
if (ml_prefix)
|
|
{
|
|
otIp6Address addr;
|
|
|
|
memcpy(addr.mFields.m8, ml_prefix, 8);
|
|
|
|
// Zero out the last 8 bytes.
|
|
memset(addr.mFields.m8 + 8, 0, 8);
|
|
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_UINT8_S,
|
|
&addr,
|
|
64
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_VOID_S
|
|
);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_IPV6_ML_ADDR(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
const otIp6Address *ml64 = otThreadGetMeshLocalEid(mInstance);
|
|
|
|
if (ml64)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_IPv6ADDR_S,
|
|
ml64
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_VOID_S
|
|
);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_IPV6_LL_ADDR(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
// TODO!
|
|
(void)key;
|
|
|
|
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_IPV6_ADDRESS_TABLE(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
mDisableStreamWrite = true;
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
|
|
|
|
for (const otNetifAddress *address = otIp6GetUnicastAddresses(mInstance); address; address = address->mNext)
|
|
{
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(
|
|
SPINEL_DATATYPE_STRUCT_S("6CLL"),
|
|
&address->mAddress,
|
|
address->mPrefixLength,
|
|
address->mPreferred ? 0xffffffff : 0,
|
|
address->mValid ? 0xffffffff : 0
|
|
));
|
|
}
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
mDisableStreamWrite = false;
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_IPV6_ROUTE_TABLE(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
// TODO: Implement get route table
|
|
(void)key;
|
|
|
|
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_IPV6_ICMP_PING_OFFLOAD(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
otIcmp6IsEchoEnabled(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
// Note reverse logic: passthru enabled = filter disabled
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
!otIp6IsReceiveFilterEnabled(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_LOCAL_ROUTES(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
// TODO: Implement get external route table
|
|
(void)key;
|
|
|
|
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_STREAM_NET(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
// TODO: Implement explicit data poll.
|
|
(void)key;
|
|
|
|
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
|
|
}
|
|
|
|
#if OPENTHREAD_ENABLE_JAM_DETECTION
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_ENABLE(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
otJamDetectionIsEnabled(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_JAM_DETECTED(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
otJamDetectionGetState(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_INT8_S,
|
|
otJamDetectionGetRssiThreshold(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_WINDOW(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
otJamDetectionGetWindow(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_BUSY(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
otJamDetectionGetBusyPeriod(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_HISTORY_BITMAP(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
uint64_t historyBitmap = otJamDetectionGetHistoryBitmap(mInstance);
|
|
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_UINT32_S,
|
|
static_cast<uint32_t>(historyBitmap & 0xffffffff),
|
|
static_cast<uint32_t>(historyBitmap >> 32)
|
|
);
|
|
}
|
|
|
|
#endif // OPENTHREAD_ENABLE_JAM_DETECTION
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_MAC_CNTR(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
uint32_t value;
|
|
const otMacCounters *macCounters;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
macCounters = otLinkGetCounters(mInstance);
|
|
|
|
assert(macCounters != NULL);
|
|
|
|
switch (key)
|
|
{
|
|
case SPINEL_PROP_CNTR_TX_PKT_TOTAL:
|
|
value = macCounters->mTxTotal;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_PKT_ACK_REQ:
|
|
value = macCounters->mTxAckRequested;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_PKT_ACKED:
|
|
value = macCounters->mTxAcked;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_PKT_NO_ACK_REQ:
|
|
value = macCounters->mTxNoAckRequested;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_PKT_DATA:
|
|
value = macCounters->mTxData;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_PKT_DATA_POLL:
|
|
value = macCounters->mTxDataPoll;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_PKT_BEACON:
|
|
value = macCounters->mTxBeacon;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_PKT_BEACON_REQ:
|
|
value = macCounters->mTxBeaconRequest;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_PKT_OTHER:
|
|
value = macCounters->mTxOther;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_PKT_RETRY:
|
|
value = macCounters->mTxRetry;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_ERR_CCA:
|
|
value = macCounters->mTxErrCca;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_PKT_UNICAST:
|
|
value = macCounters->mTxUnicast;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_PKT_BROADCAST:
|
|
value = macCounters->mTxBroadcast;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_ERR_ABORT:
|
|
value = macCounters->mTxErrAbort;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_PKT_TOTAL:
|
|
value = macCounters->mRxTotal;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_PKT_DATA:
|
|
value = macCounters->mRxData;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_PKT_DATA_POLL:
|
|
value = macCounters->mRxDataPoll;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_PKT_BEACON:
|
|
value = macCounters->mRxBeacon;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_PKT_BEACON_REQ:
|
|
value = macCounters->mRxBeaconRequest;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_PKT_OTHER:
|
|
value = macCounters->mRxOther;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_PKT_FILT_WL:
|
|
value = macCounters->mRxWhitelistFiltered;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_PKT_FILT_DA:
|
|
value = macCounters->mRxDestAddrFiltered;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_PKT_DUP:
|
|
value = macCounters->mRxDuplicated;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_PKT_UNICAST:
|
|
value = macCounters->mRxUnicast;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_PKT_BROADCAST:
|
|
value = macCounters->mRxBroadcast;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_ERR_EMPTY:
|
|
value = macCounters->mRxErrNoFrame;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_ERR_UKWN_NBR:
|
|
value = macCounters->mRxErrUnknownNeighbor;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_ERR_NVLD_SADDR:
|
|
value = macCounters->mRxErrInvalidSrcAddr;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_ERR_SECURITY:
|
|
value = macCounters->mRxErrSec;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_ERR_BAD_FCS:
|
|
value = macCounters->mRxErrFcs;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_ERR_OTHER:
|
|
value = macCounters->mRxErrOther;
|
|
break;
|
|
|
|
default:
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_INTERNAL_ERROR);
|
|
goto bail;
|
|
break;
|
|
}
|
|
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT32_S,
|
|
value
|
|
);
|
|
|
|
bail:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_NCP_CNTR(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
uint32_t value;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
switch (key)
|
|
{
|
|
case SPINEL_PROP_CNTR_TX_IP_SEC_TOTAL:
|
|
value = mInboundSecureIpFrameCounter;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_IP_INSEC_TOTAL:
|
|
value = mInboundInsecureIpFrameCounter;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_IP_DROPPED:
|
|
value = mDroppedInboundIpFrameCounter;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_IP_SEC_TOTAL:
|
|
value = mOutboundSecureIpFrameCounter;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_IP_INSEC_TOTAL:
|
|
value = mOutboundInsecureIpFrameCounter;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_IP_DROPPED:
|
|
value = mDroppedOutboundIpFrameCounter;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_TX_SPINEL_TOTAL:
|
|
value = mTxSpinelFrameCounter;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_SPINEL_TOTAL:
|
|
value = mRxSpinelFrameCounter;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_SPINEL_OUT_OF_ORDER_TID:
|
|
value = mRxSpinelOutOfOrderTidCounter;
|
|
break;
|
|
|
|
case SPINEL_PROP_CNTR_RX_SPINEL_ERR:
|
|
value = mFramingErrorCounter;
|
|
break;
|
|
|
|
default:
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_INTERNAL_ERROR);
|
|
goto bail;
|
|
break;
|
|
}
|
|
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT32_S,
|
|
value
|
|
);
|
|
|
|
bail:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_MSG_BUFFER_COUNTERS(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
otBufferInfo bufferInfo;
|
|
|
|
otMessageGetBufferInfo(mInstance, &bufferInfo);
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked("SSSSSSSSSSSSSSSS",
|
|
bufferInfo.mTotalBuffers,
|
|
bufferInfo.mFreeBuffers,
|
|
bufferInfo.m6loSendMessages,
|
|
bufferInfo.m6loSendBuffers,
|
|
bufferInfo.m6loReassemblyMessages,
|
|
bufferInfo.m6loReassemblyBuffers,
|
|
bufferInfo.mIp6Messages,
|
|
bufferInfo.mIp6Buffers,
|
|
bufferInfo.mMplMessages,
|
|
bufferInfo.mMplBuffers,
|
|
bufferInfo.mMleMessages,
|
|
bufferInfo.mMleBuffers,
|
|
bufferInfo.mArpMessages,
|
|
bufferInfo.mArpBuffers,
|
|
bufferInfo.mCoapClientMessages,
|
|
bufferInfo.mCoapClientBuffers
|
|
));
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_DEBUG_TEST_ASSERT(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
assert(false);
|
|
|
|
// We only get to this point if `assert(false)`
|
|
// does not cause an NCP reset on the platform.
|
|
// In such a case we return `false` as the
|
|
// property value to indicate this.
|
|
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
false
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_DEBUG_NCP_LOG_LEVEL(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
uint8_t logLevel = 0;
|
|
|
|
switch (otGetDynamicLogLevel(mInstance))
|
|
{
|
|
case kLogLevelNone:
|
|
logLevel = SPINEL_NCP_LOG_LEVEL_EMERG;
|
|
break;
|
|
|
|
case kLogLevelCrit:
|
|
logLevel = SPINEL_NCP_LOG_LEVEL_CRIT;
|
|
break;
|
|
|
|
case kLogLevelWarn:
|
|
logLevel = SPINEL_NCP_LOG_LEVEL_WARN;
|
|
break;
|
|
|
|
case kLogLevelInfo:
|
|
logLevel = SPINEL_NCP_LOG_LEVEL_INFO;
|
|
break;
|
|
|
|
case kLogLevelDebg:
|
|
logLevel = SPINEL_NCP_LOG_LEVEL_DEBUG;
|
|
break;
|
|
}
|
|
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
logLevel
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_MAC_WHITELIST(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
otMacWhitelistEntry entry;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
mDisableStreamWrite = true;
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameBegin());
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, header, SPINEL_CMD_PROP_VALUE_IS, key));
|
|
|
|
for (uint8_t i = 0; (i != 255) && (errorCode == kThreadError_None); i++)
|
|
{
|
|
errorCode = otLinkGetWhitelistEntry(mInstance, i, &entry);
|
|
|
|
if (errorCode != kThreadError_None)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (entry.mValid)
|
|
{
|
|
if (!entry.mFixedRssi)
|
|
{
|
|
entry.mRssi = RSSI_OVERRIDE_DISABLED;
|
|
}
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_STRUCT_S("Ec"), entry.mExtAddress.m8, entry.mRssi));
|
|
}
|
|
}
|
|
|
|
SuccessOrExit(errorCode = OutboundFrameSend());
|
|
|
|
exit:
|
|
mDisableStreamWrite = false;
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_MAC_WHITELIST_ENABLED(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
otLinkIsWhitelistEnabled(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_MODE(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
uint8_t numeric_mode(0);
|
|
otLinkModeConfig mode_config(otThreadGetLinkMode(mInstance));
|
|
|
|
if (mode_config.mRxOnWhenIdle)
|
|
{
|
|
numeric_mode |= kThreadMode_RxOnWhenIdle;
|
|
}
|
|
|
|
if (mode_config.mSecureDataRequests)
|
|
{
|
|
numeric_mode |= kThreadMode_SecureDataRequest;
|
|
}
|
|
|
|
if (mode_config.mDeviceType)
|
|
{
|
|
numeric_mode |= kThreadMode_FullFunctionDevice;
|
|
}
|
|
|
|
if (mode_config.mNetworkData)
|
|
{
|
|
numeric_mode |= kThreadMode_FullNetworkData;
|
|
}
|
|
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
numeric_mode
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_CHILD_COUNT_MAX(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
otThreadGetMaxAllowedChildren(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_CHILD_TIMEOUT(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT32_S,
|
|
otThreadGetChildTimeout(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_RLOC16(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT16_S,
|
|
otThreadGetRloc16(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
otThreadGetRouterUpgradeThreshold(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
otThreadGetRouterDowngradeThreshold(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
otThreadGetRouterSelectionJitter(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT32_S,
|
|
otThreadGetContextIdReuseDelay(mInstance)
|
|
);
|
|
}
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
otThreadGetNetworkIdTimeout(mInstance)
|
|
);
|
|
}
|
|
|
|
#if OPENTHREAD_ENABLE_COMMISSIONER
|
|
ThreadError NcpBase::GetPropertyHandler_THREAD_COMMISSIONER_ENABLED(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
bool isEnabled = false;
|
|
if (otCommissionerGetState(mInstance) == kCommissionerStateActive)
|
|
isEnabled = true;
|
|
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
isEnabled
|
|
);
|
|
}
|
|
#endif
|
|
|
|
ThreadError NcpBase::GetPropertyHandler_NET_REQUIRE_JOIN_EXISTING(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
mRequireJoinExistingNetwork
|
|
);
|
|
}
|
|
|
|
#if OPENTHREAD_ENABLE_LEGACY
|
|
ThreadError NcpBase::GetPropertyHandler_NEST_LEGACY_ULA_PREFIX(uint8_t header, spinel_prop_key_t key)
|
|
{
|
|
return SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_DATA_S,
|
|
mLegacyUlaPrefix,
|
|
sizeof(mLegacyUlaPrefix)
|
|
);
|
|
}
|
|
#endif // OPENTHREAD_ENABLE_LEGACY
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Individual Property Setters
|
|
// ----------------------------------------------------------------------------
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_POWER_STATE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
// TODO: Implement POWER_STATE
|
|
(void)key;
|
|
(void)value_ptr;
|
|
(void)value_len;
|
|
|
|
return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED);
|
|
}
|
|
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_PHY_ENABLED(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
bool value = false;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
&value
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
if (value == false)
|
|
{
|
|
// If we have raw stream enabled stop receiving
|
|
if (mIsRawStreamEnabled)
|
|
{
|
|
otLinkRawSleep(mInstance);
|
|
}
|
|
|
|
errorCode = otLinkRawSetEnable(mInstance, false);
|
|
}
|
|
else
|
|
{
|
|
errorCode = otLinkRawSetEnable(mInstance, true);
|
|
|
|
// If we have raw stream enabled already, start receiving
|
|
if (errorCode == kThreadError_None && mIsRawStreamEnabled)
|
|
{
|
|
errorCode = otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_Parse;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_PHY_TX_POWER(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
int8_t value = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_INT8_S,
|
|
&value
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otLinkSetMaxTransmitPower(mInstance, value);
|
|
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_PHY_CHAN(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
unsigned int i = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT_PACKED_S,
|
|
&i
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otLinkSetChannel(mInstance, static_cast<uint8_t>(i));
|
|
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
// Cache the channel. If the raw link layer isn't enabled yet, the otSetChannel call
|
|
// doesn't call into the radio layer to set the channel. We will have to do it
|
|
// manually whenever the radios are enabled and/or raw stream is enabled.
|
|
mCurReceiveChannel = static_cast<uint8_t>(i);
|
|
|
|
// Make sure we are update the receiving channel if raw link is enabled and we have raw
|
|
// stream enabled already
|
|
if (otLinkRawIsEnabled(mInstance) && mIsRawStreamEnabled)
|
|
{
|
|
errorCode = otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone);
|
|
}
|
|
}
|
|
|
|
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_MAC_PROMISCUOUS_MODE(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
uint8_t i = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&i
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
switch (i)
|
|
{
|
|
case SPINEL_MAC_PROMISCUOUS_MODE_OFF:
|
|
otPlatRadioSetPromiscuous(mInstance, false);
|
|
errorCode = kThreadError_None;
|
|
break;
|
|
|
|
case SPINEL_MAC_PROMISCUOUS_MODE_NETWORK:
|
|
case SPINEL_MAC_PROMISCUOUS_MODE_FULL:
|
|
otPlatRadioSetPromiscuous(mInstance, true);
|
|
errorCode = kThreadError_None;
|
|
break;
|
|
|
|
default:
|
|
errorCode = kThreadError_InvalidArgs;
|
|
break;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_MAC_SCAN_MASK(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint32_t new_mask(0);
|
|
|
|
for (; value_len != 0; value_len--, value_ptr++)
|
|
{
|
|
if ((value_ptr[0] > 31)
|
|
|| (mSupportedChannelMask & (1 << value_ptr[0])) == 0
|
|
)
|
|
{
|
|
errorCode = kThreadError_InvalidArgs;
|
|
break;
|
|
}
|
|
|
|
new_mask |= (1 << value_ptr[0]);
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
mChannelMask = new_mask;
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_MAC_SCAN_PERIOD(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
uint16_t tmp(mScanPeriod);
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT16_S,
|
|
&tmp
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
mScanPeriod = tmp;
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_NET_REQUIRE_JOIN_EXISTING(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
bool tmp(mRequireJoinExistingNetwork);
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
&tmp
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
mRequireJoinExistingNetwork = tmp;
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
bool HasOnly1BitSet(uint32_t aValue)
|
|
{
|
|
return aValue != 0 && ((aValue & (aValue-1)) == 0);
|
|
}
|
|
|
|
uint8_t IndexOfMSB(uint32_t aValue)
|
|
{
|
|
uint8_t index = 0;
|
|
while (aValue >>= 1)
|
|
{
|
|
index++;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_MAC_SCAN_STATE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
uint8_t i = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&i
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
switch (i)
|
|
{
|
|
case SPINEL_SCAN_STATE_IDLE:
|
|
errorCode = kThreadError_None;
|
|
break;
|
|
|
|
case SPINEL_SCAN_STATE_BEACON:
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
if (otLinkRawIsEnabled(mInstance))
|
|
{
|
|
errorCode = kThreadError_NotImplemented;
|
|
}
|
|
else
|
|
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
|
|
{
|
|
errorCode = otLinkActiveScan(
|
|
mInstance,
|
|
mChannelMask,
|
|
mScanPeriod,
|
|
&HandleActiveScanResult_Jump,
|
|
this
|
|
);
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
mShouldSignalEndOfScan = false;
|
|
}
|
|
|
|
break;
|
|
|
|
case SPINEL_SCAN_STATE_ENERGY:
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
if (otLinkRawIsEnabled(mInstance))
|
|
{
|
|
// Make sure we aren't already scanning and that we have
|
|
// only 1 bit set for the channel mask.
|
|
if (mCurScanChannel == NCP_INVALID_SCAN_CHANNEL)
|
|
{
|
|
if (HasOnly1BitSet(mChannelMask))
|
|
{
|
|
uint8_t scanChannel = IndexOfMSB(mChannelMask);
|
|
mCurScanChannel = (int8_t)scanChannel;
|
|
|
|
errorCode = otLinkRawEnergyScan(
|
|
mInstance,
|
|
scanChannel,
|
|
mScanPeriod,
|
|
LinkRawEnergyScanDone
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_InvalidArgs;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_InvalidState;
|
|
}
|
|
}
|
|
else
|
|
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
|
|
{
|
|
errorCode = otLinkEnergyScan(
|
|
mInstance,
|
|
mChannelMask,
|
|
mScanPeriod,
|
|
&HandleEnergyScanResult_Jump,
|
|
this
|
|
);
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
mShouldSignalEndOfScan = false;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
errorCode = kThreadError_InvalidArgs;
|
|
break;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_MAC_15_4_PANID(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
uint16_t tmp;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT16_S,
|
|
&tmp
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otLinkSetPanId(mInstance, tmp);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_MAC_15_4_LADDR(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
otExtAddress *tmp;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_EUI64_S,
|
|
&tmp
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otLinkSetExtendedAddress(mInstance, tmp);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_MAC_RAW_STREAM_ENABLED(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr,uint16_t value_len)
|
|
{
|
|
bool value = false;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
&value
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
if (otLinkRawIsEnabled(mInstance))
|
|
{
|
|
if (value)
|
|
{
|
|
errorCode = otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone);
|
|
}
|
|
else
|
|
{
|
|
errorCode = otLinkRawSleep(mInstance);
|
|
}
|
|
}
|
|
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_Parse;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
mIsRawStreamEnabled = value;
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_MAC_15_4_SADDR(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
uint16_t tmp;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT16_S,
|
|
&tmp
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otLinkRawSetShortAddress(mInstance, tmp);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_STREAM_RAW(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
if (otLinkRawIsEnabled(mInstance))
|
|
{
|
|
spinel_ssize_t parsedLength(0);
|
|
uint8_t *frame_buffer(NULL);
|
|
unsigned int frame_len(0);
|
|
|
|
RadioPacket *packet = otLinkRawGetTransmitBuffer(mInstance);
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_DATA_WLEN_S
|
|
SPINEL_DATATYPE_UINT8_S
|
|
SPINEL_DATATYPE_INT8_S,
|
|
&frame_buffer,
|
|
&frame_len,
|
|
&packet->mChannel,
|
|
&packet->mPower
|
|
);
|
|
|
|
if (parsedLength > 0 && frame_len <= kMaxPHYPacketSize)
|
|
{
|
|
// Cache the transaction ID for async response
|
|
mCurTransmitTID = SPINEL_HEADER_GET_TID(header);
|
|
|
|
// Update packet buffer and length
|
|
packet->mLength = static_cast<uint8_t>(frame_len);
|
|
memcpy(packet->mPsdu, frame_buffer, packet->mLength);
|
|
|
|
// TODO: This should be later added in the STREAM_RAW argument to allow user to directly specify it.
|
|
packet->mMaxTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT;
|
|
|
|
// Pass packet to the radio layer. Note, this fails if we
|
|
// haven't enabled raw stream or are already transmitting.
|
|
errorCode = otLinkRawTransmit(mInstance, packet, &NcpBase::LinkRawTransmitDone);
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_Parse;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_InvalidState;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
// Don't do anything here yet. We will complete the transaction when we get a transmit done callback
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
(void)key;
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_NET_IF_UP(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
bool value = false;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
&value
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otIp6SetEnabled(mInstance, value);
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_Parse;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_NET_STACK_UP(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
bool value = false;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
&value
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
// If the value has changed...
|
|
if ((value != false) != (otThreadGetDeviceRole(mInstance) != kDeviceRoleDisabled))
|
|
{
|
|
if (value != false)
|
|
{
|
|
errorCode = otThreadSetEnabled(mInstance, true);
|
|
|
|
#if OPENTHREAD_ENABLE_LEGACY
|
|
mLegacyNodeDidJoin = false;
|
|
if (mLegacyHandlers != NULL)
|
|
{
|
|
if (mLegacyHandlers->mStartLegacy)
|
|
{
|
|
mLegacyHandlers->mStartLegacy();
|
|
}
|
|
}
|
|
#endif // OPENTHREAD_ENABLE_LEGACY
|
|
}
|
|
else
|
|
{
|
|
errorCode = otThreadSetEnabled(mInstance, false);
|
|
|
|
#if OPENTHREAD_ENABLE_LEGACY
|
|
mLegacyNodeDidJoin = false;
|
|
if (mLegacyHandlers != NULL)
|
|
{
|
|
if (mLegacyHandlers->mStopLegacy)
|
|
{
|
|
mLegacyHandlers->mStopLegacy();
|
|
}
|
|
}
|
|
#endif // OPENTHREAD_ENABLE_LEGACY
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_Parse;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_NET_ROLE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
unsigned int i(0);
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT_PACKED_S,
|
|
&i
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
switch (i)
|
|
{
|
|
case SPINEL_NET_ROLE_DETACHED:
|
|
errorCode = otThreadBecomeDetached(mInstance);
|
|
break;
|
|
|
|
case SPINEL_NET_ROLE_ROUTER:
|
|
errorCode = otThreadBecomeRouter(mInstance);
|
|
break;
|
|
|
|
case SPINEL_NET_ROLE_LEADER:
|
|
errorCode = otThreadBecomeLeader(mInstance);
|
|
break;
|
|
|
|
case SPINEL_NET_ROLE_CHILD:
|
|
errorCode = otThreadBecomeChild(mInstance, kMleAttachAnyPartition);
|
|
break;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_NET_NETWORK_NAME(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
const char *string(NULL);
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UTF8_S,
|
|
&string
|
|
);
|
|
|
|
if ((parsedLength > 0) && (string != NULL))
|
|
{
|
|
errorCode = otThreadSetNetworkName(mInstance, string);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_NET_XPANID(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
const uint8_t *ptr = NULL;
|
|
spinel_size_t len;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_DATA_S,
|
|
&ptr,
|
|
&len
|
|
);
|
|
|
|
if ((parsedLength > 0) && (len == sizeof(spinel_net_xpanid_t)))
|
|
{
|
|
otThreadSetExtendedPanId(mInstance, ptr);
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_NET_MASTER_KEY(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
const uint8_t *ptr = NULL;
|
|
spinel_size_t len;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_DATA_S,
|
|
&ptr,
|
|
&len
|
|
);
|
|
|
|
if ((parsedLength > 0) && (len < 100))
|
|
{
|
|
errorCode = otThreadSetMasterKey(mInstance, ptr, static_cast<uint8_t>(len));
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_NET_KEY_SEQUENCE_COUNTER(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
unsigned int i(0);
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT32_S,
|
|
&i
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otThreadSetKeySequenceCounter(mInstance, i);
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_NET_KEY_SWITCH_GUARDTIME(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
unsigned int i(0);
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT32_S,
|
|
&i
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otThreadSetKeySwitchGuardTime(mInstance, i);
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint8_t value = 0;
|
|
spinel_ssize_t parsedLength;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&value
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otThreadSetLocalLeaderWeight(mInstance, value);
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_Parse;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_STREAM_NET_INSECURE(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
const uint8_t *frame_ptr(NULL);
|
|
unsigned int frame_len(0);
|
|
const uint8_t *meta_ptr(NULL);
|
|
unsigned int meta_len(0);
|
|
|
|
// STREAM_NET_INSECURE packets are not secured at layer 2.
|
|
otMessage *message = otIp6NewMessage(mInstance, false);
|
|
|
|
if (message == NULL)
|
|
{
|
|
errorCode = kThreadError_NoBufs;
|
|
}
|
|
else
|
|
{
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_S,
|
|
&frame_ptr,
|
|
&frame_len,
|
|
&meta_ptr,
|
|
&meta_len
|
|
);
|
|
|
|
// We ignore metadata for now.
|
|
// May later include TX power, allow retransmits, etc...
|
|
(void)meta_ptr;
|
|
(void)meta_len;
|
|
(void)parsedLength;
|
|
|
|
errorCode = otMessageAppend(message, frame_ptr, static_cast<uint16_t>(frame_len));
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
// Ensure the insecure message is forwarded using direct transmission.
|
|
otMessageSetDirectTransmission(message, true);
|
|
|
|
errorCode = otIp6Send(mInstance, message);
|
|
}
|
|
else if (message)
|
|
{
|
|
otMessageFree(message);
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
mInboundInsecureIpFrameCounter++;
|
|
|
|
if (SPINEL_HEADER_GET_TID(header) != 0)
|
|
{
|
|
// Only send a successful status update if
|
|
// there was a transaction id in the header.
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_OK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mDroppedInboundIpFrameCounter++;
|
|
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
(void)key;
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_STREAM_NET(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
const uint8_t *frame_ptr(NULL);
|
|
unsigned int frame_len(0);
|
|
const uint8_t *meta_ptr(NULL);
|
|
unsigned int meta_len(0);
|
|
|
|
// STREAM_NET requires layer 2 security.
|
|
otMessage *message = otIp6NewMessage(mInstance, true);
|
|
|
|
if (message == NULL)
|
|
{
|
|
errorCode = kThreadError_NoBufs;
|
|
}
|
|
else
|
|
{
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_S,
|
|
&frame_ptr,
|
|
&frame_len,
|
|
&meta_ptr,
|
|
&meta_len
|
|
);
|
|
|
|
// We ignore metadata for now.
|
|
// May later include TX power, allow retransmits, etc...
|
|
(void)meta_ptr;
|
|
(void)meta_len;
|
|
(void)parsedLength;
|
|
|
|
errorCode = otMessageAppend(message, frame_ptr, static_cast<uint16_t>(frame_len));
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = otIp6Send(mInstance, message);
|
|
}
|
|
else if (message)
|
|
{
|
|
otMessageFree(message);
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
mInboundSecureIpFrameCounter++;
|
|
|
|
if (SPINEL_HEADER_GET_TID(header) != 0)
|
|
{
|
|
// Only send a successful status update if
|
|
// there was a transaction id in the header.
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_OK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mDroppedInboundIpFrameCounter++;
|
|
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
|
|
}
|
|
|
|
(void)key;
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_IPV6_ML_PREFIX(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
if (value_len >= 8)
|
|
{
|
|
errorCode = otThreadSetMeshLocalPrefix(mInstance, value_ptr);
|
|
HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_Parse;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_IPV6_ICMP_PING_OFFLOAD(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
bool isEnabled(false);
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
&isEnabled
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otIcmp6SetEchoEnabled(mInstance, isEnabled);
|
|
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
bool isEnabled(false);
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
&isEnabled
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
// Note reverse logic: passthru enabled = filter disabled
|
|
otIp6SetReceiveFilterEnabled(mInstance, !isEnabled);
|
|
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint8_t num_entries = 0;
|
|
const uint16_t *ports = otIp6GetUnsecurePorts(mInstance, &num_entries);
|
|
spinel_ssize_t parsedLength = 1;
|
|
int ports_changed = 0;
|
|
|
|
// First, we need to remove all of the current assisting ports.
|
|
for (; num_entries != 0; ports++, num_entries--)
|
|
{
|
|
errorCode = otIp6RemoveUnsecurePort(mInstance, *ports);
|
|
|
|
if (errorCode != kThreadError_None)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ports_changed++;
|
|
}
|
|
|
|
while ((errorCode == kThreadError_None)
|
|
&& (parsedLength > 0)
|
|
&& (value_len >= 2)
|
|
)
|
|
{
|
|
uint16_t port;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
"S",
|
|
&port
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otIp6AddUnsecurePort(mInstance, port);
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_Parse;
|
|
}
|
|
|
|
if (errorCode != kThreadError_None)
|
|
{
|
|
break;
|
|
}
|
|
|
|
value_ptr += parsedLength;
|
|
value_len -= parsedLength;
|
|
|
|
ports_changed++;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
|
|
if (ports_changed)
|
|
{
|
|
// We had an error, but we've actually changed
|
|
// the state of these ports---so we need to report
|
|
// those incomplete changes via an asynchronous
|
|
// change event.
|
|
HandleCommandPropertyGet(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, key);
|
|
}
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
bool value = false;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
bool should_register_with_leader = false;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
&value
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
// Register any net data changes on transition from `true` to `false`.
|
|
should_register_with_leader = (mAllowLocalNetworkDataChange == true) && (value == false);
|
|
|
|
mAllowLocalNetworkDataChange = value;
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_Parse;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
if (should_register_with_leader)
|
|
{
|
|
otNetDataRegister(mInstance);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
bool isEnabled;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
&isEnabled
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otThreadSetRouterRoleEnabled(mInstance, isEnabled);
|
|
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_CNTR_RESET(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint8_t value = 0;
|
|
spinel_ssize_t parsedLength;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&value
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
if (value == 1)
|
|
{
|
|
// TODO: Implement counter reset!
|
|
errorCode = kThreadError_NotImplemented;
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_InvalidArgs;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_Parse;
|
|
}
|
|
|
|
(void)key;
|
|
|
|
// There is currently no getter for PROP_CNTR_RESET, so we just
|
|
// return SPINEL_STATUS_OK for success when the counters are reset.
|
|
|
|
return SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
#if OPENTHREAD_ENABLE_COMMISSIONER
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_COMMISSIONER_ENABLED(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
bool value = false;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
(void)key;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
&value
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
if (value == false)
|
|
{
|
|
errorCode = otCommissionerStop(mInstance);
|
|
}
|
|
else
|
|
{
|
|
errorCode = otCommissionerStart(mInstance);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_Parse;
|
|
}
|
|
|
|
return SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
#endif // OPENTHREAD_ENABLE_COMMISSIONER
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_MAC_WHITELIST(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
spinel_ssize_t parsedLength = 1;
|
|
|
|
// First, clear the whitelist.
|
|
otLinkClearWhitelist(mInstance);
|
|
|
|
while ((errorCode == kThreadError_None)
|
|
&& (parsedLength > 0)
|
|
&& (value_len > 0)
|
|
)
|
|
{
|
|
otExtAddress *ext_addr = NULL;
|
|
int8_t rssi = RSSI_OVERRIDE_DISABLED;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_STRUCT_S(
|
|
SPINEL_DATATYPE_EUI64_S
|
|
SPINEL_DATATYPE_INT8_S
|
|
),
|
|
&ext_addr,
|
|
&rssi
|
|
);
|
|
|
|
if (parsedLength <= 0)
|
|
{
|
|
rssi = RSSI_OVERRIDE_DISABLED;
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_STRUCT_S(
|
|
SPINEL_DATATYPE_EUI64_S
|
|
),
|
|
&ext_addr
|
|
);
|
|
}
|
|
|
|
if (parsedLength <= 0)
|
|
{
|
|
errorCode = kThreadError_Parse;
|
|
break;
|
|
}
|
|
|
|
if (rssi == RSSI_OVERRIDE_DISABLED)
|
|
{
|
|
errorCode = otLinkAddWhitelist(mInstance, ext_addr->m8);
|
|
}
|
|
else
|
|
{
|
|
errorCode = otLinkAddWhitelistRssi(mInstance, ext_addr->m8, rssi);
|
|
}
|
|
|
|
value_ptr += parsedLength;
|
|
value_len -= parsedLength;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
|
|
// We had an error, but we may have actually changed
|
|
// the state of the whitelist---so we need to report
|
|
// those incomplete changes via an asynchronous
|
|
// change event.
|
|
HandleCommandPropertyGet(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, key);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_MAC_WHITELIST_ENABLED(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
bool isEnabled;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
&isEnabled
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otLinkSetWhitelistEnabled(mInstance, isEnabled);
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_MAC_SRC_MATCH_ENABLED(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
bool isEnabled;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
&isEnabled
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otLinkRawSrcMatchEnable(mInstance, isEnabled);
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
spinel_status_t errorStatus = SPINEL_STATUS_OK;
|
|
const uint8_t *data = value_ptr;
|
|
uint16_t data_len = value_len;
|
|
|
|
// Clear the list first
|
|
errorCode = otLinkRawSrcMatchClearShortEntries(mInstance);
|
|
|
|
VerifyOrExit(errorCode == kThreadError_None,
|
|
errorStatus = ThreadErrorToSpinelStatus(errorCode));
|
|
|
|
// Loop through the addresses and add them
|
|
while (data_len >= sizeof(uint16_t))
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
uint16_t short_address;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
data,
|
|
data_len,
|
|
SPINEL_DATATYPE_UINT16_S,
|
|
&short_address
|
|
);
|
|
|
|
VerifyOrExit(parsedLength > 0, errorStatus = SPINEL_STATUS_PARSE_ERROR);
|
|
|
|
data += parsedLength;
|
|
data_len -= (uint16_t)parsedLength;
|
|
|
|
errorCode = otLinkRawSrcMatchAddShortEntry(mInstance, short_address);
|
|
|
|
VerifyOrExit(errorCode == kThreadError_None,
|
|
errorStatus = ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
errorCode =
|
|
SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
|
|
exit:
|
|
|
|
if (errorStatus != SPINEL_STATUS_OK)
|
|
{
|
|
errorCode = SendLastStatus(header, errorStatus);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
spinel_status_t errorStatus = SPINEL_STATUS_OK;
|
|
const uint8_t *data = value_ptr;
|
|
uint16_t data_len = value_len;
|
|
|
|
// Clear the list first
|
|
errorCode = otLinkRawSrcMatchClearExtEntries(mInstance);
|
|
|
|
VerifyOrExit(errorCode == kThreadError_None,
|
|
errorStatus = ThreadErrorToSpinelStatus(errorCode));
|
|
|
|
// Loop through the addresses and add them
|
|
while (data_len >= sizeof(otExtAddress))
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
uint8_t *ext_address;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
data,
|
|
data_len,
|
|
SPINEL_DATATYPE_EUI64_S,
|
|
&ext_address
|
|
);
|
|
|
|
VerifyOrExit(parsedLength > 0, errorStatus = SPINEL_STATUS_PARSE_ERROR);
|
|
|
|
data += parsedLength;
|
|
data_len -= (uint16_t)parsedLength;
|
|
|
|
errorCode = otLinkRawSrcMatchAddExtEntry(mInstance, ext_address);
|
|
|
|
VerifyOrExit(errorCode == kThreadError_None,
|
|
errorStatus = ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
|
|
errorCode =
|
|
SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
|
|
exit:
|
|
|
|
if (errorStatus != SPINEL_STATUS_OK)
|
|
{
|
|
errorCode = SendLastStatus(header, errorStatus);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
#endif
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_MODE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
uint8_t numeric_mode = 0;
|
|
otLinkModeConfig mode_config;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&numeric_mode
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
mode_config.mRxOnWhenIdle = ((numeric_mode & kThreadMode_RxOnWhenIdle) == kThreadMode_RxOnWhenIdle);
|
|
mode_config.mSecureDataRequests =
|
|
((numeric_mode & kThreadMode_SecureDataRequest) == kThreadMode_SecureDataRequest);
|
|
mode_config.mDeviceType = ((numeric_mode & kThreadMode_FullFunctionDevice) == kThreadMode_FullFunctionDevice);
|
|
mode_config.mNetworkData = ((numeric_mode & kThreadMode_FullNetworkData) == kThreadMode_FullNetworkData);
|
|
|
|
errorCode = otThreadSetLinkMode(mInstance, mode_config);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_CHILD_COUNT_MAX(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
uint8_t n = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&n
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otThreadSetMaxAllowedChildren(mInstance, n);
|
|
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_CHILD_TIMEOUT(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
uint32_t i = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT32_S,
|
|
&i
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otThreadSetChildTimeout(mInstance, i);
|
|
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
uint8_t i = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&i
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otThreadSetRouterUpgradeThreshold(mInstance, i);
|
|
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
uint8_t i = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&i
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otThreadSetRouterDowngradeThreshold(mInstance, i);
|
|
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
uint8_t i = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&i
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otThreadSetRouterSelectionJitter(mInstance, i);
|
|
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_PREFERRED_ROUTER_ID(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
uint8_t router_id = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&router_id
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otThreadSetPreferredRouterId(mInstance, router_id);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
router_id
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_DEBUG_NCP_LOG_LEVEL(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
uint8_t spinelNcpLogLevel = 0;
|
|
otLogLevel logLevel;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&spinelNcpLogLevel
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
switch (spinelNcpLogLevel)
|
|
{
|
|
case SPINEL_NCP_LOG_LEVEL_EMERG:
|
|
case SPINEL_NCP_LOG_LEVEL_ALERT:
|
|
logLevel = kLogLevelNone;
|
|
break;
|
|
|
|
case SPINEL_NCP_LOG_LEVEL_CRIT:
|
|
logLevel = kLogLevelCrit;
|
|
break;
|
|
|
|
case SPINEL_NCP_LOG_LEVEL_ERR:
|
|
case SPINEL_NCP_LOG_LEVEL_WARN:
|
|
logLevel = kLogLevelWarn;
|
|
break;
|
|
|
|
case SPINEL_NCP_LOG_LEVEL_NOTICE:
|
|
case SPINEL_NCP_LOG_LEVEL_INFO:
|
|
logLevel = kLogLevelInfo;
|
|
break;
|
|
|
|
case SPINEL_NCP_LOG_LEVEL_DEBUG:
|
|
logLevel = kLogLevelDebg;
|
|
break;
|
|
|
|
default:
|
|
errorCode = kThreadError_InvalidArgs;
|
|
break;
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = otSetDynamicLogLevel(mInstance, logLevel);
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
if (errorCode == kThreadError_NotCapable)
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_COMMAND_FOR_PROP);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
uint32_t i = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT32_S,
|
|
&i
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otThreadSetContextIdReuseDelay(mInstance, i);
|
|
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
uint8_t i = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&i
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otThreadSetNetworkIdTimeout(mInstance, i);
|
|
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
#if OPENTHREAD_ENABLE_JAM_DETECTION
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_JAM_DETECT_ENABLE(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
bool isEnabled;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
&isEnabled
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
if (isEnabled)
|
|
{
|
|
otJamDetectionStart(mInstance, &NcpBase::HandleJamStateChange_Jump, this);
|
|
}
|
|
else
|
|
{
|
|
otJamDetectionStop(mInstance);
|
|
}
|
|
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
int8_t value = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_INT8_S,
|
|
&value
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otJamDetectionSetRssiThreshold(mInstance, value);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_JAM_DETECT_WINDOW(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
uint8_t value = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&value
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otJamDetectionSetWindow(mInstance, value);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::SetPropertyHandler_JAM_DETECT_BUSY(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
uint8_t value = 0;
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&value
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otJamDetectionSetBusyPeriod(mInstance, value);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
void NcpBase::HandleJamStateChange_Jump(bool aJamState, void *aContext)
|
|
{
|
|
static_cast<NcpBase *>(aContext)->HandleJamStateChange(aJamState);
|
|
}
|
|
|
|
void NcpBase::HandleJamStateChange(bool aJamState)
|
|
{
|
|
ThreadError errorCode;
|
|
|
|
errorCode = SendPropertyUpdate(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
SPINEL_PROP_JAM_DETECTED,
|
|
SPINEL_DATATYPE_BOOL_S,
|
|
aJamState
|
|
);
|
|
|
|
// If we could not send the jam state change indicator (no
|
|
// buffer space), we set `mShouldSignalJamStateChange` to true to send
|
|
// it out when buffer space becomes available.
|
|
if (errorCode != kThreadError_None)
|
|
{
|
|
mShouldSignalJamStateChange = true;
|
|
}
|
|
}
|
|
|
|
#endif // OPENTHREAD_ENABLE_JAM_DETECTION
|
|
|
|
#if OPENTHREAD_ENABLE_DIAG
|
|
ThreadError NcpBase::SetPropertyHandler_NEST_STREAM_MFG(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,
|
|
uint16_t value_len)
|
|
{
|
|
char *string(NULL);
|
|
char *output(NULL);
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UTF8_S,
|
|
&string
|
|
);
|
|
|
|
if ((parsedLength > 0) && (string != NULL))
|
|
{
|
|
// all diagnostics related features are processed within diagnostics module
|
|
output = otDiagProcessCmdLine(string);
|
|
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
key,
|
|
reinterpret_cast<uint8_t *>(output),
|
|
static_cast<uint16_t>(strlen(output) + 1)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
#endif
|
|
|
|
#if OPENTHREAD_ENABLE_LEGACY
|
|
ThreadError NcpBase::SetPropertyHandler_NEST_LEGACY_ULA_PREFIX(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
const uint8_t *ptr = NULL;
|
|
spinel_size_t len;
|
|
spinel_ssize_t parsedLength;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_DATA_S,
|
|
&ptr,
|
|
&len
|
|
);
|
|
|
|
if ((parsedLength > 0) && (len <= sizeof(mLegacyUlaPrefix)))
|
|
{
|
|
memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix));
|
|
memcpy(mLegacyUlaPrefix, ptr, len);
|
|
|
|
if (mLegacyHandlers)
|
|
{
|
|
if (mLegacyHandlers->mSetLegacyUlaPrefix)
|
|
{
|
|
mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix);
|
|
}
|
|
}
|
|
|
|
errorCode = HandleCommandPropertyGet(header, key);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
#endif // OPENTHREAD_ENABLE_LEGACY
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Individual Property Inserters
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
|
|
ThreadError NcpBase::InsertPropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
spinel_status_t errorStatus = SPINEL_STATUS_OK;
|
|
uint16_t short_address;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT16_S,
|
|
&short_address
|
|
);
|
|
|
|
VerifyOrExit(parsedLength > 0, errorStatus = SPINEL_STATUS_PARSE_ERROR);
|
|
|
|
errorCode = otLinkRawSrcMatchAddShortEntry(mInstance, short_address);
|
|
|
|
VerifyOrExit(errorCode == kThreadError_None,
|
|
errorStatus = ThreadErrorToSpinelStatus(errorCode));
|
|
|
|
errorCode =
|
|
SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_INSERTED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
|
|
exit:
|
|
|
|
if (errorStatus != SPINEL_STATUS_OK)
|
|
{
|
|
errorCode = SendLastStatus(header, errorStatus);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::InsertPropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
spinel_status_t errorStatus = SPINEL_STATUS_OK;
|
|
uint8_t *ext_address = NULL;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_EUI64_S,
|
|
&ext_address
|
|
);
|
|
|
|
VerifyOrExit(parsedLength > 0, errorStatus = SPINEL_STATUS_PARSE_ERROR);
|
|
|
|
errorCode = otLinkRawSrcMatchAddExtEntry(mInstance, ext_address);
|
|
|
|
VerifyOrExit(errorCode == kThreadError_None,
|
|
errorStatus = ThreadErrorToSpinelStatus(errorCode));
|
|
|
|
errorCode =
|
|
SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_INSERTED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
|
|
exit:
|
|
|
|
if (errorStatus != SPINEL_STATUS_OK)
|
|
{
|
|
errorCode = SendLastStatus(header, errorStatus);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
#endif
|
|
|
|
ThreadError NcpBase::InsertPropertyHandler_IPV6_ADDRESS_TABLE(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
spinel_status_t errorStatus = SPINEL_STATUS_OK;
|
|
otNetifAddress netif_addr;
|
|
otIp6Address *addr_ptr;
|
|
uint32_t preferred_lifetime;
|
|
uint32_t valid_lifetime;
|
|
uint8_t prefix_len;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
"6CLL",
|
|
&addr_ptr,
|
|
&prefix_len,
|
|
&preferred_lifetime,
|
|
&valid_lifetime
|
|
);
|
|
|
|
VerifyOrExit(parsedLength > 0, errorStatus = SPINEL_STATUS_PARSE_ERROR);
|
|
|
|
netif_addr.mAddress = *addr_ptr;
|
|
netif_addr.mPrefixLength = prefix_len;
|
|
netif_addr.mPreferred = preferred_lifetime != 0;
|
|
netif_addr.mValid = valid_lifetime != 0;
|
|
|
|
errorCode = otIp6AddUnicastAddress(mInstance, &netif_addr);
|
|
|
|
VerifyOrExit(errorCode == kThreadError_None,
|
|
errorStatus = ThreadErrorToSpinelStatus(errorCode));
|
|
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_INSERTED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
|
|
errorStatus = SPINEL_STATUS_OK;
|
|
|
|
exit:
|
|
|
|
if (errorStatus != SPINEL_STATUS_OK)
|
|
{
|
|
errorCode = SendLastStatus(header, errorStatus);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::InsertPropertyHandler_THREAD_LOCAL_ROUTES(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
const static int kPreferenceOffset = 6;
|
|
const static int kPreferenceMask = 3 << kPreferenceOffset;
|
|
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
otExternalRouteConfig ext_route_config;
|
|
otIp6Address *addr_ptr;
|
|
bool stable = false;
|
|
uint8_t flags = 0;
|
|
|
|
memset(&ext_route_config, 0, sizeof(otExternalRouteConfig));
|
|
|
|
VerifyOrExit(
|
|
mAllowLocalNetworkDataChange == true,
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE)
|
|
);
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
"6CbC",
|
|
&addr_ptr,
|
|
&ext_route_config.mPrefix.mLength,
|
|
&stable,
|
|
&flags
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
ext_route_config.mPrefix.mPrefix = *addr_ptr;
|
|
ext_route_config.mStable = stable;
|
|
ext_route_config.mPreference = ((flags & kPreferenceMask) >> kPreferenceOffset);
|
|
errorCode = otNetDataAddRoute(mInstance, &ext_route_config);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_INSERTED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::InsertPropertyHandler_THREAD_ON_MESH_NETS(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
const static int kPreferenceOffset = 6;
|
|
const static int kPreferenceMask = 3 << kPreferenceOffset;
|
|
const static int kPreferredFlag = 1 << 5;
|
|
const static int kSlaacFlag = 1 << 4;
|
|
const static int kDhcpFlag = 1 << 3;
|
|
const static int kConfigureFlag = 1 << 2;
|
|
const static int kDefaultRouteFlag = 1 << 1;
|
|
const static int kOnMeshFlag = 1 << 0;
|
|
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
otBorderRouterConfig border_router_config;
|
|
otIp6Address *addr_ptr;
|
|
bool stable = false;
|
|
uint8_t flags = 0;
|
|
|
|
memset(&border_router_config, 0, sizeof(otBorderRouterConfig));
|
|
|
|
VerifyOrExit(
|
|
mAllowLocalNetworkDataChange == true,
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE)
|
|
);
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
"6CbC",
|
|
&addr_ptr,
|
|
&border_router_config.mPrefix.mLength,
|
|
&stable,
|
|
&flags
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
border_router_config.mPrefix.mPrefix = *addr_ptr;
|
|
border_router_config.mStable = stable;
|
|
border_router_config.mPreference = ((flags & kPreferenceMask) >> kPreferenceOffset);
|
|
border_router_config.mPreferred = ((flags & kPreferredFlag) == kPreferredFlag);
|
|
border_router_config.mSlaac = ((flags & kSlaacFlag) == kSlaacFlag);
|
|
border_router_config.mDhcp = ((flags & kDhcpFlag) == kDhcpFlag);
|
|
border_router_config.mConfigure = ((flags & kConfigureFlag) == kConfigureFlag);
|
|
border_router_config.mDefaultRoute = ((flags & kDefaultRouteFlag) == kDefaultRouteFlag);
|
|
border_router_config.mOnMesh = ((flags & kOnMeshFlag) == kOnMeshFlag);
|
|
|
|
errorCode = otNetDataAddPrefixInfo(mInstance, &border_router_config);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_INSERTED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::InsertPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint16_t port;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT16_S,
|
|
&port
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otIp6AddUnsecurePort(mInstance, port);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_INSERTED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::InsertPropertyHandler_MAC_WHITELIST(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
spinel_ssize_t parsedLength;
|
|
otExtAddress *ext_addr = NULL;
|
|
int8_t rssi = RSSI_OVERRIDE_DISABLED;
|
|
|
|
|
|
if (value_len > static_cast<spinel_ssize_t>(sizeof(ext_addr)))
|
|
{
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_INT8_S,
|
|
&ext_addr,
|
|
&rssi
|
|
);
|
|
}
|
|
else
|
|
{
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_EUI64_S,
|
|
&ext_addr
|
|
);
|
|
}
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
if (rssi == RSSI_OVERRIDE_DISABLED)
|
|
{
|
|
errorCode = otLinkAddWhitelist(mInstance, ext_addr->m8);
|
|
}
|
|
else
|
|
{
|
|
errorCode = otLinkAddWhitelistRssi(mInstance, ext_addr->m8, rssi);
|
|
}
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_INSERTED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
#if OPENTHREAD_ENABLE_COMMISSIONER
|
|
ThreadError NcpBase::InsertPropertyHandler_THREAD_JOINERS(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
otExtAddress *ot_ext_address = NULL;
|
|
const char *aPSKd = NULL;
|
|
uint32_t joiner_timeout = 0;
|
|
|
|
VerifyOrExit(
|
|
mAllowLocalNetworkDataChange == true,
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE)
|
|
);
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
"ULE",
|
|
&aPSKd,
|
|
&joiner_timeout,
|
|
&ot_ext_address
|
|
);
|
|
|
|
if (parsedLength <= 0)
|
|
{
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
"UL",
|
|
&aPSKd,
|
|
&joiner_timeout
|
|
);
|
|
ot_ext_address = NULL;
|
|
}
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otCommissionerAddJoiner(mInstance, ot_ext_address, aPSKd, joiner_timeout);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_INSERTED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
#endif // OPENTHREAD_ENABLE_COMMISSIONER
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: Individual Property Removers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
|
|
|
ThreadError NcpBase::RemovePropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint16_t short_address;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT16_S,
|
|
&short_address
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otLinkRawSrcMatchClearShortEntry(mInstance, short_address);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_REMOVED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::RemovePropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint8_t *ext_address;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_EUI64_S,
|
|
&ext_address
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otLinkRawSrcMatchClearExtEntry(mInstance, ext_address);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_REMOVED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
#endif
|
|
|
|
ThreadError NcpBase::RemovePropertyHandler_IPV6_ADDRESS_TABLE(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
otIp6Address *addr_ptr;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
"6",
|
|
&addr_ptr
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otIp6RemoveUnicastAddress(mInstance, addr_ptr);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_REMOVED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::RemovePropertyHandler_THREAD_LOCAL_ROUTES(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
otIp6Prefix ip6_prefix;
|
|
memset(&ip6_prefix, 0, sizeof(otIp6Prefix));
|
|
otIp6Address *addr_ptr;
|
|
|
|
VerifyOrExit(
|
|
mAllowLocalNetworkDataChange == true,
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE)
|
|
);
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
"6C",
|
|
&addr_ptr,
|
|
&ip6_prefix.mLength
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
ip6_prefix.mPrefix = *addr_ptr;
|
|
errorCode = otNetDataRemoveRoute(mInstance, &ip6_prefix);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_REMOVED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::RemovePropertyHandler_THREAD_ON_MESH_NETS(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
otIp6Prefix ip6_prefix;
|
|
memset(&ip6_prefix, 0, sizeof(otIp6Prefix));
|
|
otIp6Address *addr_ptr;
|
|
|
|
VerifyOrExit(
|
|
mAllowLocalNetworkDataChange == true,
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE)
|
|
);
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
"6C",
|
|
&addr_ptr,
|
|
&ip6_prefix.mLength
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
ip6_prefix.mPrefix = *addr_ptr;
|
|
errorCode = otNetDataRemovePrefixInfo(mInstance, &ip6_prefix);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_REMOVED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
exit:
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::RemovePropertyHandler_THREAD_ASSISTING_PORTS(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint16_t port;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT16_S,
|
|
&port
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otIp6RemoveUnsecurePort(mInstance, port);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_REMOVED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::RemovePropertyHandler_THREAD_ACTIVE_ROUTER_IDS(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
spinel_ssize_t parsedLength;
|
|
ThreadError errorCode = kThreadError_None;
|
|
uint8_t router_id;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_UINT8_S,
|
|
&router_id
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
errorCode = otThreadReleaseRouterId(mInstance, router_id);
|
|
|
|
if (errorCode == kThreadError_None)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
header,
|
|
SPINEL_CMD_PROP_VALUE_REMOVED,
|
|
key,
|
|
value_ptr,
|
|
value_len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
ThreadError NcpBase::RemovePropertyHandler_MAC_WHITELIST(uint8_t header, spinel_prop_key_t key,
|
|
const uint8_t *value_ptr, uint16_t value_len)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
spinel_ssize_t parsedLength;
|
|
otExtAddress ext_addr;
|
|
|
|
parsedLength = spinel_datatype_unpack(
|
|
value_ptr,
|
|
value_len,
|
|
SPINEL_DATATYPE_EUI64_S,
|
|
&ext_addr
|
|
);
|
|
|
|
if (parsedLength > 0)
|
|
{
|
|
otLinkRemoveWhitelist(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 = (otThreadGetDeviceRole(mInstance) != kDeviceRoleDisabled);
|
|
|
|
if (isEnabled)
|
|
{
|
|
if (mLegacyHandlers->mStartLegacy)
|
|
{
|
|
mLegacyHandlers->mStartLegacy();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mLegacyHandlers->mStopLegacy)
|
|
{
|
|
mLegacyHandlers->mStopLegacy();
|
|
}
|
|
}
|
|
|
|
if (mLegacyHandlers->mSetLegacyUlaPrefix)
|
|
{
|
|
mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix);
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void NcpBase::HandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix)
|
|
{
|
|
memcpy(mLegacyUlaPrefix, aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH);
|
|
|
|
SuccessOrExit(OutboundFrameBegin());
|
|
|
|
SuccessOrExit(
|
|
OutboundFrameFeedPacked(
|
|
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_DATA_S,
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
SPINEL_PROP_NEST_LEGACY_ULA_PREFIX,
|
|
aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH
|
|
));
|
|
|
|
OutboundFrameSend();
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void NcpBase::HandleLegacyNodeDidJoin(const otExtAddress *aExtAddr)
|
|
{
|
|
mLegacyNodeDidJoin = true;
|
|
|
|
SuccessOrExit(OutboundFrameBegin());
|
|
|
|
SuccessOrExit(
|
|
OutboundFrameFeedPacked(
|
|
SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_EUI64_S,
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
SPINEL_PROP_NEST_LEGACY_JOINED_NODE,
|
|
aExtAddr->m8
|
|
));
|
|
|
|
OutboundFrameSend();
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
#endif // OPENTHREAD_ENABLE_LEGACY
|
|
|
|
ThreadError NcpBase::StreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen)
|
|
{
|
|
ThreadError errorCode = kThreadError_None;
|
|
|
|
if (aStreamId == 0)
|
|
{
|
|
aStreamId = SPINEL_PROP_STREAM_DEBUG;
|
|
}
|
|
|
|
if (!mDisableStreamWrite)
|
|
{
|
|
errorCode = SendPropertyUpdate(
|
|
SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
|
|
SPINEL_CMD_PROP_VALUE_IS,
|
|
static_cast<spinel_prop_key_t>(aStreamId),
|
|
aDataPtr,
|
|
static_cast<uint16_t>(aDataLen)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
errorCode = kThreadError_InvalidState;
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
} // namespace 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
|
|
}
|