| /* |
| * Copyright (c) 2016, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @file |
| * This file implements a Spinel interface to the OpenThread stack. |
| */ |
| |
| #ifdef OPENTHREAD_CONFIG_FILE |
| #include OPENTHREAD_CONFIG_FILE |
| #else |
| #include <openthread-config.h> |
| #endif |
| |
| #include <assert.h> |
| #include <stdlib.h> |
| #include <common/code_utils.hpp> |
| #include <ncp/ncp.h> |
| #include <ncp/ncp_base.hpp> |
| #include <net/ip6.hpp> |
| #include <openthread.h> |
| #include <openthread-diag.h> |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| #include <openthread-jam-detection.h> |
| #endif |
| #include <openthread-instance.h> |
| #include <stdarg.h> |
| #include <platform/radio.h> |
| #include <platform/misc.h> |
| |
| namespace Thread |
| { |
| |
| static NcpBase *sNcpContext = NULL; |
| |
| #define NCP_PLAT_RESET_REASON (1U<<31) |
| |
| enum |
| { |
| kThreadMode_RxOnWhenIdle = (1 << 3), |
| kThreadMode_SecureDataRequest = (1 << 2), |
| kThreadMode_FullFunctionDevice = (1 << 1), |
| kThreadMode_FullNetworkData = (1 << 0), |
| }; |
| |
| #define RSSI_OVERRIDE_DISABLED 127 // Used for PROP_MAC_WHITELIST |
| |
| #define IGNORE_RETURN_VALUE(s) do { if (s){} } while (0) |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Command/Property Jump Tables |
| // ---------------------------------------------------------------------------- |
| |
| const NcpBase::CommandHandlerEntry NcpBase::mCommandHandlerTable[] = |
| { |
| { SPINEL_CMD_NOOP, &NcpBase::CommandHandler_NOOP }, |
| { SPINEL_CMD_RESET, &NcpBase::CommandHandler_RESET }, |
| { SPINEL_CMD_PROP_VALUE_GET, &NcpBase::CommandHandler_PROP_VALUE_GET }, |
| { SPINEL_CMD_PROP_VALUE_SET, &NcpBase::CommandHandler_PROP_VALUE_SET }, |
| { SPINEL_CMD_PROP_VALUE_INSERT, &NcpBase::CommandHandler_PROP_VALUE_INSERT }, |
| { SPINEL_CMD_PROP_VALUE_REMOVE, &NcpBase::CommandHandler_PROP_VALUE_REMOVE }, |
| { SPINEL_CMD_NET_SAVE, &NcpBase::CommandHandler_NET_SAVE }, |
| { SPINEL_CMD_NET_CLEAR, &NcpBase::CommandHandler_NET_CLEAR }, |
| { SPINEL_CMD_NET_RECALL, &NcpBase::CommandHandler_NET_RECALL }, |
| }; |
| |
| const NcpBase::GetPropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] = |
| { |
| { SPINEL_PROP_LAST_STATUS, &NcpBase::GetPropertyHandler_LAST_STATUS }, |
| { SPINEL_PROP_PROTOCOL_VERSION, &NcpBase::GetPropertyHandler_PROTOCOL_VERSION }, |
| { SPINEL_PROP_INTERFACE_TYPE, &NcpBase::GetPropertyHandler_INTERFACE_TYPE }, |
| { SPINEL_PROP_VENDOR_ID, &NcpBase::GetPropertyHandler_VENDOR_ID }, |
| { SPINEL_PROP_CAPS, &NcpBase::GetPropertyHandler_CAPS }, |
| { SPINEL_PROP_NCP_VERSION, &NcpBase::GetPropertyHandler_NCP_VERSION }, |
| { SPINEL_PROP_INTERFACE_COUNT, &NcpBase::GetPropertyHandler_INTERFACE_COUNT }, |
| { SPINEL_PROP_POWER_STATE, &NcpBase::GetPropertyHandler_POWER_STATE }, |
| { SPINEL_PROP_HWADDR, &NcpBase::GetPropertyHandler_HWADDR }, |
| { SPINEL_PROP_LOCK, &NcpBase::GetPropertyHandler_LOCK }, |
| |
| { SPINEL_PROP_PHY_ENABLED, &NcpBase::GetPropertyHandler_PHY_ENABLED }, |
| { SPINEL_PROP_PHY_FREQ, &NcpBase::GetPropertyHandler_PHY_FREQ }, |
| { SPINEL_PROP_PHY_CHAN_SUPPORTED, &NcpBase::GetPropertyHandler_PHY_CHAN_SUPPORTED }, |
| { SPINEL_PROP_PHY_CHAN, &NcpBase::GetPropertyHandler_PHY_CHAN }, |
| { SPINEL_PROP_PHY_RSSI, &NcpBase::GetPropertyHandler_PHY_RSSI }, |
| |
| { SPINEL_PROP_MAC_SCAN_STATE, &NcpBase::GetPropertyHandler_MAC_SCAN_STATE }, |
| { SPINEL_PROP_MAC_SCAN_MASK, &NcpBase::GetPropertyHandler_MAC_SCAN_MASK }, |
| { SPINEL_PROP_MAC_SCAN_PERIOD, &NcpBase::GetPropertyHandler_MAC_SCAN_PERIOD }, |
| { SPINEL_PROP_MAC_15_4_PANID, &NcpBase::GetPropertyHandler_MAC_15_4_PANID }, |
| { SPINEL_PROP_MAC_15_4_LADDR, &NcpBase::GetPropertyHandler_MAC_15_4_LADDR }, |
| { SPINEL_PROP_MAC_15_4_SADDR, &NcpBase::GetPropertyHandler_MAC_15_4_SADDR }, |
| { SPINEL_PROP_MAC_RAW_STREAM_ENABLED, &NcpBase::GetPropertyHandler_MAC_RAW_STREAM_ENABLED }, |
| { SPINEL_PROP_MAC_PROMISCUOUS_MODE, &NcpBase::GetPropertyHandler_MAC_PROMISCUOUS_MODE }, |
| { SPINEL_PROP_MAC_EXTENDED_ADDR, &NcpBase::GetPropertyHandler_MAC_EXTENDED_ADDR }, |
| |
| { SPINEL_PROP_NET_IF_UP, &NcpBase::GetPropertyHandler_NET_IF_UP }, |
| { SPINEL_PROP_NET_STACK_UP, &NcpBase::GetPropertyHandler_NET_STACK_UP }, |
| { SPINEL_PROP_NET_ROLE, &NcpBase::GetPropertyHandler_NET_ROLE }, |
| { SPINEL_PROP_NET_NETWORK_NAME, &NcpBase::GetPropertyHandler_NET_NETWORK_NAME }, |
| { SPINEL_PROP_NET_XPANID, &NcpBase::GetPropertyHandler_NET_XPANID }, |
| { SPINEL_PROP_NET_MASTER_KEY, &NcpBase::GetPropertyHandler_NET_MASTER_KEY }, |
| { SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER, &NcpBase::GetPropertyHandler_NET_KEY_SEQUENCE_COUNTER }, |
| { SPINEL_PROP_NET_PARTITION_ID, &NcpBase::GetPropertyHandler_NET_PARTITION_ID }, |
| { SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME, &NcpBase::GetPropertyHandler_NET_KEY_SWITCH_GUARDTIME}, |
| |
| { SPINEL_PROP_THREAD_LEADER_ADDR, &NcpBase::GetPropertyHandler_THREAD_LEADER_ADDR }, |
| { SPINEL_PROP_THREAD_PARENT, &NcpBase::GetPropertyHandler_THREAD_PARENT }, |
| { SPINEL_PROP_THREAD_CHILD_TABLE, &NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE }, |
| { SPINEL_PROP_THREAD_NEIGHBOR_TABLE, &NcpBase::GetPropertyHandler_THREAD_NEIGHBOR_TABLE }, |
| { SPINEL_PROP_THREAD_LEADER_RID, &NcpBase::GetPropertyHandler_THREAD_LEADER_RID }, |
| { SPINEL_PROP_THREAD_LEADER_WEIGHT, &NcpBase::GetPropertyHandler_THREAD_LEADER_WEIGHT }, |
| { SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT, &NcpBase::GetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT }, |
| { SPINEL_PROP_THREAD_NETWORK_DATA, &NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA }, |
| { SPINEL_PROP_THREAD_NETWORK_DATA_VERSION, &NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA_VERSION }, |
| { SPINEL_PROP_THREAD_STABLE_NETWORK_DATA, &NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA }, |
| { SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION, &NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA_VERSION }, |
| { SPINEL_PROP_THREAD_LEADER_NETWORK_DATA, &NcpBase::GetPropertyHandler_THREAD_LEADER_NETWORK_DATA }, |
| { SPINEL_PROP_THREAD_STABLE_LEADER_NETWORK_DATA, &NcpBase::GetPropertyHandler_THREAD_STABLE_LEADER_NETWORK_DATA }, |
| { SPINEL_PROP_THREAD_LOCAL_ROUTES, &NcpBase::NcpBase::GetPropertyHandler_THREAD_LOCAL_ROUTES }, |
| { SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::NcpBase::GetPropertyHandler_THREAD_ASSISTING_PORTS }, |
| { SPINEL_PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE, &NcpBase::GetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE }, |
| { SPINEL_PROP_THREAD_ROUTER_ROLE_ENABLED, &NcpBase::GetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED }, |
| |
| { SPINEL_PROP_MAC_WHITELIST, &NcpBase::GetPropertyHandler_MAC_WHITELIST }, |
| { SPINEL_PROP_MAC_WHITELIST_ENABLED, &NcpBase::GetPropertyHandler_MAC_WHITELIST_ENABLED }, |
| { SPINEL_PROP_THREAD_MODE, &NcpBase::GetPropertyHandler_THREAD_MODE }, |
| { SPINEL_PROP_THREAD_CHILD_COUNT_MAX, &NcpBase::GetPropertyHandler_THREAD_CHILD_COUNT_MAX }, |
| { SPINEL_PROP_THREAD_CHILD_TIMEOUT, &NcpBase::GetPropertyHandler_THREAD_CHILD_TIMEOUT }, |
| { SPINEL_PROP_THREAD_RLOC16, &NcpBase::GetPropertyHandler_THREAD_RLOC16 }, |
| { SPINEL_PROP_THREAD_ROUTER_UPGRADE_THRESHOLD, &NcpBase::GetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD }, |
| { SPINEL_PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD, &NcpBase::GetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD }, |
| { SPINEL_PROP_THREAD_CONTEXT_REUSE_DELAY, &NcpBase::GetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY }, |
| { SPINEL_PROP_THREAD_NETWORK_ID_TIMEOUT, &NcpBase::GetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT }, |
| { SPINEL_PROP_THREAD_ON_MESH_NETS, &NcpBase::NcpBase::GetPropertyHandler_THREAD_ON_MESH_NETS }, |
| { SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING, &NcpBase::GetPropertyHandler_NET_REQUIRE_JOIN_EXISTING }, |
| { SPINEL_PROP_THREAD_ROUTER_SELECTION_JITTER, &NcpBase::GetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER }, |
| |
| { SPINEL_PROP_IPV6_ML_PREFIX, &NcpBase::GetPropertyHandler_IPV6_ML_PREFIX }, |
| { SPINEL_PROP_IPV6_ML_ADDR, &NcpBase::GetPropertyHandler_IPV6_ML_ADDR }, |
| { SPINEL_PROP_IPV6_LL_ADDR, &NcpBase::GetPropertyHandler_IPV6_LL_ADDR }, |
| { SPINEL_PROP_IPV6_ADDRESS_TABLE, &NcpBase::GetPropertyHandler_IPV6_ADDRESS_TABLE }, |
| { SPINEL_PROP_IPV6_ROUTE_TABLE, &NcpBase::GetPropertyHandler_IPV6_ROUTE_TABLE }, |
| { SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD, &NcpBase::GetPropertyHandler_IPV6_ICMP_PING_OFFLOAD }, |
| { SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU, &NcpBase::GetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU }, |
| |
| { SPINEL_PROP_STREAM_NET, &NcpBase::GetPropertyHandler_STREAM_NET }, |
| |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| { SPINEL_PROP_JAM_DETECT_ENABLE, &NcpBase::GetPropertyHandler_JAM_DETECT_ENABLE }, |
| { SPINEL_PROP_JAM_DETECTED, &NcpBase::GetPropertyHandler_JAM_DETECTED }, |
| { SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD, &NcpBase::GetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD }, |
| { SPINEL_PROP_JAM_DETECT_WINDOW, &NcpBase::GetPropertyHandler_JAM_DETECT_WINDOW }, |
| { SPINEL_PROP_JAM_DETECT_BUSY, &NcpBase::GetPropertyHandler_JAM_DETECT_BUSY }, |
| { SPINEL_PROP_JAM_DETECT_HISTORY_BITMAP, &NcpBase::GetPropertyHandler_JAM_DETECT_HISTORY_BITMAP }, |
| #endif |
| |
| { SPINEL_PROP_CNTR_TX_PKT_TOTAL, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_TX_PKT_ACK_REQ, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_TX_PKT_ACKED, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_TX_PKT_NO_ACK_REQ, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_TX_PKT_DATA, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_TX_PKT_DATA_POLL, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_TX_PKT_BEACON, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_TX_PKT_BEACON_REQ, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_TX_PKT_OTHER, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_TX_PKT_RETRY, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_TX_PKT_UNICAST, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_TX_PKT_BROADCAST, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_TX_ERR_CCA, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_PKT_TOTAL, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_PKT_DATA, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_PKT_DATA_POLL, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_PKT_BEACON, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_PKT_BEACON_REQ, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_PKT_OTHER, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_PKT_FILT_WL, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_PKT_FILT_DA, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_PKT_UNICAST, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_PKT_BROADCAST, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_ERR_EMPTY, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_ERR_UKWN_NBR, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_ERR_NVLD_SADDR, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_ERR_SECURITY, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_ERR_BAD_FCS, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_ERR_OTHER, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| { SPINEL_PROP_CNTR_RX_PKT_DUP, &NcpBase::GetPropertyHandler_MAC_CNTR }, |
| |
| { SPINEL_PROP_CNTR_TX_IP_SEC_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR }, |
| { SPINEL_PROP_CNTR_TX_IP_INSEC_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR }, |
| { SPINEL_PROP_CNTR_TX_IP_DROPPED, &NcpBase::GetPropertyHandler_NCP_CNTR }, |
| { SPINEL_PROP_CNTR_RX_IP_SEC_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR }, |
| { SPINEL_PROP_CNTR_RX_IP_INSEC_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR }, |
| { SPINEL_PROP_CNTR_RX_IP_DROPPED, &NcpBase::GetPropertyHandler_NCP_CNTR }, |
| { SPINEL_PROP_CNTR_TX_SPINEL_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR }, |
| { SPINEL_PROP_CNTR_RX_SPINEL_TOTAL, &NcpBase::GetPropertyHandler_NCP_CNTR }, |
| { SPINEL_PROP_CNTR_RX_SPINEL_ERR, &NcpBase::GetPropertyHandler_NCP_CNTR }, |
| |
| { SPINEL_PROP_MSG_BUFFER_COUNTERS, &NcpBase::GetPropertyHandler_MSG_BUFFER_COUNTERS }, |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| { SPINEL_PROP_NEST_LEGACY_ULA_PREFIX, &NcpBase::GetPropertyHandler_NEST_LEGACY_ULA_PREFIX }, |
| #endif |
| }; |
| |
| const NcpBase::SetPropertyHandlerEntry NcpBase::mSetPropertyHandlerTable[] = |
| { |
| { SPINEL_PROP_POWER_STATE, &NcpBase::SetPropertyHandler_POWER_STATE }, |
| |
| { SPINEL_PROP_PHY_ENABLED, &NcpBase::SetPropertyHandler_PHY_ENABLED }, |
| { SPINEL_PROP_PHY_TX_POWER, &NcpBase::SetPropertyHandler_PHY_TX_POWER }, |
| { SPINEL_PROP_PHY_CHAN, &NcpBase::SetPropertyHandler_PHY_CHAN }, |
| { SPINEL_PROP_MAC_PROMISCUOUS_MODE, &NcpBase::SetPropertyHandler_MAC_PROMISCUOUS_MODE }, |
| |
| { SPINEL_PROP_MAC_SCAN_MASK, &NcpBase::SetPropertyHandler_MAC_SCAN_MASK }, |
| { SPINEL_PROP_MAC_SCAN_STATE, &NcpBase::SetPropertyHandler_MAC_SCAN_STATE }, |
| { SPINEL_PROP_MAC_SCAN_PERIOD, &NcpBase::SetPropertyHandler_MAC_SCAN_PERIOD }, |
| { SPINEL_PROP_MAC_15_4_PANID, &NcpBase::SetPropertyHandler_MAC_15_4_PANID }, |
| { SPINEL_PROP_MAC_RAW_STREAM_ENABLED, &NcpBase::SetPropertyHandler_MAC_RAW_STREAM_ENABLED }, |
| |
| { SPINEL_PROP_NET_IF_UP, &NcpBase::SetPropertyHandler_NET_IF_UP }, |
| { SPINEL_PROP_NET_STACK_UP, &NcpBase::SetPropertyHandler_NET_STACK_UP }, |
| { SPINEL_PROP_NET_ROLE, &NcpBase::SetPropertyHandler_NET_ROLE }, |
| { SPINEL_PROP_NET_NETWORK_NAME, &NcpBase::SetPropertyHandler_NET_NETWORK_NAME }, |
| { SPINEL_PROP_NET_XPANID, &NcpBase::SetPropertyHandler_NET_XPANID }, |
| { SPINEL_PROP_NET_MASTER_KEY, &NcpBase::SetPropertyHandler_NET_MASTER_KEY }, |
| { SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER, &NcpBase::SetPropertyHandler_NET_KEY_SEQUENCE_COUNTER }, |
| { SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME, &NcpBase::SetPropertyHandler_NET_KEY_SWITCH_GUARDTIME}, |
| |
| { SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT, &NcpBase::SetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT }, |
| { SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::SetPropertyHandler_THREAD_ASSISTING_PORTS }, |
| { SPINEL_PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE, &NcpBase::SetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE }, |
| { SPINEL_PROP_THREAD_NETWORK_ID_TIMEOUT, &NcpBase::SetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT }, |
| { SPINEL_PROP_THREAD_ROUTER_ROLE_ENABLED, &NcpBase::SetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED }, |
| |
| { SPINEL_PROP_STREAM_NET_INSECURE, &NcpBase::SetPropertyHandler_STREAM_NET_INSECURE }, |
| { SPINEL_PROP_STREAM_NET, &NcpBase::SetPropertyHandler_STREAM_NET }, |
| |
| { SPINEL_PROP_IPV6_ML_PREFIX, &NcpBase::SetPropertyHandler_IPV6_ML_PREFIX }, |
| { SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD, &NcpBase::SetPropertyHandler_IPV6_ICMP_PING_OFFLOAD }, |
| { SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU, &NcpBase::SetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU }, |
| |
| { SPINEL_PROP_MAC_WHITELIST, &NcpBase::SetPropertyHandler_MAC_WHITELIST }, |
| { SPINEL_PROP_MAC_WHITELIST_ENABLED, &NcpBase::SetPropertyHandler_MAC_WHITELIST_ENABLED }, |
| { SPINEL_PROP_THREAD_MODE, &NcpBase::SetPropertyHandler_THREAD_MODE }, |
| { SPINEL_PROP_THREAD_CHILD_COUNT_MAX, &NcpBase::SetPropertyHandler_THREAD_CHILD_COUNT_MAX }, |
| { SPINEL_PROP_THREAD_CHILD_TIMEOUT, &NcpBase::SetPropertyHandler_THREAD_CHILD_TIMEOUT }, |
| { SPINEL_PROP_THREAD_ROUTER_UPGRADE_THRESHOLD, &NcpBase::SetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD }, |
| { SPINEL_PROP_THREAD_ROUTER_DOWNGRADE_THRESHOLD, &NcpBase::SetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD }, |
| { SPINEL_PROP_THREAD_CONTEXT_REUSE_DELAY, &NcpBase::SetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY }, |
| { SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING, &NcpBase::SetPropertyHandler_NET_REQUIRE_JOIN_EXISTING }, |
| { SPINEL_PROP_THREAD_ROUTER_SELECTION_JITTER, &NcpBase::SetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER }, |
| { SPINEL_PROP_THREAD_PREFERRED_ROUTER_ID, &NcpBase::SetPropertyHandler_THREAD_PREFERRED_ROUTER_ID }, |
| |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| { SPINEL_PROP_JAM_DETECT_ENABLE, &NcpBase::SetPropertyHandler_JAM_DETECT_ENABLE }, |
| { SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD, &NcpBase::SetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD }, |
| { SPINEL_PROP_JAM_DETECT_WINDOW, &NcpBase::SetPropertyHandler_JAM_DETECT_WINDOW }, |
| { SPINEL_PROP_JAM_DETECT_BUSY, &NcpBase::SetPropertyHandler_JAM_DETECT_BUSY }, |
| #endif |
| |
| #if OPENTHREAD_ENABLE_DIAG |
| { SPINEL_PROP_NEST_STREAM_MFG, &NcpBase::SetPropertyHandler_NEST_STREAM_MFG }, |
| #endif |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| { SPINEL_PROP_NEST_LEGACY_ULA_PREFIX, &NcpBase::SetPropertyHandler_NEST_LEGACY_ULA_PREFIX }, |
| #endif |
| }; |
| |
| const NcpBase::InsertPropertyHandlerEntry NcpBase::mInsertPropertyHandlerTable[] = |
| { |
| { SPINEL_PROP_IPV6_ADDRESS_TABLE, &NcpBase::NcpBase::InsertPropertyHandler_IPV6_ADDRESS_TABLE }, |
| { SPINEL_PROP_THREAD_LOCAL_ROUTES, &NcpBase::NcpBase::InsertPropertyHandler_THREAD_LOCAL_ROUTES }, |
| { SPINEL_PROP_THREAD_ON_MESH_NETS, &NcpBase::NcpBase::InsertPropertyHandler_THREAD_ON_MESH_NETS }, |
| { SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::NcpBase::InsertPropertyHandler_THREAD_ASSISTING_PORTS }, |
| |
| { SPINEL_PROP_CNTR_RESET, &NcpBase::SetPropertyHandler_CNTR_RESET }, |
| |
| { SPINEL_PROP_MAC_WHITELIST, &NcpBase::InsertPropertyHandler_MAC_WHITELIST }, |
| }; |
| |
| const NcpBase::RemovePropertyHandlerEntry NcpBase::mRemovePropertyHandlerTable[] = |
| { |
| { SPINEL_PROP_IPV6_ADDRESS_TABLE, &NcpBase::NcpBase::RemovePropertyHandler_IPV6_ADDRESS_TABLE }, |
| { SPINEL_PROP_THREAD_LOCAL_ROUTES, &NcpBase::NcpBase::RemovePropertyHandler_THREAD_LOCAL_ROUTES }, |
| { SPINEL_PROP_THREAD_ON_MESH_NETS, &NcpBase::NcpBase::RemovePropertyHandler_THREAD_ON_MESH_NETS }, |
| { SPINEL_PROP_THREAD_ASSISTING_PORTS, &NcpBase::NcpBase::RemovePropertyHandler_THREAD_ASSISTING_PORTS }, |
| { SPINEL_PROP_MAC_WHITELIST, &NcpBase::RemovePropertyHandler_MAC_WHITELIST }, |
| { SPINEL_PROP_THREAD_ACTIVE_ROUTER_IDS, &NcpBase::RemovePropertyHandler_THREAD_ACTIVE_ROUTER_IDS }, |
| }; |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Utility Functions |
| // ---------------------------------------------------------------------------- |
| |
| static spinel_status_t ThreadErrorToSpinelStatus(ThreadError error) |
| { |
| spinel_status_t ret; |
| |
| switch (error) |
| { |
| case kThreadError_None: |
| ret = SPINEL_STATUS_OK; |
| break; |
| |
| case kThreadError_Failed: |
| ret = SPINEL_STATUS_FAILURE; |
| break; |
| |
| case kThreadError_Drop: |
| ret = SPINEL_STATUS_DROPPED; |
| break; |
| |
| case kThreadError_NoBufs: |
| ret = SPINEL_STATUS_NOMEM; |
| break; |
| |
| case kThreadError_Busy: |
| ret = SPINEL_STATUS_BUSY; |
| break; |
| |
| case kThreadError_Parse: |
| ret = SPINEL_STATUS_PARSE_ERROR; |
| break; |
| |
| case kThreadError_InvalidArgs: |
| ret = SPINEL_STATUS_INVALID_ARGUMENT; |
| break; |
| |
| case kThreadError_NotImplemented: |
| ret = SPINEL_STATUS_UNIMPLEMENTED; |
| break; |
| |
| case kThreadError_InvalidState: |
| ret = SPINEL_STATUS_INVALID_STATE; |
| break; |
| |
| case kThreadError_NoAck: |
| ret = SPINEL_STATUS_NO_ACK; |
| break; |
| |
| case kThreadError_ChannelAccessFailure: |
| ret = SPINEL_STATUS_CCA_FAILURE; |
| break; |
| |
| case kThreadError_Already: |
| ret = SPINEL_STATUS_ALREADY; |
| break; |
| |
| case kThreadError_NotFound: |
| ret = SPINEL_STATUS_ITEM_NOT_FOUND; |
| break; |
| |
| |
| default: |
| // Unknown error code. Wrap it as a Spinel status and return that. |
| ret = static_cast<spinel_status_t>(SPINEL_STATUS_STACK_NATIVE__BEGIN + error); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static spinel_status_t ResetReasonToSpinelStatus(otPlatResetReason reason) |
| { |
| spinel_status_t ret; |
| switch (reason) |
| { |
| case kPlatResetReason_PowerOn: |
| ret = SPINEL_STATUS_RESET_POWER_ON; |
| break; |
| case kPlatResetReason_External: |
| ret = SPINEL_STATUS_RESET_EXTERNAL; |
| break; |
| case kPlatResetReason_Software: |
| ret = SPINEL_STATUS_RESET_SOFTWARE; |
| break; |
| case kPlatResetReason_Fault: |
| ret = SPINEL_STATUS_RESET_FAULT; |
| break; |
| case kPlatResetReason_Crash: |
| ret = SPINEL_STATUS_RESET_CRASH; |
| break; |
| case kPlatResetReason_Assert: |
| ret = SPINEL_STATUS_RESET_ASSERT; |
| break; |
| case kPlatResetReason_Watchdog: |
| ret = SPINEL_STATUS_RESET_WATCHDOG; |
| break; |
| case kPlatResetReason_Other: |
| ret = SPINEL_STATUS_RESET_OTHER; |
| break; |
| default: |
| ret = SPINEL_STATUS_RESET_UNKNOWN; |
| break; |
| } |
| return ret; |
| } |
| |
| static uint8_t BorderRouterConfigToFlagByte(const otBorderRouterConfig &config) |
| { |
| uint8_t flags(0); |
| |
| if (config.mPreferred) |
| { |
| flags |= SPINEL_NET_FLAG_PREFERRED; |
| } |
| |
| if (config.mSlaac) |
| { |
| flags |= SPINEL_NET_FLAG_SLAAC; |
| } |
| |
| if (config.mDhcp) |
| { |
| flags |= SPINEL_NET_FLAG_DHCP; |
| } |
| |
| if (config.mDefaultRoute) |
| { |
| flags |= SPINEL_NET_FLAG_DEFAULT_ROUTE; |
| } |
| |
| if (config.mConfigure) |
| { |
| flags |= SPINEL_NET_FLAG_CONFIGURE; |
| } |
| |
| if (config.mOnMesh) |
| { |
| flags |= SPINEL_NET_FLAG_ON_MESH; |
| } |
| |
| flags |= (config.mPreference << SPINEL_NET_FLAG_PREFERENCE_OFFSET); |
| |
| return flags; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Class Boilerplate |
| // ---------------------------------------------------------------------------- |
| |
| NcpBase::NcpBase(otInstance *aInstance): |
| mInstance(aInstance), |
| mLastStatus(SPINEL_STATUS_OK), |
| mSupportedChannelMask(kPhySupportedChannelMask), |
| mChannelMask(kPhySupportedChannelMask), |
| mScanPeriod(200), // ms |
| mUpdateChangedPropsTask(aInstance->mIp6.mTaskletScheduler, &NcpBase::UpdateChangedProps, this), |
| mChangedFlags(NCP_PLAT_RESET_REASON), |
| mShouldSignalEndOfScan(false), |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| mShouldSignalJamStateChange(false), |
| #endif |
| mDroppedReplyTid(0), |
| mDroppedReplyTidBitSet(0), |
| mNextExpectedTid(0), |
| mAllowLocalNetworkDataChange(false), |
| mRequireJoinExistingNetwork(false), |
| mIsRawStreamEnabled(false), |
| mDisableStreamWrite(false), |
| |
| mFramingErrorCounter(0), |
| mRxSpinelFrameCounter(0), |
| mRxSpinelOutOfOrderTidCounter(0), |
| mTxSpinelFrameCounter(0), |
| mInboundSecureIpFrameCounter(0), |
| mInboundInsecureIpFrameCounter(0), |
| mOutboundSecureIpFrameCounter(0), |
| mOutboundInsecureIpFrameCounter(0), |
| mDroppedOutboundIpFrameCounter(0), |
| mDroppedInboundIpFrameCounter(0) |
| { |
| assert(mInstance != NULL); |
| |
| sNcpContext = this; |
| |
| otSetStateChangedCallback(mInstance, &NcpBase::HandleNetifStateChanged, this); |
| otSetReceiveIp6DatagramCallback(mInstance, &NcpBase::HandleDatagramFromStack, this); |
| otSetLinkPcapCallback(mInstance, &NcpBase::HandleRawFrame, static_cast<void*>(this)); |
| otSetIcmpEchoEnabled(mInstance, false); |
| |
| mUpdateChangedPropsTask.Post(); |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| mLegacyNodeDidJoin = false; |
| mLegacyHandlers = NULL; |
| memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix)); |
| #endif |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Outbound Datagram Handling |
| // ---------------------------------------------------------------------------- |
| |
| void NcpBase::HandleDatagramFromStack(otMessage aMessage, void *aContext) |
| { |
| static_cast<NcpBase *>(aContext)->HandleDatagramFromStack(aMessage); |
| } |
| |
| void NcpBase::HandleDatagramFromStack(otMessage aMessage) |
| { |
| ThreadError errorCode = kThreadError_None; |
| bool isSecure = otIsMessageLinkSecurityEnabled(aMessage); |
| uint16_t length = otGetMessageLength(aMessage); |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| |
| SuccessOrExit( |
| errorCode = OutboundFrameFeedPacked( |
| "CiiS", |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| isSecure |
| ? SPINEL_PROP_STREAM_NET |
| : SPINEL_PROP_STREAM_NET_INSECURE, |
| length |
| )); |
| |
| SuccessOrExit(errorCode = OutboundFrameFeedMessage(aMessage)); |
| |
| // Set the aMessage pointer to NULL, to indicate that it does not need to be freed at the exit. |
| // The aMessage is now owned by the OutboundFrame and will be freed when the frame is either successfully sent and |
| // then removed, or if the frame gets discarded. |
| aMessage = NULL; |
| |
| // Append any metadata (rssi, lqi, channel, etc) here! |
| |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| |
| if (aMessage != NULL) |
| { |
| otFreeMessage(aMessage); |
| } |
| |
| if (errorCode != kThreadError_None) |
| { |
| SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_DROPPED); |
| mDroppedOutboundIpFrameCounter++; |
| } |
| else |
| { |
| if (isSecure) |
| { |
| mOutboundSecureIpFrameCounter++; |
| } |
| else |
| { |
| mOutboundInsecureIpFrameCounter++; |
| } |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Raw frame handling |
| // ---------------------------------------------------------------------------- |
| |
| void NcpBase::HandleRawFrame(const RadioPacket *aFrame, void *aContext) |
| { |
| static_cast<NcpBase *>(aContext)->HandleRawFrame(aFrame); |
| } |
| |
| void NcpBase::HandleRawFrame(const RadioPacket *aFrame) |
| { |
| ThreadError errorCode = kThreadError_None; |
| uint16_t flags = 0; |
| |
| if (!mIsRawStreamEnabled) |
| { |
| goto exit; |
| } |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| |
| if (aFrame->mDidTX) |
| { |
| flags |= SPINEL_MD_FLAG_TX; |
| } |
| |
| // Append frame header and frame length |
| SuccessOrExit( |
| errorCode = OutboundFrameFeedPacked( |
| "CiiS", |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_STREAM_RAW, |
| aFrame->mLength |
| ) |
| ); |
| |
| // Append the frame contents |
| SuccessOrExit( |
| errorCode = OutboundFrameFeedData( |
| aFrame->mPsdu, |
| aFrame->mLength |
| ) |
| ); |
| |
| // Append metadata (rssi, etc) |
| SuccessOrExit( |
| errorCode = OutboundFrameFeedPacked( |
| "ccS", |
| aFrame->mPower, // TX Power |
| -128, // Noise Floor (Currently unused) |
| flags // Flags |
| |
| // Skip PHY and Vendor data for now |
| ) |
| ); |
| |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| return; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Scan Results Glue |
| // ---------------------------------------------------------------------------- |
| |
| void NcpBase::HandleActiveScanResult_Jump(otActiveScanResult *aResult, void *aContext) |
| { |
| static_cast<NcpBase *>(aContext)->HandleActiveScanResult(aResult); |
| } |
| |
| void NcpBase::HandleActiveScanResult(otActiveScanResult *result) |
| { |
| ThreadError errorCode; |
| |
| if (result) |
| { |
| uint8_t flags = static_cast<uint8_t>(result->mVersion << SPINEL_BEACON_THREAD_FLAG_VERSION_SHIFT); |
| |
| if (result->mIsJoinable) |
| { |
| flags |= SPINEL_BEACON_THREAD_FLAG_JOINABLE; |
| } |
| |
| if (result->mIsNative) |
| { |
| flags |= SPINEL_BEACON_THREAD_FLAG_NATIVE; |
| } |
| |
| //chan,rssi,(laddr,saddr,panid,lqi),(proto,flags,networkid,xpanid) [icT(ESSC)T(iCUD.).] |
| NcpBase::SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| SPINEL_PROP_MAC_SCAN_BEACON, |
| "CcT(ESSC.)T(iCUD.).", |
| result->mChannel, |
| result->mRssi, |
| result->mExtAddress.m8, // laddr |
| 0xFFFF, // saddr, Not given |
| result->mPanId, |
| result->mLqi, |
| SPINEL_PROTOCOL_TYPE_THREAD, |
| flags, |
| result->mNetworkName.m8, |
| result->mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE |
| ); |
| } |
| else |
| { |
| // We are finished with the scan, so send out |
| // a property update indicating such. |
| errorCode = SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_MAC_SCAN_STATE, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_SCAN_STATE_IDLE |
| ); |
| |
| // If we could not send the end of scan inidciator message now (no |
| // buffer space), we set `mShouldSignalEndOfScan` to true to send |
| // it out when buffer space becomes available. |
| if (errorCode != kThreadError_None) |
| { |
| mShouldSignalEndOfScan = true; |
| } |
| } |
| } |
| |
| void NcpBase::HandleEnergyScanResult_Jump(otEnergyScanResult *aResult, void *aContext) |
| { |
| static_cast<NcpBase *>(aContext)->HandleEnergyScanResult(aResult); |
| } |
| |
| void NcpBase::HandleEnergyScanResult(otEnergyScanResult *aResult) |
| { |
| ThreadError errorCode; |
| |
| if (aResult) |
| { |
| NcpBase::SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| SPINEL_PROP_MAC_ENERGY_SCAN_RESULT, |
| "Cc", |
| aResult->mChannel, |
| aResult->mMaxRssi |
| ); |
| } |
| else |
| { |
| // We are finished with the scan, so send out |
| // a property update indicating such. |
| errorCode = SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_MAC_SCAN_STATE, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_SCAN_STATE_IDLE |
| ); |
| |
| // If we could not send the end of scan indicator message now (no |
| // buffer space), we set `mShouldSignalEndOfScan` to true to send |
| // it out when buffer space becomes available. |
| if (errorCode != kThreadError_None) |
| { |
| mShouldSignalEndOfScan = true; |
| } |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Address Table Changed Glue |
| // ---------------------------------------------------------------------------- |
| |
| void NcpBase::HandleNetifStateChanged(uint32_t flags, void *context) |
| { |
| NcpBase *obj = static_cast<NcpBase *>(context); |
| |
| obj->mChangedFlags |= flags; |
| |
| obj->mUpdateChangedPropsTask.Post(); |
| } |
| |
| void NcpBase::UpdateChangedProps(void *context) |
| { |
| NcpBase *obj = static_cast<NcpBase *>(context); |
| obj->UpdateChangedProps(); |
| } |
| |
| void NcpBase::UpdateChangedProps(void) |
| { |
| while (mChangedFlags != 0) |
| { |
| if ((mChangedFlags & NCP_PLAT_RESET_REASON) != 0) |
| { |
| SuccessOrExit(SendLastStatus( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| ResetReasonToSpinelStatus(otPlatGetResetReason(mInstance)) |
| )); |
| mChangedFlags &= ~static_cast<uint32_t>(NCP_PLAT_RESET_REASON); |
| } |
| else if ((mChangedFlags & OT_IP6_LL_ADDR_CHANGED) != 0) |
| { |
| SuccessOrExit(HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_IPV6_LL_ADDR |
| )); |
| mChangedFlags &= ~static_cast<uint32_t>(OT_IP6_LL_ADDR_CHANGED); |
| } |
| else if ((mChangedFlags & OT_IP6_ML_ADDR_CHANGED) != 0) |
| { |
| SuccessOrExit(HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_IPV6_ML_ADDR |
| )); |
| mChangedFlags &= ~static_cast<uint32_t>(OT_IP6_ML_ADDR_CHANGED); |
| } |
| else if ((mChangedFlags & OT_NET_ROLE) != 0) |
| { |
| if (mRequireJoinExistingNetwork) |
| { |
| switch (otGetDeviceRole(mInstance)) |
| { |
| case kDeviceRoleDetached: |
| case kDeviceRoleDisabled: |
| break; |
| |
| default: |
| mRequireJoinExistingNetwork = false; |
| break; |
| } |
| |
| if ( (otGetDeviceRole(mInstance) == kDeviceRoleLeader) |
| && otIsSingleton(mInstance) |
| #if OPENTHREAD_ENABLE_LEGACY |
| && !mLegacyNodeDidJoin |
| #endif |
| ) { |
| mChangedFlags &= ~static_cast<uint32_t>(OT_NET_PARTITION_ID); |
| otThreadStop(mInstance); |
| |
| // TODO: It would be nice to be able to indicate |
| // something more specific than SPINEL_STATUS_JOIN_FAILURE |
| // here, but it isn't clear how that would work |
| // with the current OpenThread API. |
| |
| SuccessOrExit(SendLastStatus( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_STATUS_JOIN_FAILURE |
| )); |
| |
| SuccessOrExit(HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_NET_STACK_UP |
| )); |
| } |
| |
| SuccessOrExit(HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING |
| )); |
| } |
| |
| SuccessOrExit(HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_NET_ROLE |
| )); |
| mChangedFlags &= ~static_cast<uint32_t>(OT_NET_ROLE); |
| } |
| else if ((mChangedFlags & OT_NET_PARTITION_ID) != 0) |
| { |
| SuccessOrExit(HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_NET_PARTITION_ID |
| )); |
| mChangedFlags &= ~static_cast<uint32_t>(OT_NET_PARTITION_ID); |
| } |
| else if ((mChangedFlags & OT_NET_KEY_SEQUENCE_COUNTER) != 0) |
| { |
| SuccessOrExit(HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER |
| )); |
| mChangedFlags &= ~static_cast<uint32_t>(OT_NET_KEY_SEQUENCE_COUNTER); |
| } |
| else if ((mChangedFlags & (OT_IP6_ADDRESS_ADDED | OT_IP6_ADDRESS_REMOVED)) != 0) |
| { |
| SuccessOrExit(HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_IPV6_ADDRESS_TABLE |
| )); |
| mChangedFlags &= ~static_cast<uint32_t>(OT_IP6_ADDRESS_ADDED | OT_IP6_ADDRESS_REMOVED); |
| } |
| else if ((mChangedFlags & (OT_THREAD_CHILD_ADDED | OT_THREAD_CHILD_REMOVED)) != 0) |
| { |
| SuccessOrExit(HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_THREAD_CHILD_TABLE |
| )); |
| mChangedFlags &= ~static_cast<uint32_t>(OT_THREAD_CHILD_ADDED | OT_THREAD_CHILD_REMOVED); |
| } |
| else if ((mChangedFlags & OT_THREAD_NETDATA_UPDATED) != 0) |
| { |
| SuccessOrExit(HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_THREAD_ON_MESH_NETS |
| )); |
| |
| SuccessOrExit(HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_THREAD_LEADER_NETWORK_DATA |
| )); |
| |
| mChangedFlags &= ~static_cast<uint32_t>(OT_THREAD_NETDATA_UPDATED); |
| } |
| else if ((mChangedFlags & (OT_IP6_RLOC_ADDED | OT_IP6_RLOC_REMOVED)) != 0) |
| { |
| mChangedFlags &= ~static_cast<uint32_t>(OT_IP6_RLOC_ADDED | OT_IP6_RLOC_REMOVED); |
| } |
| } |
| |
| exit: |
| return; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Serial Traffic Glue |
| // ---------------------------------------------------------------------------- |
| |
| ThreadError NcpBase::OutboundFrameSend(void) |
| { |
| ThreadError errorCode; |
| |
| SuccessOrExit(errorCode = OutboundFrameEnd()); |
| |
| mTxSpinelFrameCounter++; |
| |
| exit: |
| return errorCode; |
| } |
| |
| void NcpBase::HandleReceive(const uint8_t *buf, uint16_t bufLength) |
| { |
| uint8_t header = 0; |
| unsigned int command = 0; |
| spinel_ssize_t parsedLength; |
| const uint8_t *arg_ptr = NULL; |
| unsigned int arg_len = 0; |
| ThreadError errorCode = kThreadError_None; |
| spinel_tid_t tid = 0; |
| |
| parsedLength = spinel_datatype_unpack(buf, bufLength, "CiD", &header, &command, &arg_ptr, &arg_len); |
| tid = SPINEL_HEADER_GET_TID(header); |
| |
| if (parsedLength == bufLength) |
| { |
| errorCode = HandleCommand(header, command, arg_ptr, static_cast<uint16_t>(arg_len)); |
| |
| // Check if we may have missed a `tid` in the sequence. |
| if ((mNextExpectedTid != 0) && (tid != mNextExpectedTid)) |
| { |
| mRxSpinelOutOfOrderTidCounter++; |
| } |
| |
| mNextExpectedTid = SPINEL_GET_NEXT_TID(tid); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| if (errorCode == kThreadError_NoBufs) |
| { |
| // If we cannot send a response due to buffer space not being |
| // available, we remember the TID of command so to send an |
| // error status when buffer space becomes available later. |
| |
| // Valid TID range is 1-15 (zero being used as special case |
| // where no reply is expected). TIDs for dropped reply are |
| // stored in two variables: `mDroppedReplyTidBitSet` which |
| // is a bit set (bits 1-15 correspond to TID values 1-15). |
| // The first/next dropped TID value in the set is stored in |
| // `mDroppedReplyTid` (with value zero indicating that there |
| // is no dropped reply). |
| |
| if (tid != 0) |
| { |
| if (mDroppedReplyTid == 0) |
| { |
| mDroppedReplyTid = tid; |
| } |
| |
| mDroppedReplyTidBitSet |= (1 << tid); |
| } |
| } |
| |
| mRxSpinelFrameCounter++; |
| } |
| |
| void NcpBase::HandleSpaceAvailableInTxBuffer(void) |
| { |
| while (mDroppedReplyTid != 0) |
| { |
| SuccessOrExit( |
| SendLastStatus( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | mDroppedReplyTid, |
| SPINEL_STATUS_NOMEM |
| ) |
| ); |
| |
| mDroppedReplyTidBitSet &= ~(1 << mDroppedReplyTid); |
| |
| if (mDroppedReplyTidBitSet == 0) |
| { |
| mDroppedReplyTid = 0; |
| |
| break; |
| } |
| |
| do |
| { |
| mDroppedReplyTid = SPINEL_GET_NEXT_TID(mDroppedReplyTid); |
| } |
| while ((mDroppedReplyTidBitSet & (1 << mDroppedReplyTid)) == 0); |
| } |
| |
| if (mShouldSignalEndOfScan) |
| { |
| SuccessOrExit( |
| SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_MAC_SCAN_STATE, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_SCAN_STATE_IDLE |
| )); |
| |
| mShouldSignalEndOfScan = false; |
| } |
| |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| if (mShouldSignalJamStateChange) |
| { |
| SuccessOrExit( |
| SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_JAM_DETECTED, |
| SPINEL_DATATYPE_BOOL_S, |
| otGetJamDetectionState(mInstance) |
| )); |
| |
| mShouldSignalJamStateChange = false; |
| } |
| #endif // OPENTHREAD_ENABLE_JAM_DETECTION |
| |
| UpdateChangedProps(); |
| |
| exit: |
| return; |
| } |
| |
| void NcpBase::IncrementFrameErrorCounter(void) |
| { |
| mFramingErrorCounter++; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Inbound Command Handlers |
| // ---------------------------------------------------------------------------- |
| |
| ThreadError NcpBase::HandleCommand(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len) |
| { |
| unsigned i; |
| ThreadError errorCode = kThreadError_None; |
| |
| // Skip if this isn't a spinel frame |
| VerifyOrExit((SPINEL_HEADER_FLAG & header) == SPINEL_HEADER_FLAG, errorCode = kThreadError_InvalidArgs); |
| |
| // We only support IID zero for now. |
| VerifyOrExit( |
| SPINEL_HEADER_GET_IID(header) == 0, |
| errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_INTERFACE) |
| ); |
| |
| for (i = 0; i < sizeof(mCommandHandlerTable) / sizeof(mCommandHandlerTable[0]); i++) |
| { |
| if (mCommandHandlerTable[i].mCommand == command) |
| { |
| break; |
| } |
| } |
| |
| if (i < sizeof(mCommandHandlerTable) / sizeof(mCommandHandlerTable[0])) |
| { |
| errorCode = (this->*mCommandHandlerTable[i].mHandler)(header, command, arg_ptr, arg_len); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_COMMAND); |
| } |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::HandleCommandPropertyGet(uint8_t header, spinel_prop_key_t key) |
| { |
| unsigned i; |
| ThreadError errorCode = kThreadError_None; |
| |
| for (i = 0; i < sizeof(mGetPropertyHandlerTable) / sizeof(mGetPropertyHandlerTable[0]); i++) |
| { |
| if (mGetPropertyHandlerTable[i].mPropKey == key) |
| { |
| break; |
| } |
| } |
| |
| if (i < sizeof(mGetPropertyHandlerTable) / sizeof(mGetPropertyHandlerTable[0])) |
| { |
| errorCode = (this->*mGetPropertyHandlerTable[i].mHandler)(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PROP_NOT_FOUND); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::HandleCommandPropertySet(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| unsigned i; |
| ThreadError errorCode = kThreadError_None; |
| |
| for (i = 0; i < sizeof(mSetPropertyHandlerTable) / sizeof(mSetPropertyHandlerTable[0]); i++) |
| { |
| if (mSetPropertyHandlerTable[i].mPropKey == key) |
| { |
| break; |
| } |
| } |
| |
| if (i < sizeof(mSetPropertyHandlerTable) / sizeof(mSetPropertyHandlerTable[0])) |
| { |
| errorCode = (this->*mSetPropertyHandlerTable[i].mHandler)(header, key, value_ptr, value_len); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PROP_NOT_FOUND); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::HandleCommandPropertyInsert(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| unsigned i; |
| ThreadError errorCode = kThreadError_None; |
| |
| for (i = 0; i < sizeof(mInsertPropertyHandlerTable) / sizeof(mInsertPropertyHandlerTable[0]); i++) |
| { |
| if (mInsertPropertyHandlerTable[i].mPropKey == key) |
| { |
| break; |
| } |
| } |
| |
| if (i < sizeof(mInsertPropertyHandlerTable) / sizeof(mInsertPropertyHandlerTable[0])) |
| { |
| errorCode = (this->*mInsertPropertyHandlerTable[i].mHandler)(header, key, value_ptr, value_len); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PROP_NOT_FOUND); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::HandleCommandPropertyRemove(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| unsigned i; |
| ThreadError errorCode = kThreadError_None; |
| |
| for (i = 0; i < sizeof(mRemovePropertyHandlerTable) / sizeof(mRemovePropertyHandlerTable[0]); i++) |
| { |
| if (mRemovePropertyHandlerTable[i].mPropKey == key) |
| { |
| break; |
| } |
| } |
| |
| if (i < sizeof(mRemovePropertyHandlerTable) / sizeof(mRemovePropertyHandlerTable[0])) |
| { |
| errorCode = (this->*mRemovePropertyHandlerTable[i].mHandler)(header, key, value_ptr, value_len); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PROP_NOT_FOUND); |
| } |
| |
| return errorCode; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Outbound Command Handlers |
| // ---------------------------------------------------------------------------- |
| |
| |
| ThreadError NcpBase::SendLastStatus(uint8_t header, spinel_status_t lastStatus) |
| { |
| if (SPINEL_HEADER_GET_IID(header) == 0) |
| { |
| mLastStatus = lastStatus; |
| } |
| |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_LAST_STATUS, |
| SPINEL_DATATYPE_UINT_PACKED_S, |
| lastStatus |
| ); |
| } |
| |
| ThreadError NcpBase::SendPropertyUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key, |
| const char *pack_format, ...) |
| { |
| ThreadError errorCode = kThreadError_None; |
| va_list args; |
| |
| va_start(args, pack_format); |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, command, key)); |
| SuccessOrExit(errorCode = OutboundFrameFeedVPacked(pack_format, args)); |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| va_end(args); |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SendPropertyUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| ThreadError errorCode = kThreadError_None; |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, command, key)); |
| SuccessOrExit(errorCode = OutboundFrameFeedData(value_ptr, value_len)); |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SendPropertyUpdate(uint8_t header, uint8_t command, spinel_prop_key_t key, otMessage aMessage) |
| { |
| ThreadError errorCode = kThreadError_None; |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, command, key)); |
| SuccessOrExit(errorCode = OutboundFrameFeedMessage(aMessage)); |
| |
| // Set the aMessage pointer to NULL, to indicate that it does not need to be freed at the exit. |
| // The message is now owned by the OutboundFrame and will be freed when the frame is either successfully sent and |
| // then removed, or if the frame gets discarded. |
| aMessage = NULL; |
| |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| |
| if (aMessage != NULL) |
| { |
| otFreeMessage(aMessage); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::OutboundFrameFeedVPacked(const char *pack_format, va_list args) |
| { |
| uint8_t buf[64]; |
| ThreadError errorCode = kThreadError_NoBufs; |
| spinel_ssize_t packed_len; |
| |
| packed_len = spinel_datatype_vpack(buf, sizeof(buf), pack_format, args); |
| |
| if ((packed_len > 0) && (packed_len <= static_cast<spinel_ssize_t>(sizeof(buf)))) |
| { |
| errorCode = OutboundFrameFeedData(buf, static_cast<uint16_t>(packed_len)); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::OutboundFrameFeedPacked(const char *pack_format, ...) |
| { |
| ThreadError errorCode; |
| va_list args; |
| |
| va_start(args, pack_format); |
| errorCode = OutboundFrameFeedVPacked(pack_format, args); |
| va_end(args); |
| |
| return errorCode; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Individual Command Handlers |
| // ---------------------------------------------------------------------------- |
| |
| ThreadError NcpBase::CommandHandler_NOOP(uint8_t header, unsigned int command, const uint8_t *arg_ptr, uint16_t arg_len) |
| { |
| (void)command; |
| (void)arg_ptr; |
| (void)arg_len; |
| |
| return SendLastStatus(header, SPINEL_STATUS_OK); |
| } |
| |
| ThreadError NcpBase::CommandHandler_RESET(uint8_t header, unsigned int command, const uint8_t *arg_ptr, |
| uint16_t arg_len) |
| { |
| ThreadError errorCode = kThreadError_None; |
| |
| // We aren't using any of the arguments to this function. |
| (void)header; |
| (void)command; |
| (void)arg_ptr; |
| (void)arg_len; |
| |
| // Signal a platform reset. If implemented, this function |
| // shouldn't return. |
| otPlatformReset(mInstance); |
| |
| // We only get to this point if the |
| // platform doesn't support resetting. |
| // In such a case we fake it. |
| |
| otThreadStop(mInstance); |
| otInterfaceDown(mInstance); |
| |
| errorCode = SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_RESET_SOFTWARE); |
| |
| if (errorCode != kThreadError_None) |
| { |
| mChangedFlags |= NCP_PLAT_RESET_REASON; |
| mUpdateChangedPropsTask.Post(); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::CommandHandler_PROP_VALUE_GET(uint8_t header, unsigned int command, const uint8_t *arg_ptr, |
| uint16_t arg_len) |
| { |
| unsigned int propKey = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, "i", &propKey); |
| |
| if (parsedLength > 0) |
| { |
| errorCode = HandleCommandPropertyGet(header, static_cast<spinel_prop_key_t>(propKey)); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| (void)command; |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::CommandHandler_PROP_VALUE_SET(uint8_t header, unsigned int command, const uint8_t *arg_ptr, |
| uint16_t arg_len) |
| { |
| unsigned int propKey = 0; |
| spinel_ssize_t parsedLength; |
| const uint8_t *value_ptr; |
| unsigned int value_len; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, "iD", &propKey, &value_ptr, &value_len); |
| |
| if (parsedLength == arg_len) |
| { |
| errorCode = HandleCommandPropertySet(header, static_cast<spinel_prop_key_t>(propKey), value_ptr, |
| static_cast<uint16_t>(value_len)); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| (void)command; |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::CommandHandler_PROP_VALUE_INSERT(uint8_t header, unsigned int command, const uint8_t *arg_ptr, |
| uint16_t arg_len) |
| { |
| unsigned int propKey = 0; |
| spinel_ssize_t parsedLength; |
| const uint8_t *value_ptr; |
| unsigned int value_len; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, "iD", &propKey, &value_ptr, &value_len); |
| |
| if (parsedLength == arg_len) |
| { |
| errorCode = HandleCommandPropertyInsert(header, static_cast<spinel_prop_key_t>(propKey), value_ptr, |
| static_cast<uint16_t>(value_len)); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| (void)command; |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::CommandHandler_PROP_VALUE_REMOVE(uint8_t header, unsigned int command, const uint8_t *arg_ptr, |
| uint16_t arg_len) |
| { |
| unsigned int propKey = 0; |
| spinel_ssize_t parsedLength; |
| const uint8_t *value_ptr; |
| unsigned int value_len; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack(arg_ptr, arg_len, "iD", &propKey, &value_ptr, &value_len); |
| |
| if (parsedLength == arg_len) |
| { |
| errorCode = HandleCommandPropertyRemove(header, static_cast<spinel_prop_key_t>(propKey), value_ptr, |
| static_cast<uint16_t>(value_len)); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| (void)command; |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::CommandHandler_NET_SAVE(uint8_t header, unsigned int command, const uint8_t *arg_ptr, |
| uint16_t arg_len) |
| { |
| (void)command; |
| (void)arg_ptr; |
| (void)arg_len; |
| |
| return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| ThreadError NcpBase::CommandHandler_NET_CLEAR(uint8_t header, unsigned int command, const uint8_t *arg_ptr, |
| uint16_t arg_len) |
| { |
| (void)command; |
| (void)arg_ptr; |
| (void)arg_len; |
| |
| return SendLastStatus(header, ThreadErrorToSpinelStatus(otPersistentInfoErase(mInstance))); |
| } |
| |
| ThreadError NcpBase::CommandHandler_NET_RECALL(uint8_t header, unsigned int command, const uint8_t *arg_ptr, |
| uint16_t arg_len) |
| { |
| (void)command; |
| (void)arg_ptr; |
| (void)arg_len; |
| |
| return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Individual Property Getters |
| // ---------------------------------------------------------------------------- |
| |
| |
| ThreadError NcpBase::GetPropertyHandler_LAST_STATUS(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate(header, SPINEL_CMD_PROP_VALUE_IS, key, SPINEL_DATATYPE_UINT_PACKED_S, mLastStatus); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_PROTOCOL_VERSION(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S, |
| SPINEL_PROTOCOL_VERSION_THREAD_MAJOR, |
| SPINEL_PROTOCOL_VERSION_THREAD_MINOR |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_INTERFACE_TYPE(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT_PACKED_S, |
| SPINEL_PROTOCOL_TYPE_THREAD |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_VENDOR_ID(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT_PACKED_S, |
| 0 // Vendor ID. Zero for unknown. |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_CAPS(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key)); |
| |
| // Begin adding capabilities ////////////////////////////////////////////// |
| |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_NET_THREAD_1_0)); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_COUNTERS)); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_MAC_WHITELIST)); |
| |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_JAM_DETECT)); |
| #endif |
| |
| // TODO: Somehow get the following capability from the radio. |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, |
| SPINEL_CAP_802_15_4_2450MHZ_OQPSK)); |
| |
| #if OPENTHREAD_CONFIG_MAX_CHILDREN > 0 |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_ROLE_ROUTER)); |
| #endif |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_NEST_LEGACY_INTERFACE)); |
| #endif |
| |
| // End adding capabilities ///////////////////////////////////////////////// |
| |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_NCP_VERSION(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UTF8_S, |
| otGetVersionString() |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_INTERFACE_COUNT(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| 1 // Only one interface for now |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_POWER_STATE(uint8_t header, spinel_prop_key_t key) |
| { |
| // Always online at the moment |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_POWER_STATE_ONLINE |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_HWADDR(uint8_t header, spinel_prop_key_t key) |
| { |
| otExtAddress hwAddr; |
| otGetFactoryAssignedIeeeEui64(mInstance, &hwAddr); |
| |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_EUI64_S, |
| hwAddr.m8 |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_LOCK(uint8_t header, spinel_prop_key_t key) |
| { |
| // TODO: Implement property lock (Needs API!) |
| (void)key; |
| |
| return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_PHY_ENABLED(uint8_t header, spinel_prop_key_t key) |
| { |
| // TODO: Implement PHY_ENBLED (Needs API!) |
| (void)key; |
| |
| return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_PHY_FREQ(uint8_t header, spinel_prop_key_t key) |
| { |
| uint32_t freq_khz(0); |
| const uint8_t chan(otGetChannel(mInstance)); |
| |
| if (chan == 0) |
| { |
| freq_khz = 868300; |
| } |
| else if (chan < 11) |
| { |
| freq_khz = 906000 - (2000 * 1) + 2000 * (chan); |
| } |
| else if (chan < 26) |
| { |
| freq_khz = 2405000 - (5000 * 11) + 5000 * (chan); |
| } |
| |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT32_S, |
| freq_khz |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_PHY_CHAN_SUPPORTED(uint8_t header, spinel_prop_key_t key) |
| { |
| return GetPropertyHandler_ChannelMaskHelper(header, key, mSupportedChannelMask); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_PHY_CHAN(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| otGetChannel(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_PHY_RSSI(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_INT8_S, |
| otPlatRadioGetRssi(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_MAC_SCAN_STATE(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| |
| if (otIsActiveScanInProgress(mInstance)) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_SCAN_STATE_BEACON |
| ); |
| } |
| else if (otIsEnergyScanInProgress(mInstance)) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_SCAN_STATE_ENERGY |
| ); |
| } |
| else |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_SCAN_STATE_IDLE |
| ); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_MAC_SCAN_PERIOD(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT16_S, |
| mScanPeriod |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_ChannelMaskHelper(uint8_t header, spinel_prop_key_t key, uint32_t channel_mask) |
| { |
| ThreadError errorCode = kThreadError_None; |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key)); |
| |
| for (int i = 0; i < 32; i++) |
| { |
| if (0 != (channel_mask & (1 << i))) |
| { |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT8_S, i)); |
| } |
| } |
| |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_MAC_SCAN_MASK(uint8_t header, spinel_prop_key_t key) |
| { |
| return GetPropertyHandler_ChannelMaskHelper(header, key, mChannelMask); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_MAC_15_4_PANID(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT16_S, |
| otGetPanId(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_MAC_PROMISCUOUS_MODE(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_INT8_S, |
| otPlatRadioGetPromiscuous(mInstance) |
| ? SPINEL_MAC_PROMISCUOUS_MODE_FULL |
| : SPINEL_MAC_PROMISCUOUS_MODE_OFF |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_MAC_15_4_LADDR(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_EUI64_S, |
| otGetExtendedAddress(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_MAC_15_4_SADDR(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT16_S, |
| otGetShortAddress(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_MAC_EXTENDED_ADDR(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_EUI64_S, |
| otGetExtendedAddress(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_MAC_RAW_STREAM_ENABLED(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_BOOL_S, |
| mIsRawStreamEnabled |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_NET_IF_UP(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_BOOL_S, |
| otIsInterfaceUp(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_NET_STACK_UP(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_BOOL_S, |
| otGetDeviceRole(mInstance) != kDeviceRoleDisabled |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_NET_ROLE(uint8_t header, spinel_prop_key_t key) |
| { |
| spinel_net_role_t role(SPINEL_NET_ROLE_DETACHED); |
| |
| switch (otGetDeviceRole(mInstance)) |
| { |
| case kDeviceRoleOffline: |
| case kDeviceRoleDisabled: |
| case kDeviceRoleDetached: |
| role = SPINEL_NET_ROLE_DETACHED; |
| break; |
| |
| case kDeviceRoleChild: |
| role = SPINEL_NET_ROLE_CHILD; |
| break; |
| |
| case kDeviceRoleRouter: |
| role = SPINEL_NET_ROLE_ROUTER; |
| break; |
| |
| case kDeviceRoleLeader: |
| role = SPINEL_NET_ROLE_LEADER; |
| break; |
| } |
| |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| role |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_NET_NETWORK_NAME(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UTF8_S, |
| otGetNetworkName(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_NET_XPANID(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_DATA_S, |
| otGetExtendedPanId(mInstance), |
| sizeof(spinel_net_xpanid_t) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_NET_MASTER_KEY(uint8_t header, spinel_prop_key_t key) |
| { |
| const uint8_t *ptr(NULL); |
| uint8_t len(0); |
| |
| ptr = otGetMasterKey(mInstance, &len); |
| |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_DATA_S, |
| ptr, |
| len |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_NET_KEY_SEQUENCE_COUNTER(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT32_S, |
| otGetKeySequenceCounter(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_NET_PARTITION_ID(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT32_S, |
| otGetPartitionId(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_NET_KEY_SWITCH_GUARDTIME(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT32_S, |
| otGetKeySwitchGuardTime(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA_VERSION(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| otGetNetworkDataVersion(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA_VERSION(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| otGetStableNetworkDataVersion(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| uint8_t network_data[255]; |
| uint8_t network_data_len = 255; |
| |
| otGetNetworkDataLocal( |
| mInstance, |
| false, // Stable? |
| network_data, |
| &network_data_len |
| ); |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key)); |
| SuccessOrExit(errorCode = OutboundFrameFeedData(network_data, network_data_len)); |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| uint8_t network_data[255]; |
| uint8_t network_data_len = 255; |
| |
| otGetNetworkDataLocal( |
| mInstance, |
| true, // Stable? |
| network_data, |
| &network_data_len |
| ); |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key)); |
| SuccessOrExit(errorCode = OutboundFrameFeedData(network_data, network_data_len)); |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_LEADER_NETWORK_DATA(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| uint8_t network_data[255]; |
| uint8_t network_data_len = 255; |
| |
| otGetNetworkDataLeader( |
| mInstance, |
| false, // Stable? |
| network_data, |
| &network_data_len |
| ); |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key)); |
| SuccessOrExit(errorCode = OutboundFrameFeedData(network_data, network_data_len)); |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_STABLE_LEADER_NETWORK_DATA(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| uint8_t network_data[255]; |
| uint8_t network_data_len = 255; |
| |
| otGetNetworkDataLeader( |
| mInstance, |
| true, // Stable? |
| network_data, |
| &network_data_len |
| ); |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key)); |
| SuccessOrExit(errorCode = OutboundFrameFeedData(network_data, network_data_len)); |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_LEADER_RID(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| otGetLeaderRouterId(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| otGetLocalLeaderWeight(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_LEADER_WEIGHT(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| otGetLeaderWeight(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_LEADER_ADDR(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| otIp6Address address; |
| |
| errorCode = otGetLeaderRloc(mInstance, &address); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_IPv6ADDR_S, |
| &address |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_PARENT(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| otRouterInfo parentInfo; |
| |
| errorCode = otGetParentInfo(mInstance, &parentInfo); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT16_S, |
| parentInfo.mExtAddress.m8, |
| parentInfo.mRloc16 |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| otChildInfo childInfo; |
| uint8_t index; |
| uint8_t modeFlags; |
| |
| mDisableStreamWrite = true; |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key)); |
| |
| index = 0; |
| |
| while (otGetChildInfoByIndex(mInstance, index, &childInfo) == kThreadError_None) |
| { |
| if (childInfo.mTimeout > 0) |
| { |
| modeFlags = 0; |
| |
| if (childInfo.mRxOnWhenIdle) |
| { |
| modeFlags |= kThreadMode_RxOnWhenIdle; |
| } |
| |
| if (childInfo.mSecureDataRequest) |
| { |
| modeFlags |= kThreadMode_SecureDataRequest; |
| } |
| |
| if (childInfo.mFullFunction) |
| { |
| modeFlags |= kThreadMode_FullFunctionDevice; |
| } |
| |
| if (childInfo.mFullNetworkData) |
| { |
| modeFlags |= kThreadMode_FullNetworkData; |
| } |
| |
| SuccessOrExit( |
| errorCode = OutboundFrameFeedPacked( |
| "T(" |
| SPINEL_DATATYPE_EUI64_S // EUI64 Address |
| SPINEL_DATATYPE_UINT16_S // Rloc16 |
| SPINEL_DATATYPE_UINT32_S // Timeout |
| SPINEL_DATATYPE_UINT32_S // Age |
| SPINEL_DATATYPE_UINT8_S // Network Data Version |
| SPINEL_DATATYPE_UINT8_S // Link Quality In |
| SPINEL_DATATYPE_INT8_S // Average RSS |
| SPINEL_DATATYPE_UINT8_S // Mode (flags) |
| ")", |
| childInfo.mExtAddress.m8, |
| childInfo.mRloc16, |
| childInfo.mTimeout, |
| childInfo.mAge, |
| childInfo.mNetworkDataVersion, |
| childInfo.mLinkQualityIn, |
| childInfo.mAverageRssi, |
| modeFlags |
| )); |
| } |
| |
| index++; |
| } |
| |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| mDisableStreamWrite = false; |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_NEIGHBOR_TABLE(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| otNeighborInfoIterator iter = OT_NEIGHBOR_INFO_ITERATOR_INIT; |
| otNeighborInfo neighInfo; |
| uint8_t modeFlags; |
| |
| mDisableStreamWrite = true; |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key)); |
| |
| while (otGetNextNeighborInfo(mInstance, &iter, &neighInfo) == kThreadError_None) |
| { |
| modeFlags = 0; |
| |
| if (neighInfo.mRxOnWhenIdle) |
| { |
| modeFlags |= kThreadMode_RxOnWhenIdle; |
| } |
| |
| if (neighInfo.mSecureDataRequest) |
| { |
| modeFlags |= kThreadMode_SecureDataRequest; |
| } |
| |
| if (neighInfo.mFullFunction) |
| { |
| modeFlags |= kThreadMode_FullFunctionDevice; |
| } |
| |
| if (neighInfo.mFullNetworkData) |
| { |
| modeFlags |= kThreadMode_FullNetworkData; |
| } |
| |
| SuccessOrExit( |
| errorCode = OutboundFrameFeedPacked( |
| "T(" |
| SPINEL_DATATYPE_EUI64_S // EUI64 Address |
| SPINEL_DATATYPE_UINT16_S // Rloc16 |
| SPINEL_DATATYPE_UINT32_S // Age |
| SPINEL_DATATYPE_UINT8_S // Link Quality In |
| SPINEL_DATATYPE_INT8_S // Average RSS |
| SPINEL_DATATYPE_UINT8_S // Mode (flags) |
| SPINEL_DATATYPE_BOOL_S // Is Child |
| SPINEL_DATATYPE_UINT32_S // Link Frame Counter |
| SPINEL_DATATYPE_UINT32_S // MLE Frame Counter |
| ")", |
| neighInfo.mExtAddress.m8, |
| neighInfo.mRloc16, |
| neighInfo.mAge, |
| neighInfo.mLinkQualityIn, |
| neighInfo.mAverageRssi, |
| modeFlags, |
| neighInfo.mIsChild, |
| neighInfo.mLinkFrameCounter, |
| neighInfo.mMleFrameCounter |
| )); |
| } |
| |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| mDisableStreamWrite = false; |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| uint8_t num_entries = 0; |
| const uint16_t *ports = otGetUnsecurePorts(mInstance, &num_entries); |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key)); |
| |
| for (; num_entries != 0; ports++, num_entries--) |
| { |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("S", *ports)); |
| } |
| |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_BOOL_S, |
| mAllowLocalNetworkDataChange |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_BOOL_S, |
| otIsRouterRoleEnabled(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_ON_MESH_NETS(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| otBorderRouterConfig border_router_config; |
| uint8_t flags; |
| |
| mDisableStreamWrite = true; |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key)); |
| |
| // Fill from non-local network data first |
| for (otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT ;;) |
| { |
| errorCode = otGetNextOnMeshPrefix(mInstance, false, &iter, &border_router_config); |
| |
| if (errorCode != kThreadError_None) |
| { |
| break; |
| } |
| |
| flags = BorderRouterConfigToFlagByte(border_router_config); |
| |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked( |
| "T(" |
| SPINEL_DATATYPE_IPv6ADDR_S // IPv6 Prefix |
| SPINEL_DATATYPE_UINT8_S // Prefix Length (in bits) |
| SPINEL_DATATYPE_BOOL_S // isStable |
| SPINEL_DATATYPE_UINT8_S // Flags |
| SPINEL_DATATYPE_BOOL_S // isLocal |
| ").", |
| &border_router_config.mPrefix, |
| 64, |
| border_router_config.mStable, |
| flags, |
| true |
| )); |
| } |
| |
| // Fill from local network data last |
| for (otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT ;;) |
| { |
| errorCode = otGetNextOnMeshPrefix(mInstance, true, &iter, &border_router_config); |
| |
| if (errorCode != kThreadError_None) |
| { |
| break; |
| } |
| |
| flags = BorderRouterConfigToFlagByte(border_router_config); |
| |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked( |
| "T(" |
| SPINEL_DATATYPE_IPv6ADDR_S // IPv6 Prefix |
| SPINEL_DATATYPE_UINT8_S // Prefix Length (in bits) |
| SPINEL_DATATYPE_BOOL_S // isStable |
| SPINEL_DATATYPE_UINT8_S // Flags |
| SPINEL_DATATYPE_BOOL_S // isLocal |
| ").", |
| &border_router_config.mPrefix, |
| 64, |
| border_router_config.mStable, |
| flags, |
| false |
| )); |
| } |
| |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| mDisableStreamWrite = false; |
| return errorCode; |
| } |
| |
| |
| ThreadError NcpBase::GetPropertyHandler_IPV6_ML_PREFIX(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| const uint8_t *ml_prefix = otGetMeshLocalPrefix(mInstance); |
| |
| if (ml_prefix) |
| { |
| otIp6Address addr; |
| |
| memcpy(addr.mFields.m8, ml_prefix, 8); |
| |
| // Zero out the last 8 bytes. |
| memset(addr.mFields.m8 + 8, 0, 8); |
| |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_UINT8_S, |
| &addr, |
| 64 |
| ); |
| } |
| else |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_VOID_S |
| ); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_IPV6_ML_ADDR(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| const otIp6Address *ml64 = otGetMeshLocalEid(mInstance); |
| |
| if (ml64) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_IPv6ADDR_S, |
| ml64 |
| ); |
| } |
| else |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_VOID_S |
| ); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_IPV6_LL_ADDR(uint8_t header, spinel_prop_key_t key) |
| { |
| // TODO! |
| (void)key; |
| |
| return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_IPV6_ADDRESS_TABLE(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| |
| mDisableStreamWrite = true; |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key)); |
| |
| for (const otNetifAddress *address = otGetUnicastAddresses(mInstance); address; address = address->mNext) |
| { |
| |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked( |
| "T(6CLL).", |
| &address->mAddress, |
| address->mPrefixLength, |
| address->mPreferred ? 0xffffffff : 0, |
| address->mValid ? 0xffffffff : 0 |
| )); |
| } |
| |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| mDisableStreamWrite = false; |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_IPV6_ROUTE_TABLE(uint8_t header, spinel_prop_key_t key) |
| { |
| // TODO: Implement get route table |
| (void)key; |
| |
| return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_IPV6_ICMP_PING_OFFLOAD(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_BOOL_S, |
| otIsIcmpEchoEnabled(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU(uint8_t header, spinel_prop_key_t key) |
| { |
| // Note reverse logic: passthru enabled = filter disabled |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_BOOL_S, |
| !otIsReceiveIp6DatagramFilterEnabled(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_LOCAL_ROUTES(uint8_t header, spinel_prop_key_t key) |
| { |
| // TODO: Implement get external route table |
| (void)key; |
| |
| return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_STREAM_NET(uint8_t header, spinel_prop_key_t key) |
| { |
| // TODO: Implement explicit data poll. |
| (void)key; |
| |
| return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| |
| ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_ENABLE(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_BOOL_S, |
| otIsJamDetectionEnabled(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_JAM_DETECTED(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_BOOL_S, |
| otGetJamDetectionState(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_INT8_S, |
| otGetJamDetectionRssiThreshold(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_WINDOW(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| otGetJamDetectionWindow(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_BUSY(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| otGetJamDetectionBusyPeriod(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_JAM_DETECT_HISTORY_BITMAP(uint8_t header, spinel_prop_key_t key) |
| { |
| uint64_t historyBitmap = otGetJamDetectionHistoryBitmap(mInstance); |
| |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_UINT32_S, |
| static_cast<uint32_t>(historyBitmap & 0xffffffff), |
| static_cast<uint32_t>(historyBitmap >> 32) |
| ); |
| } |
| |
| #endif // OPENTHREAD_ENABLE_JAM_DETECTION |
| |
| ThreadError NcpBase::GetPropertyHandler_MAC_CNTR(uint8_t header, spinel_prop_key_t key) |
| { |
| uint32_t value; |
| const otMacCounters *macCounters; |
| ThreadError errorCode = kThreadError_None; |
| |
| macCounters = otGetMacCounters(mInstance); |
| |
| assert(macCounters != NULL); |
| |
| switch (key) |
| { |
| case SPINEL_PROP_CNTR_TX_PKT_TOTAL: |
| value = macCounters->mTxTotal; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_ACK_REQ: |
| value = macCounters->mTxAckRequested; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_ACKED: |
| value = macCounters->mTxAcked; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_NO_ACK_REQ: |
| value = macCounters->mTxNoAckRequested; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_DATA: |
| value = macCounters->mTxData; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_DATA_POLL: |
| value = macCounters->mTxDataPoll; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_BEACON: |
| value = macCounters->mTxBeacon; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_BEACON_REQ: |
| value = macCounters->mTxBeaconRequest; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_OTHER: |
| value = macCounters->mTxOther; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_RETRY: |
| value = macCounters->mTxRetry; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_ERR_CCA: |
| value = macCounters->mTxErrCca; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_UNICAST: |
| value = macCounters->mTxUnicast; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_BROADCAST: |
| value = macCounters->mTxBroadcast; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_TOTAL: |
| value = macCounters->mRxTotal; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_DATA: |
| value = macCounters->mRxData; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_DATA_POLL: |
| value = macCounters->mRxDataPoll; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_BEACON: |
| value = macCounters->mRxBeacon; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_BEACON_REQ: |
| value = macCounters->mRxBeaconRequest; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_OTHER: |
| value = macCounters->mRxOther; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_FILT_WL: |
| value = macCounters->mRxWhitelistFiltered; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_FILT_DA: |
| value = macCounters->mRxDestAddrFiltered; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_DUP: |
| value = macCounters->mRxDuplicated; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_UNICAST: |
| value = macCounters->mRxUnicast; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_BROADCAST: |
| value = macCounters->mRxBroadcast; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_ERR_EMPTY: |
| value = macCounters->mRxErrNoFrame; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_ERR_UKWN_NBR: |
| value = macCounters->mRxErrUnknownNeighbor; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_ERR_NVLD_SADDR: |
| value = macCounters->mRxErrInvalidSrcAddr; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_ERR_SECURITY: |
| value = macCounters->mRxErrSec; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_ERR_BAD_FCS: |
| value = macCounters->mRxErrFcs; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_ERR_OTHER: |
| value = macCounters->mRxErrOther; |
| break; |
| |
| default: |
| errorCode = SendLastStatus(header, SPINEL_STATUS_INTERNAL_ERROR); |
| goto bail; |
| break; |
| } |
| |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT32_S, |
| value |
| ); |
| |
| bail: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_NCP_CNTR(uint8_t header, spinel_prop_key_t key) |
| { |
| uint32_t value; |
| ThreadError errorCode = kThreadError_None; |
| |
| switch (key) |
| { |
| case SPINEL_PROP_CNTR_TX_IP_SEC_TOTAL: |
| value = mInboundSecureIpFrameCounter; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_IP_INSEC_TOTAL: |
| value = mInboundInsecureIpFrameCounter; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_IP_DROPPED: |
| value = mDroppedInboundIpFrameCounter; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_IP_SEC_TOTAL: |
| value = mOutboundSecureIpFrameCounter; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_IP_INSEC_TOTAL: |
| value = mOutboundInsecureIpFrameCounter; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_IP_DROPPED: |
| value = mDroppedOutboundIpFrameCounter; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_SPINEL_TOTAL: |
| value = mTxSpinelFrameCounter; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_SPINEL_TOTAL: |
| value = mRxSpinelFrameCounter; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_SPINEL_OUT_OF_ORDER_TID: |
| value = mRxSpinelOutOfOrderTidCounter; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_SPINEL_ERR: |
| value = mFramingErrorCounter; |
| break; |
| |
| default: |
| errorCode = SendLastStatus(header, SPINEL_STATUS_INTERNAL_ERROR); |
| goto bail; |
| break; |
| } |
| |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT32_S, |
| value |
| ); |
| |
| bail: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_MSG_BUFFER_COUNTERS(uint8_t header, spinel_prop_key_t key) |
| { |
| ThreadError errorCode = kThreadError_None; |
| otBufferInfo bufferInfo; |
| |
| otGetMessageBufferInfo(mInstance, &bufferInfo); |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key)); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("T(SSSSSSSSSSSSSSSS)", |
| bufferInfo.mTotalBuffers, |
| bufferInfo.mFreeBuffers, |
| bufferInfo.m6loSendMessages, |
| bufferInfo.m6loSendBuffers, |
| bufferInfo.m6loReassemblyMessages, |
| bufferInfo.m6loReassemblyBuffers, |
| bufferInfo.mIp6Messages, |
| bufferInfo.mIp6Buffers, |
| bufferInfo.mMplMessages, |
| bufferInfo.mMplBuffers, |
| bufferInfo.mMleMessages, |
| bufferInfo.mMleBuffers, |
| bufferInfo.mArpMessages, |
| bufferInfo.mArpBuffers, |
| bufferInfo.mCoapClientMessages, |
| bufferInfo.mCoapClientBuffers |
| )); |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_MAC_WHITELIST(uint8_t header, spinel_prop_key_t key) |
| { |
| otMacWhitelistEntry entry; |
| ThreadError errorCode = kThreadError_None; |
| |
| mDisableStreamWrite = true; |
| |
| SuccessOrExit(errorCode = OutboundFrameBegin()); |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("Cii", header, SPINEL_CMD_PROP_VALUE_IS, key)); |
| |
| for (uint8_t i = 0; (i != 255) && (errorCode == kThreadError_None); i++) |
| { |
| errorCode = otGetMacWhitelistEntry(mInstance, i, &entry); |
| |
| if (errorCode != kThreadError_None) |
| { |
| break; |
| } |
| |
| if (entry.mValid) |
| { |
| if (!entry.mFixedRssi) |
| { |
| entry.mRssi = RSSI_OVERRIDE_DISABLED; |
| } |
| |
| SuccessOrExit(errorCode = OutboundFrameFeedPacked("T(Ec).", entry.mExtAddress.m8, entry.mRssi)); |
| } |
| } |
| |
| SuccessOrExit(errorCode = OutboundFrameSend()); |
| |
| exit: |
| mDisableStreamWrite = false; |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_MAC_WHITELIST_ENABLED(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_BOOL_S, |
| otIsMacWhitelistEnabled(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_MODE(uint8_t header, spinel_prop_key_t key) |
| { |
| uint8_t numeric_mode(0); |
| otLinkModeConfig mode_config(otGetLinkMode(mInstance)); |
| |
| if (mode_config.mRxOnWhenIdle) |
| { |
| numeric_mode |= kThreadMode_RxOnWhenIdle; |
| } |
| |
| if (mode_config.mSecureDataRequests) |
| { |
| numeric_mode |= kThreadMode_SecureDataRequest; |
| } |
| |
| if (mode_config.mDeviceType) |
| { |
| numeric_mode |= kThreadMode_FullFunctionDevice; |
| } |
| |
| if (mode_config.mNetworkData) |
| { |
| numeric_mode |= kThreadMode_FullNetworkData; |
| } |
| |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| numeric_mode |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_CHILD_COUNT_MAX(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| otGetMaxAllowedChildren(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_CHILD_TIMEOUT(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT32_S, |
| otGetChildTimeout(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_RLOC16(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT16_S, |
| otGetRloc16(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| otGetRouterUpgradeThreshold(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| otGetRouterDowngradeThreshold(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| otGetRouterSelectionJitter(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT32_S, |
| otGetContextIdReuseDelay(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| otGetNetworkIdTimeout(mInstance) |
| ); |
| } |
| |
| ThreadError NcpBase::GetPropertyHandler_NET_REQUIRE_JOIN_EXISTING(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_BOOL_S, |
| mRequireJoinExistingNetwork |
| ); |
| } |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| ThreadError NcpBase::GetPropertyHandler_NEST_LEGACY_ULA_PREFIX(uint8_t header, spinel_prop_key_t key) |
| { |
| return SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_DATA_S, |
| mLegacyUlaPrefix, |
| sizeof(mLegacyUlaPrefix) |
| ); |
| } |
| #endif // OPENTHREAD_ENABLE_LEGACY |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Individual Property Setters |
| // ---------------------------------------------------------------------------- |
| |
| ThreadError NcpBase::SetPropertyHandler_POWER_STATE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| // TODO: Implement POWER_STATE |
| (void)key; |
| (void)value_ptr; |
| (void)value_len; |
| |
| return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_PHY_ENABLED(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| bool value = false; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_BOOL_S, |
| &value |
| ); |
| |
| if (parsedLength > 0) |
| { |
| if (value == false) |
| { |
| errorCode = otPlatRadioDisable(mInstance); |
| } |
| else |
| { |
| errorCode = otPlatRadioEnable(mInstance); |
| } |
| } |
| else |
| { |
| errorCode = kThreadError_Parse; |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_PHY_TX_POWER(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| // TODO: Implement PHY_TX_POWER |
| (void)key; |
| (void)value_ptr; |
| (void)value_len; |
| |
| return SendLastStatus(header, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_PHY_CHAN(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| unsigned int i = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT_PACKED_S, |
| &i |
| ); |
| |
| if (parsedLength > 0) |
| { |
| errorCode = otSetChannel(mInstance, static_cast<uint8_t>(i)); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_MAC_PROMISCUOUS_MODE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| uint8_t i = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT8_S, |
| &i |
| ); |
| |
| if (parsedLength > 0) |
| { |
| switch (i) |
| { |
| case SPINEL_MAC_PROMISCUOUS_MODE_OFF: |
| otPlatRadioSetPromiscuous(mInstance, false); |
| errorCode = kThreadError_None; |
| break; |
| |
| case SPINEL_MAC_PROMISCUOUS_MODE_NETWORK: |
| case SPINEL_MAC_PROMISCUOUS_MODE_FULL: |
| otPlatRadioSetPromiscuous(mInstance, true); |
| errorCode = kThreadError_None; |
| break; |
| |
| default: |
| errorCode = kThreadError_InvalidArgs; |
| break; |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_MAC_SCAN_MASK(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| ThreadError errorCode = kThreadError_None; |
| uint32_t new_mask(0); |
| |
| for (; value_len != 0; value_len--, value_ptr++) |
| { |
| if ((value_ptr[0] > 31) |
| || (mSupportedChannelMask & (1 << value_ptr[0])) == 0 |
| ) |
| { |
| errorCode = kThreadError_InvalidArgs; |
| break; |
| } |
| |
| new_mask |= (1 << value_ptr[0]); |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| mChannelMask = new_mask; |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_MAC_SCAN_PERIOD(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| uint16_t tmp(mScanPeriod); |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT16_S, |
| &tmp |
| ); |
| |
| if (parsedLength > 0) |
| { |
| mScanPeriod = tmp; |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_NET_REQUIRE_JOIN_EXISTING(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| bool tmp(mRequireJoinExistingNetwork); |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_BOOL_S, |
| &tmp |
| ); |
| |
| if (parsedLength > 0) |
| { |
| mRequireJoinExistingNetwork = tmp; |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_MAC_SCAN_STATE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| uint8_t i = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT8_S, |
| &i |
| ); |
| |
| if (parsedLength > 0) |
| { |
| switch (i) |
| { |
| case SPINEL_SCAN_STATE_IDLE: |
| errorCode = kThreadError_None; |
| break; |
| |
| case SPINEL_SCAN_STATE_BEACON: |
| errorCode = otActiveScan( |
| mInstance, |
| mChannelMask, |
| mScanPeriod, |
| &HandleActiveScanResult_Jump, |
| this |
| ); |
| |
| if (errorCode == kThreadError_None) |
| { |
| mShouldSignalEndOfScan = false; |
| } |
| |
| break; |
| |
| case SPINEL_SCAN_STATE_ENERGY: |
| errorCode = otEnergyScan( |
| mInstance, |
| mChannelMask, |
| mScanPeriod, |
| &HandleEnergyScanResult_Jump, |
| this |
| ); |
| |
| if (errorCode == kThreadError_None) |
| { |
| mShouldSignalEndOfScan = false; |
| } |
| |
| break; |
| |
| default: |
| errorCode = kThreadError_InvalidArgs; |
| break; |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_MAC_15_4_PANID(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| uint16_t tmp; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT16_S, |
| &tmp |
| ); |
| |
| if (parsedLength > 0) |
| { |
| errorCode = otSetPanId(mInstance, tmp); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_MAC_RAW_STREAM_ENABLED(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr,uint16_t value_len) |
| { |
| bool value = false; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_BOOL_S, |
| &value |
| ); |
| |
| if (parsedLength > 0) |
| { |
| mIsRawStreamEnabled = value; |
| } |
| else |
| { |
| errorCode = kThreadError_Parse; |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_NET_IF_UP(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| bool value = false; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_BOOL_S, |
| &value |
| ); |
| |
| if (parsedLength > 0) |
| { |
| if (value == false) |
| { |
| errorCode = otInterfaceDown(mInstance); |
| } |
| else |
| { |
| errorCode = otInterfaceUp(mInstance); |
| } |
| } |
| else |
| { |
| errorCode = kThreadError_Parse; |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_NET_STACK_UP(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| bool value = false; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_BOOL_S, |
| &value |
| ); |
| |
| if (parsedLength > 0) |
| { |
| // If the value has changed... |
| if ((value != false) != (otGetDeviceRole(mInstance) != kDeviceRoleDisabled)) |
| { |
| if (value != false) |
| { |
| errorCode = otThreadStart(mInstance); |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| mLegacyNodeDidJoin = false; |
| if (mLegacyHandlers != NULL) |
| { |
| if (mLegacyHandlers->mStartLegacy) |
| { |
| mLegacyHandlers->mStartLegacy(); |
| } |
| } |
| #endif // OPENTHREAD_ENABLE_LEGACY |
| } |
| else |
| { |
| errorCode = otThreadStop(mInstance); |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| mLegacyNodeDidJoin = false; |
| if (mLegacyHandlers != NULL) |
| { |
| if (mLegacyHandlers->mStopLegacy) |
| { |
| mLegacyHandlers->mStopLegacy(); |
| } |
| } |
| #endif // OPENTHREAD_ENABLE_LEGACY |
| } |
| } |
| } |
| else |
| { |
| errorCode = kThreadError_Parse; |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_NET_ROLE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| unsigned int i(0); |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT_PACKED_S, |
| &i |
| ); |
| |
| if (parsedLength > 0) |
| { |
| switch (i) |
| { |
| case SPINEL_NET_ROLE_DETACHED: |
| errorCode = otBecomeDetached(mInstance); |
| break; |
| |
| case SPINEL_NET_ROLE_ROUTER: |
| errorCode = otBecomeRouter(mInstance); |
| break; |
| |
| case SPINEL_NET_ROLE_LEADER: |
| errorCode = otBecomeLeader(mInstance); |
| break; |
| |
| case SPINEL_NET_ROLE_CHILD: |
| errorCode = otBecomeChild(mInstance, kMleAttachAnyPartition); |
| break; |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_NET_NETWORK_NAME(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| const char *string(NULL); |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UTF8_S, |
| &string |
| ); |
| |
| if ((parsedLength > 0) && (string != NULL)) |
| { |
| errorCode = otSetNetworkName(mInstance, string); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_NET_XPANID(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| const uint8_t *ptr = NULL; |
| spinel_size_t len; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_DATA_S, |
| &ptr, |
| &len |
| ); |
| |
| if ((parsedLength > 0) && (len == sizeof(spinel_net_xpanid_t))) |
| { |
| otSetExtendedPanId(mInstance, ptr); |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_NET_MASTER_KEY(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| const uint8_t *ptr = NULL; |
| spinel_size_t len; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_DATA_S, |
| &ptr, |
| &len |
| ); |
| |
| if ((parsedLength > 0) && (len < 100)) |
| { |
| errorCode = otSetMasterKey(mInstance, ptr, static_cast<uint8_t>(len)); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_NET_KEY_SEQUENCE_COUNTER(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| unsigned int i(0); |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT32_S, |
| &i |
| ); |
| |
| if (parsedLength > 0) |
| { |
| otSetKeySequenceCounter(mInstance, i); |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_NET_KEY_SWITCH_GUARDTIME(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| unsigned int i(0); |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT32_S, |
| &i |
| ); |
| |
| if (parsedLength > 0) |
| { |
| otSetKeySwitchGuardTime(mInstance, i); |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| ThreadError errorCode = kThreadError_None; |
| uint8_t value = 0; |
| spinel_ssize_t parsedLength; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT8_S, |
| &value |
| ); |
| |
| if (parsedLength > 0) |
| { |
| otSetLocalLeaderWeight(mInstance, value); |
| } |
| else |
| { |
| errorCode = kThreadError_Parse; |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| |
| return errorCode; |
| } |
| |
| |
| |
| ThreadError NcpBase::SetPropertyHandler_STREAM_NET_INSECURE(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| const uint8_t *frame_ptr(NULL); |
| unsigned int frame_len(0); |
| const uint8_t *meta_ptr(NULL); |
| unsigned int meta_len(0); |
| |
| // STREAM_NET_INSECURE packets are not secured at layer 2. |
| otMessage message = otNewIp6Message(mInstance, false); |
| |
| if (message == NULL) |
| { |
| errorCode = kThreadError_NoBufs; |
| } |
| else |
| { |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_DATA_S SPINEL_DATATYPE_DATA_S, |
| &frame_ptr, |
| &frame_len, |
| &meta_ptr, |
| &meta_len |
| ); |
| |
| // We ignore metadata for now. |
| // May later include TX power, allow retransmits, etc... |
| (void)meta_ptr; |
| (void)meta_len; |
| (void)parsedLength; |
| |
| errorCode = otAppendMessage(message, frame_ptr, static_cast<uint16_t>(frame_len)); |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = otSendIp6Datagram(mInstance, message); |
| } |
| else if (message) |
| { |
| otFreeMessage(message); |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| mInboundInsecureIpFrameCounter++; |
| |
| if (SPINEL_HEADER_GET_TID(header) != 0) |
| { |
| // Only send a successful status update if |
| // there was a transaction id in the header. |
| errorCode = SendLastStatus(header, SPINEL_STATUS_OK); |
| } |
| } |
| else |
| { |
| mDroppedInboundIpFrameCounter++; |
| |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| |
| (void)key; |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_STREAM_NET(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| const uint8_t *frame_ptr(NULL); |
| unsigned int frame_len(0); |
| const uint8_t *meta_ptr(NULL); |
| unsigned int meta_len(0); |
| |
| // STREAM_NET requires layer 2 security. |
| otMessage message = otNewIp6Message(mInstance, true); |
| |
| if (message == NULL) |
| { |
| errorCode = kThreadError_NoBufs; |
| } |
| else |
| { |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_DATA_S SPINEL_DATATYPE_DATA_S, |
| &frame_ptr, |
| &frame_len, |
| &meta_ptr, |
| &meta_len |
| ); |
| |
| // We ignore metadata for now. |
| // May later include TX power, allow retransmits, etc... |
| (void)meta_ptr; |
| (void)meta_len; |
| (void)parsedLength; |
| |
| errorCode = otAppendMessage(message, frame_ptr, static_cast<uint16_t>(frame_len)); |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = otSendIp6Datagram(mInstance, message); |
| } |
| else if (message) |
| { |
| otFreeMessage(message); |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| mInboundSecureIpFrameCounter++; |
| |
| if (SPINEL_HEADER_GET_TID(header) != 0) |
| { |
| // Only send a successful status update if |
| // there was a transaction id in the header. |
| errorCode = SendLastStatus(header, SPINEL_STATUS_OK); |
| } |
| } |
| else |
| { |
| mDroppedInboundIpFrameCounter++; |
| |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| |
| } |
| |
| (void)key; |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_IPV6_ML_PREFIX(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| ThreadError errorCode = kThreadError_None; |
| |
| if (value_len >= 8) |
| { |
| errorCode = otSetMeshLocalPrefix(mInstance, value_ptr); |
| HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = kThreadError_Parse; |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_IPV6_ICMP_PING_OFFLOAD(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| bool isEnabled(false); |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_BOOL_S, |
| &isEnabled |
| ); |
| |
| if (parsedLength > 0) |
| { |
| otSetIcmpEchoEnabled(mInstance, isEnabled); |
| |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| bool isEnabled(false); |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_BOOL_S, |
| &isEnabled |
| ); |
| |
| if (parsedLength > 0) |
| { |
| // Note reverse logic: passthru enabled = filter disabled |
| otSetReceiveIp6DatagramFilterEnabled(mInstance, !isEnabled); |
| |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| ThreadError errorCode = kThreadError_None; |
| uint8_t num_entries = 0; |
| const uint16_t *ports = otGetUnsecurePorts(mInstance, &num_entries); |
| spinel_ssize_t parsedLength = 1; |
| int ports_changed = 0; |
| |
| // First, we need to remove all of the current assisting ports. |
| for (; num_entries != 0; ports++, num_entries--) |
| { |
| errorCode = otRemoveUnsecurePort(mInstance, *ports); |
| |
| if (errorCode != kThreadError_None) |
| { |
| break; |
| } |
| |
| ports_changed++; |
| } |
| |
| while ((errorCode == kThreadError_None) |
| && (parsedLength > 0) |
| && (value_len >= 2) |
| ) |
| { |
| uint16_t port; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "S", |
| &port |
| ); |
| |
| if (parsedLength > 0) |
| { |
| errorCode = otAddUnsecurePort(mInstance, port); |
| } |
| else |
| { |
| errorCode = kThreadError_Parse; |
| } |
| |
| if (errorCode != kThreadError_None) |
| { |
| break; |
| } |
| |
| value_ptr += parsedLength; |
| value_len -= parsedLength; |
| |
| ports_changed++; |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| |
| if (ports_changed) |
| { |
| // We had an error, but we've actually changed |
| // the state of these ports---so we need to report |
| // those incomplete changes via an asynchronous |
| // change event. |
| HandleCommandPropertyGet(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, key); |
| } |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| bool value = false; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| bool should_register_with_leader = false; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_BOOL_S, |
| &value |
| ); |
| |
| if (parsedLength > 0) |
| { |
| // Register any net data changes on transition from `true` to `false`. |
| should_register_with_leader = (mAllowLocalNetworkDataChange == true) && (value == false); |
| |
| mAllowLocalNetworkDataChange = value; |
| } |
| else |
| { |
| errorCode = kThreadError_Parse; |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| |
| if (should_register_with_leader) |
| { |
| otSendServerData(mInstance); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| bool isEnabled; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_BOOL_S, |
| &isEnabled |
| ); |
| |
| if (parsedLength > 0) |
| { |
| otSetRouterRoleEnabled(mInstance, isEnabled); |
| |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_CNTR_RESET(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| ThreadError errorCode = kThreadError_None; |
| uint8_t value = 0; |
| spinel_ssize_t parsedLength; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT8_S, |
| &value |
| ); |
| |
| if (parsedLength > 0) |
| { |
| if (value == 1) |
| { |
| // TODO: Implement counter reset! |
| errorCode = kThreadError_NotImplemented; |
| } |
| else |
| { |
| errorCode = kThreadError_InvalidArgs; |
| } |
| } |
| else |
| { |
| errorCode = kThreadError_Parse; |
| } |
| |
| (void)key; |
| |
| // There is currently no getter for PROP_CNTR_RESET, so we just |
| // return SPINEL_STATUS_OK for success when the counters are reset. |
| |
| return SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_MAC_WHITELIST(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| ThreadError errorCode = kThreadError_None; |
| spinel_ssize_t parsedLength = 1; |
| |
| // First, clear the whitelist. |
| otClearMacWhitelist(mInstance); |
| |
| while ((errorCode == kThreadError_None) |
| && (parsedLength > 0) |
| && (value_len > 0) |
| ) |
| { |
| otExtAddress *ext_addr = NULL; |
| int8_t rssi = RSSI_OVERRIDE_DISABLED; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "T(Ec).", |
| &ext_addr, |
| &rssi |
| ); |
| |
| if (parsedLength <= 0) |
| { |
| rssi = RSSI_OVERRIDE_DISABLED; |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "T(E).", |
| &ext_addr |
| ); |
| } |
| |
| if (parsedLength <= 0) |
| { |
| errorCode = kThreadError_Parse; |
| break; |
| } |
| |
| if (rssi == RSSI_OVERRIDE_DISABLED) |
| { |
| errorCode = otAddMacWhitelist(mInstance, ext_addr->m8); |
| } |
| else |
| { |
| errorCode = otAddMacWhitelistRssi(mInstance, ext_addr->m8, rssi); |
| } |
| |
| value_ptr += parsedLength; |
| value_len -= parsedLength; |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| |
| // We had an error, but we may have actually changed |
| // the state of the whitelist---so we need to report |
| // those incomplete changes via an asynchronous |
| // change event. |
| HandleCommandPropertyGet(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, key); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_MAC_WHITELIST_ENABLED(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| bool isEnabled; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_BOOL_S, |
| &isEnabled |
| ); |
| |
| if (parsedLength > 0) |
| { |
| if (isEnabled) |
| { |
| otEnableMacWhitelist(mInstance); |
| } |
| else |
| { |
| otDisableMacWhitelist(mInstance); |
| } |
| |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_MODE(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| uint8_t numeric_mode = 0; |
| otLinkModeConfig mode_config; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT8_S, |
| &numeric_mode |
| ); |
| |
| if (parsedLength > 0) |
| { |
| mode_config.mRxOnWhenIdle = ((numeric_mode & kThreadMode_RxOnWhenIdle) == kThreadMode_RxOnWhenIdle); |
| mode_config.mSecureDataRequests = ((numeric_mode & kThreadMode_SecureDataRequest) == kThreadMode_SecureDataRequest); |
| mode_config.mDeviceType = ((numeric_mode & kThreadMode_FullFunctionDevice) == kThreadMode_FullFunctionDevice); |
| mode_config.mNetworkData = ((numeric_mode & kThreadMode_FullNetworkData) == kThreadMode_FullNetworkData); |
| |
| errorCode = otSetLinkMode(mInstance, mode_config); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_CHILD_COUNT_MAX(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| uint8_t n = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT8_S, |
| &n |
| ); |
| |
| if (parsedLength > 0) |
| { |
| otSetMaxAllowedChildren(mInstance, n); |
| |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_CHILD_TIMEOUT(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| uint32_t i = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT32_S, |
| &i |
| ); |
| |
| if (parsedLength > 0) |
| { |
| otSetChildTimeout(mInstance, i); |
| |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| uint8_t i = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT8_S, |
| &i |
| ); |
| |
| if (parsedLength > 0) |
| { |
| otSetRouterUpgradeThreshold(mInstance, i); |
| |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| uint8_t i = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT8_S, |
| &i |
| ); |
| |
| if (parsedLength > 0) |
| { |
| otSetRouterDowngradeThreshold(mInstance, i); |
| |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| uint8_t i = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT8_S, |
| &i |
| ); |
| |
| if (parsedLength > 0) |
| { |
| otSetRouterSelectionJitter(mInstance, i); |
| |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_PREFERRED_ROUTER_ID(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| uint8_t router_id = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT8_S, |
| &router_id |
| ); |
| |
| if (parsedLength > 0) |
| { |
| errorCode = otSetPreferredRouterId(mInstance, router_id); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| SPINEL_DATATYPE_UINT8_S, |
| router_id |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| uint32_t i = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT32_S, |
| &i |
| ); |
| |
| if (parsedLength > 0) |
| { |
| otSetContextIdReuseDelay(mInstance, i); |
| |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| uint8_t i = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT8_S, |
| &i |
| ); |
| |
| if (parsedLength > 0) |
| { |
| otSetNetworkIdTimeout(mInstance, i); |
| |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| |
| ThreadError NcpBase::SetPropertyHandler_JAM_DETECT_ENABLE(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| bool isEnabled; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_BOOL_S, |
| &isEnabled |
| ); |
| |
| if (parsedLength > 0) |
| { |
| if (isEnabled) |
| { |
| otStartJamDetection(mInstance, &NcpBase::HandleJamStateChange_Jump, this); |
| } |
| else |
| { |
| otStopJamDetection(mInstance); |
| } |
| |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| int8_t value = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_INT8_S, |
| &value |
| ); |
| |
| if (parsedLength > 0) |
| { |
| errorCode = otSetJamDetectionRssiThreshold(mInstance, value); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_JAM_DETECT_WINDOW(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| uint8_t value = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT8_S, |
| &value |
| ); |
| |
| if (parsedLength > 0) |
| { |
| errorCode = otSetJamDetectionWindow(mInstance, value); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::SetPropertyHandler_JAM_DETECT_BUSY(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, |
| uint16_t value_len) |
| { |
| uint8_t value = 0; |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UINT8_S, |
| &value |
| ); |
| |
| if (parsedLength > 0) |
| { |
| errorCode = otSetJamDetectionBusyPeriod(mInstance, value); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| void NcpBase::HandleJamStateChange_Jump(bool aJamState, void *aContext) |
| { |
| static_cast<NcpBase *>(aContext)->HandleJamStateChange(aJamState); |
| } |
| |
| void NcpBase::HandleJamStateChange(bool aJamState) |
| { |
| ThreadError errorCode; |
| |
| errorCode = SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_JAM_DETECTED, |
| SPINEL_DATATYPE_BOOL_S, |
| aJamState |
| ); |
| |
| // If we could not send the jam state change indicator (no |
| // buffer space), we set `mShouldSignalJamStateChange` to true to send |
| // it out when buffer space becomes available. |
| if (errorCode != kThreadError_None) |
| { |
| mShouldSignalJamStateChange = true; |
| } |
| } |
| |
| #endif // OPENTHREAD_ENABLE_JAM_DETECTION |
| |
| #if OPENTHREAD_ENABLE_DIAG |
| ThreadError NcpBase::SetPropertyHandler_NEST_STREAM_MFG(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| char *string(NULL); |
| char *output(NULL); |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_UTF8_S, |
| &string |
| ); |
| |
| if ((parsedLength > 0) && (string != NULL)) |
| { |
| // all diagnostics related features are processed within diagnostics module |
| output = diagProcessCmdLine(string); |
| |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_IS, |
| key, |
| reinterpret_cast<uint8_t *>(output), |
| static_cast<uint16_t>(strlen(output) + 1) |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| #endif |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| ThreadError NcpBase::SetPropertyHandler_NEST_LEGACY_ULA_PREFIX(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| ThreadError errorCode = kThreadError_None; |
| const uint8_t *ptr = NULL; |
| spinel_size_t len; |
| spinel_ssize_t parsedLength; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| SPINEL_DATATYPE_DATA_S, |
| &ptr, |
| &len |
| ); |
| |
| if ((parsedLength > 0) && (len <= sizeof(mLegacyUlaPrefix))) |
| { |
| memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix)); |
| memcpy(mLegacyUlaPrefix, ptr, len); |
| |
| if (mLegacyHandlers) |
| { |
| if (mLegacyHandlers->mSetLegacyUlaPrefix) |
| { |
| mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix); |
| } |
| } |
| |
| errorCode = HandleCommandPropertyGet(header, key); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| #endif // OPENTHREAD_ENABLE_LEGACY |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Individual Property Inserters |
| // ---------------------------------------------------------------------------- |
| |
| |
| ThreadError NcpBase::InsertPropertyHandler_IPV6_ADDRESS_TABLE(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| spinel_status_t errorStatus = SPINEL_STATUS_OK; |
| otNetifAddress netif_addr; |
| otIp6Address *addr_ptr; |
| uint32_t preferred_lifetime; |
| uint32_t valid_lifetime; |
| uint8_t prefix_len; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "6CLL", |
| &addr_ptr, |
| &prefix_len, |
| &preferred_lifetime, |
| &valid_lifetime |
| ); |
| |
| VerifyOrExit(parsedLength > 0, errorStatus = SPINEL_STATUS_PARSE_ERROR); |
| |
| netif_addr.mAddress = *addr_ptr; |
| netif_addr.mPrefixLength = prefix_len; |
| netif_addr.mPreferred = preferred_lifetime != 0; |
| netif_addr.mValid = valid_lifetime != 0; |
| |
| errorCode = otAddUnicastAddress(mInstance, &netif_addr); |
| |
| VerifyOrExit(errorCode == kThreadError_None, |
| errorStatus = ThreadErrorToSpinelStatus(errorCode)); |
| |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| key, |
| value_ptr, |
| value_len |
| ); |
| |
| errorStatus = SPINEL_STATUS_OK; |
| |
| exit: |
| |
| if (errorStatus != SPINEL_STATUS_OK) |
| { |
| errorCode = SendLastStatus(header, errorStatus); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::InsertPropertyHandler_THREAD_LOCAL_ROUTES(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| const static int kPreferenceOffset = 6; |
| const static int kPreferenceMask = 3 << kPreferenceOffset; |
| |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| otExternalRouteConfig ext_route_config; |
| otIp6Address *addr_ptr; |
| bool stable = false; |
| uint8_t flags = 0; |
| |
| memset(&ext_route_config, 0, sizeof(otExternalRouteConfig)); |
| |
| VerifyOrExit( |
| mAllowLocalNetworkDataChange == true, |
| errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE) |
| ); |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "6CbC", |
| &addr_ptr, |
| &ext_route_config.mPrefix.mLength, |
| &stable, |
| &flags |
| ); |
| |
| if (parsedLength > 0) |
| { |
| ext_route_config.mPrefix.mPrefix = *addr_ptr; |
| ext_route_config.mStable = stable; |
| ext_route_config.mPreference = ((flags & kPreferenceMask) >> kPreferenceOffset); |
| errorCode = otAddExternalRoute(mInstance, &ext_route_config); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| key, |
| value_ptr, |
| value_len |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::InsertPropertyHandler_THREAD_ON_MESH_NETS(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| const static int kPreferenceOffset = 6; |
| const static int kPreferenceMask = 3 << kPreferenceOffset; |
| const static int kPreferredFlag = 1 << 5; |
| const static int kSlaacFlag = 1 << 4; |
| const static int kDhcpFlag = 1 << 3; |
| const static int kConfigureFlag = 1 << 2; |
| const static int kDefaultRouteFlag = 1 << 1; |
| const static int kOnMeshFlag = 1 << 0; |
| |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| otBorderRouterConfig border_router_config; |
| otIp6Address *addr_ptr; |
| bool stable = false; |
| uint8_t flags = 0; |
| |
| memset(&border_router_config, 0, sizeof(otBorderRouterConfig)); |
| |
| VerifyOrExit( |
| mAllowLocalNetworkDataChange == true, |
| errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE) |
| ); |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "6CbC", |
| &addr_ptr, |
| &border_router_config.mPrefix.mLength, |
| &stable, |
| &flags |
| ); |
| |
| if (parsedLength > 0) |
| { |
| border_router_config.mPrefix.mPrefix = *addr_ptr; |
| border_router_config.mStable = stable; |
| border_router_config.mPreference = ((flags & kPreferenceMask) >> kPreferenceOffset); |
| border_router_config.mPreferred = ((flags & kPreferredFlag) == kPreferredFlag); |
| border_router_config.mSlaac = ((flags & kSlaacFlag) == kSlaacFlag); |
| border_router_config.mDhcp = ((flags & kDhcpFlag) == kDhcpFlag); |
| border_router_config.mConfigure = ((flags & kConfigureFlag) == kConfigureFlag); |
| border_router_config.mDefaultRoute = ((flags & kDefaultRouteFlag) == kDefaultRouteFlag); |
| border_router_config.mOnMesh = ((flags & kOnMeshFlag) == kOnMeshFlag); |
| |
| errorCode = otAddBorderRouter(mInstance, &border_router_config); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| key, |
| value_ptr, |
| value_len |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::InsertPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| uint16_t port; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "S", |
| &port |
| ); |
| |
| if (parsedLength > 0) |
| { |
| errorCode = otAddUnsecurePort(mInstance, port); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| key, |
| value_ptr, |
| value_len |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::InsertPropertyHandler_MAC_WHITELIST(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| ThreadError errorCode = kThreadError_None; |
| spinel_ssize_t parsedLength; |
| otExtAddress *ext_addr = NULL; |
| int8_t rssi = RSSI_OVERRIDE_DISABLED; |
| |
| |
| if (value_len > static_cast<spinel_ssize_t>(sizeof(ext_addr))) |
| { |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "Ec", |
| &ext_addr, |
| &rssi |
| ); |
| } |
| else |
| { |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "E", |
| &ext_addr |
| ); |
| } |
| |
| if (parsedLength > 0) |
| { |
| if (rssi == RSSI_OVERRIDE_DISABLED) |
| { |
| errorCode = otAddMacWhitelist(mInstance, ext_addr->m8); |
| } |
| else |
| { |
| errorCode = otAddMacWhitelistRssi(mInstance, ext_addr->m8, rssi); |
| } |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| key, |
| value_ptr, |
| value_len |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Individual Property Removers |
| // ---------------------------------------------------------------------------- |
| |
| |
| ThreadError NcpBase::RemovePropertyHandler_IPV6_ADDRESS_TABLE(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| otIp6Address *addr_ptr; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "6", |
| &addr_ptr |
| ); |
| |
| if (parsedLength > 0) |
| { |
| errorCode = otRemoveUnicastAddress(mInstance, addr_ptr); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| key, |
| value_ptr, |
| value_len |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::RemovePropertyHandler_THREAD_LOCAL_ROUTES(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| otIp6Prefix ip6_prefix; |
| memset(&ip6_prefix, 0, sizeof(otIp6Prefix)); |
| otIp6Address *addr_ptr; |
| |
| VerifyOrExit( |
| mAllowLocalNetworkDataChange == true, |
| errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE) |
| ); |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "6C", |
| &addr_ptr, |
| &ip6_prefix.mLength |
| ); |
| |
| if (parsedLength > 0) |
| { |
| ip6_prefix.mPrefix = *addr_ptr; |
| errorCode = otRemoveExternalRoute(mInstance, &ip6_prefix); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| key, |
| value_ptr, |
| value_len |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::RemovePropertyHandler_THREAD_ON_MESH_NETS(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| |
| otIp6Prefix ip6_prefix; |
| memset(&ip6_prefix, 0, sizeof(otIp6Prefix)); |
| otIp6Address *addr_ptr; |
| |
| VerifyOrExit( |
| mAllowLocalNetworkDataChange == true, |
| errorCode = SendLastStatus(header, SPINEL_STATUS_INVALID_STATE) |
| ); |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "6C", |
| &addr_ptr, |
| &ip6_prefix.mLength |
| ); |
| |
| if (parsedLength > 0) |
| { |
| ip6_prefix.mPrefix = *addr_ptr; |
| errorCode = otRemoveBorderRouter(mInstance, &ip6_prefix); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| key, |
| value_ptr, |
| value_len |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| exit: |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::RemovePropertyHandler_THREAD_ASSISTING_PORTS(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| uint16_t port; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "S", |
| &port |
| ); |
| |
| if (parsedLength > 0) |
| { |
| errorCode = otRemoveUnsecurePort(mInstance, port); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| key, |
| value_ptr, |
| value_len |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::RemovePropertyHandler_THREAD_ACTIVE_ROUTER_IDS(uint8_t header, spinel_prop_key_t key, |
| const uint8_t *value_ptr, uint16_t value_len) |
| { |
| spinel_ssize_t parsedLength; |
| ThreadError errorCode = kThreadError_None; |
| uint8_t router_id; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "C", |
| &router_id |
| ); |
| |
| if (parsedLength > 0) |
| { |
| errorCode = otReleaseRouterId(mInstance, router_id); |
| |
| if (errorCode == kThreadError_None) |
| { |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| key, |
| value_ptr, |
| value_len |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, ThreadErrorToSpinelStatus(errorCode)); |
| } |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| ThreadError NcpBase::RemovePropertyHandler_MAC_WHITELIST(uint8_t header, spinel_prop_key_t key, const uint8_t *value_ptr, uint16_t value_len) |
| { |
| ThreadError errorCode = kThreadError_None; |
| spinel_ssize_t parsedLength; |
| otExtAddress ext_addr; |
| |
| parsedLength = spinel_datatype_unpack( |
| value_ptr, |
| value_len, |
| "E", |
| &ext_addr |
| ); |
| |
| if (parsedLength > 0) |
| { |
| otRemoveMacWhitelist(mInstance, ext_addr.m8); |
| |
| errorCode = SendPropertyUpdate( |
| header, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| key, |
| value_ptr, |
| value_len |
| ); |
| } |
| else |
| { |
| errorCode = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return errorCode; |
| } |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| |
| void NcpBase::RegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers) |
| { |
| mLegacyHandlers = aHandlers; |
| bool isEnabled; |
| |
| VerifyOrExit(mLegacyHandlers != NULL, ;); |
| |
| isEnabled = (otGetDeviceRole(mInstance) != kDeviceRoleDisabled); |
| |
| if (isEnabled) |
| { |
| if (mLegacyHandlers->mStartLegacy) |
| { |
| mLegacyHandlers->mStartLegacy(); |
| } |
| } |
| else |
| { |
| if (mLegacyHandlers->mStopLegacy) |
| { |
| mLegacyHandlers->mStopLegacy(); |
| } |
| } |
| |
| if (mLegacyHandlers->mSetLegacyUlaPrefix) |
| { |
| mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix); |
| } |
| |
| exit: |
| return; |
| } |
| |
| void NcpBase::HandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix) |
| { |
| memcpy(mLegacyUlaPrefix, aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH); |
| |
| SuccessOrExit(OutboundFrameBegin()); |
| |
| SuccessOrExit( |
| OutboundFrameFeedPacked( |
| "Cii" SPINEL_DATATYPE_DATA_S, |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_NEST_LEGACY_ULA_PREFIX, |
| aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH |
| )); |
| |
| OutboundFrameSend(); |
| |
| exit: |
| return; |
| } |
| |
| void NcpBase::HandleLegacyNodeDidJoin(const otExtAddress *aExtAddr) |
| { |
| mLegacyNodeDidJoin = true; |
| |
| SuccessOrExit(OutboundFrameBegin()); |
| |
| SuccessOrExit( |
| OutboundFrameFeedPacked( |
| "Cii" SPINEL_DATATYPE_EUI64_S, |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_NEST_LEGACY_JOINED_NODE, |
| aExtAddr->m8 |
| )); |
| |
| OutboundFrameSend(); |
| |
| exit: |
| return; |
| } |
| |
| #endif // OPENTHREAD_ENABLE_LEGACY |
| |
| ThreadError NcpBase::StreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen) |
| { |
| ThreadError errorCode = kThreadError_None; |
| |
| if (aStreamId == 0) |
| { |
| aStreamId = SPINEL_PROP_STREAM_DEBUG; |
| } |
| |
| if (!mDisableStreamWrite) |
| { |
| errorCode = SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| static_cast<spinel_prop_key_t>(aStreamId), |
| aDataPtr, |
| static_cast<uint16_t>(aDataLen) |
| ); |
| } |
| else |
| { |
| errorCode = kThreadError_InvalidState; |
| } |
| |
| return errorCode; |
| } |
| |
| } // namespace Thread |
| |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Virtual Datastream I/O (Public API) |
| // ---------------------------------------------------------------------------- |
| |
| ThreadError otNcpStreamWrite(int aStreamId, const uint8_t* aDataPtr, int aDataLen) |
| { |
| ThreadError errorCode = kThreadError_InvalidState; |
| |
| if (Thread::sNcpContext) |
| { |
| errorCode = Thread::sNcpContext->StreamWrite(aStreamId, aDataPtr, aDataLen); |
| } |
| |
| return errorCode; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Legacy network APIs |
| // ---------------------------------------------------------------------------- |
| |
| void otNcpRegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers) |
| { |
| #if OPENTHREAD_ENABLE_LEGACY |
| Thread::sNcpContext->RegisterLegacyHandlers(aHandlers); |
| #else |
| (void)aHandlers; |
| #endif |
| } |
| |
| void otNcpHandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix) |
| { |
| #if OPENTHREAD_ENABLE_LEGACY |
| Thread::sNcpContext->HandleDidReceiveNewLegacyUlaPrefix(aUlaPrefix); |
| #else |
| (void)aUlaPrefix; |
| #endif |
| } |
| |
| void otNcpHandleLegacyNodeDidJoin(const otExtAddress *aExtAddr) |
| { |
| #if OPENTHREAD_ENABLE_LEGACY |
| Thread::sNcpContext->HandleLegacyNodeDidJoin(aExtAddr); |
| #else |
| (void)aExtAddr; |
| #endif |
| } |