blob: 6988e529c2599539b85669caf4a5211ac8d3cc87 [file] [log] [blame]
/*
* Copyright (c) 2016-2017, 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 general thread device required Spinel interface to the OpenThread stack.
*/
#include "ncp_base.hpp"
#include <stdarg.h>
#include <stdlib.h>
#include <openthread/diag.h>
#include <openthread/icmp6.h>
#include <openthread/link.h>
#include <openthread/ncp.h>
#include <openthread/openthread.h>
#include <openthread/platform/misc.h>
#include <openthread/platform/radio.h>
#include "common/code_utils.hpp"
#include "common/debug.hpp"
namespace ot {
namespace Ncp {
// ----------------------------------------------------------------------------
// MARK: Property Handler Jump Tables and Methods
// ----------------------------------------------------------------------------
#define NCP_GET_PROP_HANDLER_ENTRY(name) { SPINEL_PROP_##name, &NcpBase::GetPropertyHandler_##name }
const NcpBase::PropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] =
{
NCP_GET_PROP_HANDLER_ENTRY(CAPS),
NCP_GET_PROP_HANDLER_ENTRY(DEBUG_TEST_ASSERT),
NCP_GET_PROP_HANDLER_ENTRY(DEBUG_TEST_WATCHDOG),
NCP_GET_PROP_HANDLER_ENTRY(DEBUG_NCP_LOG_LEVEL),
NCP_GET_PROP_HANDLER_ENTRY(HWADDR),
NCP_GET_PROP_HANDLER_ENTRY(HOST_POWER_STATE),
NCP_GET_PROP_HANDLER_ENTRY(INTERFACE_COUNT),
NCP_GET_PROP_HANDLER_ENTRY(INTERFACE_TYPE),
NCP_GET_PROP_HANDLER_ENTRY(LAST_STATUS),
NCP_GET_PROP_HANDLER_ENTRY(LOCK),
NCP_GET_PROP_HANDLER_ENTRY(PHY_ENABLED),
NCP_GET_PROP_HANDLER_ENTRY(PHY_CHAN),
NCP_GET_PROP_HANDLER_ENTRY(PHY_RX_SENSITIVITY),
NCP_GET_PROP_HANDLER_ENTRY(PHY_TX_POWER),
NCP_GET_PROP_HANDLER_ENTRY(POWER_STATE),
NCP_GET_PROP_HANDLER_ENTRY(MCU_POWER_STATE),
NCP_GET_PROP_HANDLER_ENTRY(PROTOCOL_VERSION),
NCP_GET_PROP_HANDLER_ENTRY(MAC_15_4_PANID),
NCP_GET_PROP_HANDLER_ENTRY(MAC_15_4_LADDR),
NCP_GET_PROP_HANDLER_ENTRY(MAC_15_4_SADDR),
NCP_GET_PROP_HANDLER_ENTRY(MAC_RAW_STREAM_ENABLED),
NCP_GET_PROP_HANDLER_ENTRY(MAC_PROMISCUOUS_MODE),
NCP_GET_PROP_HANDLER_ENTRY(NCP_VERSION),
NCP_GET_PROP_HANDLER_ENTRY(UNSOL_UPDATE_FILTER),
NCP_GET_PROP_HANDLER_ENTRY(UNSOL_UPDATE_LIST),
NCP_GET_PROP_HANDLER_ENTRY(VENDOR_ID),
#if OPENTHREAD_MTD || OPENTHREAD_FTD
NCP_GET_PROP_HANDLER_ENTRY(MAC_DATA_POLL_PERIOD),
NCP_GET_PROP_HANDLER_ENTRY(MAC_EXTENDED_ADDR),
NCP_GET_PROP_HANDLER_ENTRY(MAC_SCAN_STATE),
NCP_GET_PROP_HANDLER_ENTRY(MAC_SCAN_MASK),
NCP_GET_PROP_HANDLER_ENTRY(MAC_SCAN_PERIOD),
NCP_GET_PROP_HANDLER_ENTRY(MAC_CCA_FAILURE_RATE),
#if OPENTHREAD_ENABLE_MAC_FILTER
NCP_GET_PROP_HANDLER_ENTRY(MAC_BLACKLIST),
NCP_GET_PROP_HANDLER_ENTRY(MAC_BLACKLIST_ENABLED),
NCP_GET_PROP_HANDLER_ENTRY(MAC_FIXED_RSS),
NCP_GET_PROP_HANDLER_ENTRY(MAC_WHITELIST),
NCP_GET_PROP_HANDLER_ENTRY(MAC_WHITELIST_ENABLED),
#endif
NCP_GET_PROP_HANDLER_ENTRY(MSG_BUFFER_COUNTERS),
NCP_GET_PROP_HANDLER_ENTRY(PHY_CHAN_SUPPORTED),
NCP_GET_PROP_HANDLER_ENTRY(PHY_FREQ),
NCP_GET_PROP_HANDLER_ENTRY(PHY_RSSI),
NCP_GET_PROP_HANDLER_ENTRY(NET_IF_UP),
NCP_GET_PROP_HANDLER_ENTRY(NET_KEY_SEQUENCE_COUNTER),
NCP_GET_PROP_HANDLER_ENTRY(NET_KEY_SWITCH_GUARDTIME),
NCP_GET_PROP_HANDLER_ENTRY(NET_MASTER_KEY),
NCP_GET_PROP_HANDLER_ENTRY(NET_NETWORK_NAME),
NCP_GET_PROP_HANDLER_ENTRY(NET_PARTITION_ID),
NCP_GET_PROP_HANDLER_ENTRY(NET_REQUIRE_JOIN_EXISTING),
NCP_GET_PROP_HANDLER_ENTRY(NET_ROLE),
NCP_GET_PROP_HANDLER_ENTRY(NET_SAVED),
NCP_GET_PROP_HANDLER_ENTRY(NET_STACK_UP),
NCP_GET_PROP_HANDLER_ENTRY(NET_XPANID),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ALLOW_LOCAL_NET_DATA_CHANGE),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ASSISTING_PORTS),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_CHILD_TIMEOUT),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_JOINER_FLAG),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_ENABLE_FILTERING),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_PANID),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_LEADER_ADDR),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_LEADER_NETWORK_DATA),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_LEADER_RID),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_MODE),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_NEIGHBOR_TABLE),
#if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING
NCP_GET_PROP_HANDLER_ENTRY(THREAD_NEIGHBOR_TABLE_ERROR_RATES),
#endif
NCP_GET_PROP_HANDLER_ENTRY(THREAD_NETWORK_DATA_VERSION),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_OFF_MESH_ROUTES),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ON_MESH_NETS),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_PARENT),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_RLOC16),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_RLOC16_DEBUG_PASSTHRU),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_STABLE_LEADER_NETWORK_DATA),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_STABLE_NETWORK_DATA_VERSION),
#if OPENTHREAD_ENABLE_BORDER_ROUTER
NCP_GET_PROP_HANDLER_ENTRY(THREAD_NETWORK_DATA),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_STABLE_NETWORK_DATA),
#endif
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ACTIVE_DATASET),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_PENDING_DATASET),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_ADDRESS_TABLE),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_ICMP_PING_OFFLOAD),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_ICMP_PING_OFFLOAD_MODE),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_LL_ADDR),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_ML_PREFIX),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_ML_ADDR),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_MULTICAST_ADDRESS_TABLE),
NCP_GET_PROP_HANDLER_ENTRY(IPV6_ROUTE_TABLE),
#if OPENTHREAD_ENABLE_JAM_DETECTION
NCP_GET_PROP_HANDLER_ENTRY(JAM_DETECT_ENABLE),
NCP_GET_PROP_HANDLER_ENTRY(JAM_DETECTED),
NCP_GET_PROP_HANDLER_ENTRY(JAM_DETECT_RSSI_THRESHOLD),
NCP_GET_PROP_HANDLER_ENTRY(JAM_DETECT_WINDOW),
NCP_GET_PROP_HANDLER_ENTRY(JAM_DETECT_BUSY),
NCP_GET_PROP_HANDLER_ENTRY(JAM_DETECT_HISTORY_BITMAP),
#endif
#if OPENTHREAD_ENABLE_CHANNEL_MONITOR
NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MONITOR_SAMPLE_INTERVAL),
NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MONITOR_RSSI_THRESHOLD),
NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MONITOR_SAMPLE_WINDOW),
NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MONITOR_SAMPLE_COUNT),
NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MONITOR_CHANNEL_OCCUPANCY),
#endif
#if OPENTHREAD_ENABLE_LEGACY
NCP_GET_PROP_HANDLER_ENTRY(NEST_LEGACY_ULA_PREFIX),
NCP_GET_PROP_HANDLER_ENTRY(NEST_LEGACY_LAST_NODE_JOINED),
#endif
// MAC counters
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_PKT_TOTAL),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_PKT_ACK_REQ),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_PKT_ACKED),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_PKT_NO_ACK_REQ),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_PKT_DATA),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_PKT_DATA_POLL),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_PKT_BEACON),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_PKT_BEACON_REQ),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_PKT_OTHER),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_PKT_RETRY),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_PKT_UNICAST),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_PKT_BROADCAST),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_ERR_CCA),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_ERR_ABORT),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_PKT_TOTAL),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_PKT_DATA),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_PKT_DATA_POLL),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_PKT_BEACON),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_PKT_BEACON_REQ),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_PKT_OTHER),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_PKT_FILT_WL),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_PKT_FILT_DA),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_PKT_UNICAST),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_PKT_BROADCAST),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_ERR_EMPTY),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_ERR_UKWN_NBR),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_ERR_NVLD_SADDR),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_ERR_SECURITY),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_ERR_BAD_FCS),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_ERR_OTHER),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_PKT_DUP),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_ALL_MAC_COUNTERS),
// NCP counters
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_IP_SEC_TOTAL),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_IP_INSEC_TOTAL),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_IP_DROPPED),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_IP_SEC_TOTAL),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_IP_INSEC_TOTAL),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_IP_DROPPED),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_TX_SPINEL_TOTAL),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_SPINEL_TOTAL),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_RX_SPINEL_ERR),
// IP counters
NCP_GET_PROP_HANDLER_ENTRY(CNTR_IP_TX_SUCCESS),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_IP_RX_SUCCESS),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_IP_TX_FAILURE),
NCP_GET_PROP_HANDLER_ENTRY(CNTR_IP_RX_FAILURE),
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
#if OPENTHREAD_FTD
NCP_GET_PROP_HANDLER_ENTRY(NET_PSKC),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_LEADER_WEIGHT),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_CHILD_TABLE),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_CHILD_TABLE_ADDRESSES),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ROUTER_TABLE),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_LOCAL_LEADER_WEIGHT),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ROUTER_ROLE_ENABLED),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_CHILD_COUNT_MAX),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ROUTER_UPGRADE_THRESHOLD),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ROUTER_DOWNGRADE_THRESHOLD),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_CONTEXT_REUSE_DELAY),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_NETWORK_ID_TIMEOUT),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ROUTER_SELECTION_JITTER),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_PREFERRED_ROUTER_ID),
NCP_GET_PROP_HANDLER_ENTRY(THREAD_ADDRESS_CACHE_TABLE),
#if OPENTHREAD_ENABLE_COMMISSIONER
NCP_GET_PROP_HANDLER_ENTRY(THREAD_COMMISSIONER_ENABLED),
#endif
#if OPENTHREAD_ENABLE_TMF_PROXY
NCP_GET_PROP_HANDLER_ENTRY(THREAD_TMF_PROXY_ENABLED),
#endif
#if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
NCP_GET_PROP_HANDLER_ENTRY(THREAD_STEERING_DATA),
#endif
#if OPENTHREAD_ENABLE_CHANNEL_MANAGER
NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_NEW_CHANNEL),
NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_DELAY),
NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_SUPPORTED_CHANNELS),
NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_FAVORED_CHANNELS),
NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_CHANNEL_SELECT),
NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_AUTO_SELECT_ENABLED),
NCP_GET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_AUTO_SELECT_INTERVAL),
#endif
#endif // OPENTHREAD_FTD
#if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
NCP_GET_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_ENABLED),
#endif
};
#define NCP_SET_PROP_HANDLER_ENTRY(name) { SPINEL_PROP_##name, &NcpBase::SetPropertyHandler_##name }
const NcpBase::PropertyHandlerEntry NcpBase::mSetPropertyHandlerTable[] =
{
#if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
NCP_SET_PROP_HANDLER_ENTRY(MAC_15_4_SADDR),
NCP_SET_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_ENABLED),
NCP_SET_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_SHORT_ADDRESSES),
NCP_SET_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_EXTENDED_ADDRESSES),
NCP_SET_PROP_HANDLER_ENTRY(PHY_ENABLED),
#endif // OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
NCP_SET_PROP_HANDLER_ENTRY(POWER_STATE),
NCP_SET_PROP_HANDLER_ENTRY(MCU_POWER_STATE),
NCP_SET_PROP_HANDLER_ENTRY(UNSOL_UPDATE_FILTER),
NCP_SET_PROP_HANDLER_ENTRY(PHY_TX_POWER),
NCP_SET_PROP_HANDLER_ENTRY(PHY_CHAN),
NCP_SET_PROP_HANDLER_ENTRY(MAC_PROMISCUOUS_MODE),
NCP_SET_PROP_HANDLER_ENTRY(MAC_15_4_PANID),
NCP_SET_PROP_HANDLER_ENTRY(MAC_15_4_LADDR),
NCP_SET_PROP_HANDLER_ENTRY(MAC_RAW_STREAM_ENABLED),
#if OPENTHREAD_MTD || OPENTHREAD_FTD
NCP_SET_PROP_HANDLER_ENTRY(MAC_DATA_POLL_PERIOD),
NCP_SET_PROP_HANDLER_ENTRY(MAC_SCAN_MASK),
NCP_SET_PROP_HANDLER_ENTRY(MAC_SCAN_STATE),
NCP_SET_PROP_HANDLER_ENTRY(MAC_SCAN_PERIOD),
NCP_SET_PROP_HANDLER_ENTRY(NET_IF_UP),
NCP_SET_PROP_HANDLER_ENTRY(NET_STACK_UP),
NCP_SET_PROP_HANDLER_ENTRY(NET_ROLE),
NCP_SET_PROP_HANDLER_ENTRY(NET_NETWORK_NAME),
NCP_SET_PROP_HANDLER_ENTRY(NET_XPANID),
NCP_SET_PROP_HANDLER_ENTRY(NET_MASTER_KEY),
NCP_SET_PROP_HANDLER_ENTRY(NET_KEY_SEQUENCE_COUNTER),
NCP_SET_PROP_HANDLER_ENTRY(NET_KEY_SWITCH_GUARDTIME),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_ASSISTING_PORTS),
NCP_SET_PROP_HANDLER_ENTRY(STREAM_NET_INSECURE),
NCP_SET_PROP_HANDLER_ENTRY(STREAM_NET),
NCP_SET_PROP_HANDLER_ENTRY(IPV6_ML_PREFIX),
NCP_SET_PROP_HANDLER_ENTRY(IPV6_ICMP_PING_OFFLOAD),
NCP_SET_PROP_HANDLER_ENTRY(IPV6_ICMP_PING_OFFLOAD_MODE),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_RLOC16_DEBUG_PASSTHRU),
#if OPENTHREAD_ENABLE_MAC_FILTER
NCP_SET_PROP_HANDLER_ENTRY(MAC_WHITELIST),
NCP_SET_PROP_HANDLER_ENTRY(MAC_WHITELIST_ENABLED),
NCP_SET_PROP_HANDLER_ENTRY(MAC_BLACKLIST),
NCP_SET_PROP_HANDLER_ENTRY(MAC_BLACKLIST_ENABLED),
NCP_SET_PROP_HANDLER_ENTRY(MAC_FIXED_RSS),
#endif
NCP_SET_PROP_HANDLER_ENTRY(THREAD_MODE),
NCP_SET_PROP_HANDLER_ENTRY(NET_REQUIRE_JOIN_EXISTING),
NCP_SET_PROP_HANDLER_ENTRY(DEBUG_NCP_LOG_LEVEL),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_JOINER_FLAG),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_ENABLE_FILTERING),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_PANID),
#if OPENTHREAD_ENABLE_BORDER_ROUTER
NCP_SET_PROP_HANDLER_ENTRY(THREAD_ALLOW_LOCAL_NET_DATA_CHANGE),
#endif
#if OPENTHREAD_ENABLE_JAM_DETECTION
NCP_SET_PROP_HANDLER_ENTRY(JAM_DETECT_ENABLE),
NCP_SET_PROP_HANDLER_ENTRY(JAM_DETECT_RSSI_THRESHOLD),
NCP_SET_PROP_HANDLER_ENTRY(JAM_DETECT_WINDOW),
NCP_SET_PROP_HANDLER_ENTRY(JAM_DETECT_BUSY),
#endif
#if OPENTHREAD_ENABLE_LEGACY
NCP_SET_PROP_HANDLER_ENTRY(NEST_LEGACY_ULA_PREFIX),
#endif
NCP_SET_PROP_HANDLER_ENTRY(CNTR_RESET),
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
#if OPENTHREAD_FTD
NCP_SET_PROP_HANDLER_ENTRY(NET_PSKC),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_CHILD_TIMEOUT),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_NETWORK_ID_TIMEOUT),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_LOCAL_LEADER_WEIGHT),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_ROUTER_ROLE_ENABLED),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_CHILD_COUNT_MAX),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_ROUTER_UPGRADE_THRESHOLD),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_ROUTER_DOWNGRADE_THRESHOLD),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_CONTEXT_REUSE_DELAY),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_ROUTER_SELECTION_JITTER),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_PREFERRED_ROUTER_ID),
#if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
NCP_SET_PROP_HANDLER_ENTRY(THREAD_STEERING_DATA),
#endif
#if OPENTHREAD_ENABLE_TMF_PROXY
NCP_SET_PROP_HANDLER_ENTRY(THREAD_TMF_PROXY_ENABLED),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_TMF_PROXY_STREAM),
#endif
NCP_SET_PROP_HANDLER_ENTRY(THREAD_ACTIVE_DATASET),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_PENDING_DATASET),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_MGMT_ACTIVE_DATASET),
NCP_SET_PROP_HANDLER_ENTRY(THREAD_MGMT_PENDING_DATASET),
#if OPENTHREAD_ENABLE_CHANNEL_MANAGER
NCP_SET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_NEW_CHANNEL),
NCP_SET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_DELAY),
NCP_SET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_SUPPORTED_CHANNELS),
NCP_SET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_FAVORED_CHANNELS),
NCP_SET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_CHANNEL_SELECT),
NCP_SET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_AUTO_SELECT_ENABLED),
NCP_SET_PROP_HANDLER_ENTRY(CHANNEL_MANAGER_AUTO_SELECT_INTERVAL),
#endif
#endif // #if OPENTHREAD_FTD
};
#define NCP_INSERT_PROP_HANDLER_ENTRY(name) { SPINEL_PROP_##name, &NcpBase::InsertPropertyHandler_##name }
const NcpBase::PropertyHandlerEntry NcpBase::mInsertPropertyHandlerTable[] =
{
NCP_INSERT_PROP_HANDLER_ENTRY(UNSOL_UPDATE_FILTER),
#if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
NCP_INSERT_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_SHORT_ADDRESSES),
NCP_INSERT_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_EXTENDED_ADDRESSES),
#endif
#if OPENTHREAD_MTD || OPENTHREAD_FTD
NCP_INSERT_PROP_HANDLER_ENTRY(IPV6_ADDRESS_TABLE),
NCP_INSERT_PROP_HANDLER_ENTRY(IPV6_MULTICAST_ADDRESS_TABLE),
NCP_INSERT_PROP_HANDLER_ENTRY(THREAD_ASSISTING_PORTS),
#if OPENTHREAD_ENABLE_BORDER_ROUTER
NCP_INSERT_PROP_HANDLER_ENTRY(THREAD_OFF_MESH_ROUTES),
NCP_INSERT_PROP_HANDLER_ENTRY(THREAD_ON_MESH_NETS),
#endif
#if OPENTHREAD_ENABLE_MAC_FILTER
NCP_INSERT_PROP_HANDLER_ENTRY(MAC_WHITELIST),
NCP_INSERT_PROP_HANDLER_ENTRY(MAC_BLACKLIST),
NCP_INSERT_PROP_HANDLER_ENTRY(MAC_FIXED_RSS),
#endif
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
#if OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_COMMISSIONER
NCP_INSERT_PROP_HANDLER_ENTRY(THREAD_JOINERS),
#endif
#endif // OPENTHREAD_FTD
};
#define NCP_REMOVE_PROP_HANDLER_ENTRY(name) { SPINEL_PROP_##name, &NcpBase::RemovePropertyHandler_##name }
const NcpBase::PropertyHandlerEntry NcpBase::mRemovePropertyHandlerTable[] =
{
NCP_REMOVE_PROP_HANDLER_ENTRY(UNSOL_UPDATE_FILTER),
#if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
NCP_REMOVE_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_SHORT_ADDRESSES),
NCP_REMOVE_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_EXTENDED_ADDRESSES),
#endif
#if OPENTHREAD_MTD || OPENTHREAD_FTD
NCP_REMOVE_PROP_HANDLER_ENTRY(IPV6_ADDRESS_TABLE),
NCP_REMOVE_PROP_HANDLER_ENTRY(IPV6_MULTICAST_ADDRESS_TABLE),
#if OPENTHREAD_ENABLE_BORDER_ROUTER
NCP_REMOVE_PROP_HANDLER_ENTRY(THREAD_OFF_MESH_ROUTES),
NCP_REMOVE_PROP_HANDLER_ENTRY(THREAD_ON_MESH_NETS),
#endif
NCP_REMOVE_PROP_HANDLER_ENTRY(THREAD_ASSISTING_PORTS),
#if OPENTHREAD_ENABLE_MAC_FILTER
NCP_REMOVE_PROP_HANDLER_ENTRY(MAC_WHITELIST),
NCP_REMOVE_PROP_HANDLER_ENTRY(MAC_BLACKLIST),
NCP_REMOVE_PROP_HANDLER_ENTRY(MAC_FIXED_RSS),
#endif
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
#if OPENTHREAD_FTD
NCP_REMOVE_PROP_HANDLER_ENTRY(THREAD_ACTIVE_ROUTER_IDS),
#endif
};
// ----------------------------------------------------------------------------
// MARK: Utility Functions
// ----------------------------------------------------------------------------
spinel_status_t NcpBase::ThreadErrorToSpinelStatus(otError aError)
{
spinel_status_t ret;
switch (aError)
{
case OT_ERROR_NONE:
ret = SPINEL_STATUS_OK;
break;
case OT_ERROR_FAILED:
ret = SPINEL_STATUS_FAILURE;
break;
case OT_ERROR_DROP:
ret = SPINEL_STATUS_DROPPED;
break;
case OT_ERROR_NO_BUFS:
ret = SPINEL_STATUS_NOMEM;
break;
case OT_ERROR_BUSY:
ret = SPINEL_STATUS_BUSY;
break;
case OT_ERROR_PARSE:
ret = SPINEL_STATUS_PARSE_ERROR;
break;
case OT_ERROR_INVALID_ARGS:
ret = SPINEL_STATUS_INVALID_ARGUMENT;
break;
case OT_ERROR_NOT_IMPLEMENTED:
ret = SPINEL_STATUS_UNIMPLEMENTED;
break;
case OT_ERROR_INVALID_STATE:
ret = SPINEL_STATUS_INVALID_STATE;
break;
case OT_ERROR_NO_ACK:
ret = SPINEL_STATUS_NO_ACK;
break;
case OT_ERROR_CHANNEL_ACCESS_FAILURE:
ret = SPINEL_STATUS_CCA_FAILURE;
break;
case OT_ERROR_ALREADY:
ret = SPINEL_STATUS_ALREADY;
break;
case OT_ERROR_NOT_FOUND:
ret = SPINEL_STATUS_ITEM_NOT_FOUND;
break;
case OT_ERROR_DISABLED_FEATURE:
ret = SPINEL_STATUS_INVALID_COMMAND_FOR_PROP;
break;
default:
// Unknown error code. Wrap it as a Spinel status and return that.
ret = static_cast<spinel_status_t>(SPINEL_STATUS_STACK_NATIVE__BEGIN + static_cast<uint32_t>(aError));
break;
}
return ret;
}
static spinel_status_t ResetReasonToSpinelStatus(otPlatResetReason aReason)
{
spinel_status_t ret;
switch (aReason)
{
case OT_PLAT_RESET_REASON_POWER_ON:
ret = SPINEL_STATUS_RESET_POWER_ON;
break;
case OT_PLAT_RESET_REASON_EXTERNAL:
ret = SPINEL_STATUS_RESET_EXTERNAL;
break;
case OT_PLAT_RESET_REASON_SOFTWARE:
ret = SPINEL_STATUS_RESET_SOFTWARE;
break;
case OT_PLAT_RESET_REASON_FAULT:
ret = SPINEL_STATUS_RESET_FAULT;
break;
case OT_PLAT_RESET_REASON_CRASH:
ret = SPINEL_STATUS_RESET_CRASH;
break;
case OT_PLAT_RESET_REASON_ASSERT:
ret = SPINEL_STATUS_RESET_ASSERT;
break;
case OT_PLAT_RESET_REASON_WATCHDOG:
ret = SPINEL_STATUS_RESET_WATCHDOG;
break;
case OT_PLAT_RESET_REASON_OTHER:
ret = SPINEL_STATUS_RESET_OTHER;
break;
default:
ret = SPINEL_STATUS_RESET_UNKNOWN;
break;
}
return ret;
}
// ----------------------------------------------------------------------------
// MARK: Class Boilerplate
// ----------------------------------------------------------------------------
NcpBase *NcpBase::sNcpInstance = NULL;
NcpBase::NcpBase(Instance *aInstance):
mInstance(aInstance),
mTxFrameBuffer(mTxBuffer, sizeof(mTxBuffer)),
mEncoder(mTxFrameBuffer),
mDecoder(),
mHostPowerStateInProgress(false),
mLastStatus(SPINEL_STATUS_OK),
mSupportedChannelMask(OT_RADIO_SUPPORTED_CHANNELS),
mChannelMask(OT_RADIO_SUPPORTED_CHANNELS),
mScanPeriod(200), // ms
mDiscoveryScanJoinerFlag(false),
mDiscoveryScanEnableFiltering(false),
mDiscoveryScanPanId(0xffff),
mUpdateChangedPropsTask(*aInstance, &NcpBase::UpdateChangedProps, this),
mThreadChangedFlags(0),
mChangedPropsSet(),
mHostPowerState(SPINEL_HOST_POWER_STATE_ONLINE),
mHostPowerReplyFrameTag(NcpFrameBuffer::kInvalidTag),
mHostPowerStateHeader(0),
#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
mAllowPeekDelegate(NULL),
mAllowPokeDelegate(NULL),
#endif
mNextExpectedTid(0),
mResponseQueueHead(0),
mResponseQueueTail(0),
mAllowLocalNetworkDataChange(false),
mRequireJoinExistingNetwork(false),
mIsRawStreamEnabled(false),
mDisableStreamWrite(false),
mShouldEmitChildTableUpdate(false),
#if OPENTHREAD_FTD
mPreferredRouteId(0),
#endif
#if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
mCurTransmitTID(0),
mCurScanChannel(kInvalidScanChannel),
mSrcMatchEnabled(false),
#endif // OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
#if OPENTHREAD_MTD || OPENTHREAD_FTD
mInboundSecureIpFrameCounter(0),
mInboundInsecureIpFrameCounter(0),
mOutboundSecureIpFrameCounter(0),
mOutboundInsecureIpFrameCounter(0),
mDroppedOutboundIpFrameCounter(0),
mDroppedInboundIpFrameCounter(0),
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
mFramingErrorCounter(0),
mRxSpinelFrameCounter(0),
mRxSpinelOutOfOrderTidCounter(0),
mTxSpinelFrameCounter(0),
mDidInitialUpdates(false)
{
assert(mInstance != NULL);
sNcpInstance = this;
mTxFrameBuffer.SetFrameRemovedCallback(&NcpBase::HandleFrameRemovedFromNcpBuffer, this);
memset(&mResponseQueue, 0, sizeof(mResponseQueue));
#if OPENTHREAD_MTD || OPENTHREAD_FTD
otMessageQueueInit(&mMessageQueue);
otSetStateChangedCallback(mInstance, &NcpBase::HandleStateChanged, this);
otIp6SetReceiveCallback(mInstance, &NcpBase::HandleDatagramFromStack, this);
otIp6SetReceiveFilterEnabled(mInstance, true);
otLinkSetPcapCallback(mInstance, &NcpBase::HandleRawFrame, static_cast<void *>(this));
otIcmp6SetEchoMode(mInstance, OT_ICMP6_ECHO_HANDLER_DISABLED);
#if OPENTHREAD_FTD
otThreadSetChildTableCallback(mInstance, &NcpBase::HandleChildTableChanged);
#if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
memset(&mSteeringDataAddress, 0, sizeof(mSteeringDataAddress));
#endif
#endif // OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_LEGACY
mLegacyNodeDidJoin = false;
mLegacyHandlers = NULL;
memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix));
memset(&mLegacyLastJoinedNode, 0, sizeof(mLegacyLastJoinedNode));
#endif
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
mChangedPropsSet.AddLastStatus(SPINEL_STATUS_RESET_UNKNOWN);
mUpdateChangedPropsTask.Post();
}
NcpBase *NcpBase::GetNcpInstance(void)
{
return sNcpInstance;
}
// ----------------------------------------------------------------------------
// MARK: Serial Traffic Glue
// ----------------------------------------------------------------------------
NcpFrameBuffer::FrameTag NcpBase::GetLastOutboundFrameTag(void)
{
return mTxFrameBuffer.InFrameGetLastTag();
}
void NcpBase::HandleReceive(const uint8_t *aBuf, uint16_t aBufLength)
{
otError error = OT_ERROR_NONE;
uint8_t header = 0;
spinel_tid_t tid = 0;
mDisableStreamWrite = true;
// Initialize the decoder with the newly received spinel frame.
mDecoder.Init(aBuf, aBufLength);
// Receiving any message from the host has the side effect of transitioning the host power state to online.
mHostPowerState = SPINEL_HOST_POWER_STATE_ONLINE;
mHostPowerStateInProgress = false;
// Skip if there is no header byte to read or this isn't a spinel frame.
SuccessOrExit(mDecoder.ReadUint8(header));
VerifyOrExit((SPINEL_HEADER_FLAG & header) == SPINEL_HEADER_FLAG);
mRxSpinelFrameCounter++;
// We only support IID zero for now.
if (SPINEL_HEADER_GET_IID(header) != 0)
{
WriteLastStatusFrame(header, SPINEL_STATUS_INVALID_INTERFACE);
ExitNow();
}
error = HandleCommand(header);
if (error != OT_ERROR_NONE)
{
PrepareLastStatusResponse(header, ThreadErrorToSpinelStatus(error));
}
if (!IsResponseQueueEmpty())
{
// A response may have been prepared and queued for this command,
// so we attempt to send/write any queued responses. Note that
// if the response was prepared but cannot be sent now (not
// enough buffer space available), it will be attempted again
// from `HandleFrameRemovedFromNcpBuffer()` when buffer space
// becomes available.
IgnoreReturnValue(SendQueuedResponses());
}
// Check for out of sequence TIDs and update `mNextExpectedTid`,
tid = SPINEL_HEADER_GET_TID(header);
if ((mNextExpectedTid != 0) && (tid != mNextExpectedTid))
{
mRxSpinelOutOfOrderTidCounter++;
}
mNextExpectedTid = SPINEL_GET_NEXT_TID(tid);
exit:
mDisableStreamWrite = false;
}
void NcpBase::HandleFrameRemovedFromNcpBuffer(void *aContext, NcpFrameBuffer::FrameTag aFrameTag,
NcpFrameBuffer::Priority aPriority, NcpFrameBuffer *aNcpBuffer)
{
OT_UNUSED_VARIABLE(aNcpBuffer);
OT_UNUSED_VARIABLE(aPriority);
static_cast<NcpBase *>(aContext)->HandleFrameRemovedFromNcpBuffer(aFrameTag);
}
void NcpBase::HandleFrameRemovedFromNcpBuffer(NcpFrameBuffer::FrameTag aFrameTag)
{
if (mHostPowerStateInProgress == true)
{
if (aFrameTag == mHostPowerReplyFrameTag)
{
mHostPowerStateInProgress = false;
}
}
// A frame was removed from NCP TX buffer, so more space is now available.
// We attempt to write/send any pending frames. Order of the checks
// below is important: First any queued command responses, then
// any queued IPv6 datagram messages, then any asynchronous property updates.
// If a frame still can not fit in the available buffer, we exit immediately
// and wait for next time this callback is invoked (when another frame is
// removed and more buffer space becomes available).
SuccessOrExit(SendQueuedResponses());
#if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK
VendorHandleFrameRemovedFromNcpBuffer(aFrameTag);
#endif
// Check if `HOST_POWER_STATE` property update is required.
if (mHostPowerStateHeader)
{
SuccessOrExit(WritePropertyValueIsFrame(mHostPowerStateHeader, SPINEL_PROP_HOST_POWER_STATE));
mHostPowerStateHeader = 0;
if (mHostPowerState != SPINEL_HOST_POWER_STATE_ONLINE)
{
mHostPowerReplyFrameTag = GetLastOutboundFrameTag();
mHostPowerStateInProgress = true;
}
}
#if OPENTHREAD_MTD || OPENTHREAD_FTD
// Send any queued IPv6 datagram message.
SuccessOrExit(SendQueuedDatagramMessages());
#endif
// Send any unsolicited event-triggered property updates.
UpdateChangedProps();
exit:
return;
}
bool NcpBase::ShouldWakeHost(void)
{
return (mHostPowerState != SPINEL_HOST_POWER_STATE_ONLINE && !mHostPowerStateInProgress);
}
bool NcpBase::ShouldDeferHostSend(void)
{
return (mHostPowerState == SPINEL_HOST_POWER_STATE_DEEP_SLEEP && !mHostPowerStateInProgress);
}
void NcpBase::IncrementFrameErrorCounter(void)
{
mFramingErrorCounter++;
}
otError NcpBase::StreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen)
{
otError error = OT_ERROR_NONE;
uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
spinel_prop_key_t streamPropKey;
if (aStreamId == 0)
{
streamPropKey = SPINEL_PROP_STREAM_DEBUG;
}
else
{
streamPropKey = static_cast<spinel_prop_key_t>(aStreamId);
}
VerifyOrExit(!mDisableStreamWrite, error = OT_ERROR_INVALID_STATE);
VerifyOrExit(!mChangedPropsSet.IsPropertyFiltered(streamPropKey), error = OT_ERROR_INVALID_STATE);
// If there is a pending queued response we do not allow any new log
// stream writes. This is to ensure that log messages can not continue
// to use the NCP buffer space and block other spinel frames.
VerifyOrExit(IsResponseQueueEmpty(), error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, streamPropKey));
SuccessOrExit(error = mEncoder.WriteData(aDataPtr, static_cast<uint16_t>(aDataLen)));
SuccessOrExit(error = mEncoder.EndFrame());
exit:
if (error == OT_ERROR_NO_BUFS)
{
mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM);
mUpdateChangedPropsTask.Post();
}
return error;
}
uint8_t NcpBase::ConvertLogLevel(otLogLevel aLogLevel)
{
uint8_t spinelLogLevel = SPINEL_NCP_LOG_LEVEL_EMERG;
switch (aLogLevel)
{
case OT_LOG_LEVEL_NONE:
spinelLogLevel = SPINEL_NCP_LOG_LEVEL_EMERG;
break;
case OT_LOG_LEVEL_CRIT:
spinelLogLevel = SPINEL_NCP_LOG_LEVEL_CRIT;
break;
case OT_LOG_LEVEL_WARN:
spinelLogLevel = SPINEL_NCP_LOG_LEVEL_WARN;
break;
case OT_LOG_LEVEL_NOTE:
spinelLogLevel = SPINEL_NCP_LOG_LEVEL_NOTICE;
break;
case OT_LOG_LEVEL_INFO:
spinelLogLevel = SPINEL_NCP_LOG_LEVEL_INFO;
break;
case OT_LOG_LEVEL_DEBG:
spinelLogLevel = SPINEL_NCP_LOG_LEVEL_DEBUG;
break;
}
return spinelLogLevel;
}
unsigned int NcpBase::ConvertLogRegion(otLogRegion aLogRegion)
{
unsigned int spinelLogRegion = SPINEL_NCP_LOG_REGION_NONE;
switch (aLogRegion)
{
case OT_LOG_REGION_API:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_API;
break;
case OT_LOG_REGION_MLE:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_MLE;
break;
case OT_LOG_REGION_ARP:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_ARP;
break;
case OT_LOG_REGION_NET_DATA:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_NET_DATA;
break;
case OT_LOG_REGION_ICMP:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_ICMP;
break;
case OT_LOG_REGION_IP6:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_IP6;
break;
case OT_LOG_REGION_MAC:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_MAC;
break;
case OT_LOG_REGION_MEM:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_MEM;
break;
case OT_LOG_REGION_NCP:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_NCP;
break;
case OT_LOG_REGION_MESH_COP:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_MESH_COP;
break;
case OT_LOG_REGION_NET_DIAG:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_NET_DIAG;
break;
case OT_LOG_REGION_PLATFORM:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_PLATFORM;
break;
case OT_LOG_REGION_COAP:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_COAP;
break;
case OT_LOG_REGION_CLI:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_CLI;
break;
case OT_LOG_REGION_CORE:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_CORE;
break;
case OT_LOG_REGION_UTIL:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_UTIL;
break;
}
return spinelLogRegion;
}
void NcpBase::Log(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogString)
{
otError error = OT_ERROR_NONE;
uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
VerifyOrExit(!mDisableStreamWrite);
VerifyOrExit(!mChangedPropsSet.IsPropertyFiltered(SPINEL_PROP_STREAM_LOG));
// If there is a pending queued response we do not allow any new log
// stream writes. This is to ensure that log messages can not continue
// to use the NCP buffer space and block other spinel frames.
VerifyOrExit(IsResponseQueueEmpty(), error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_STREAM_LOG));
SuccessOrExit(error = mEncoder.WriteUtf8(aLogString));
SuccessOrExit(error = mEncoder.WriteUint8(ConvertLogLevel(aLogLevel)));
SuccessOrExit(error = mEncoder.WriteUintPacked(ConvertLogRegion(aLogRegion)));
SuccessOrExit(error = mEncoder.EndFrame());
exit:
if (error == OT_ERROR_NO_BUFS)
{
mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM);
mUpdateChangedPropsTask.Post();
}
}
#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
void NcpBase::RegisterPeekPokeDelagates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate,
otNcpDelegateAllowPeekPoke aAllowPokeDelegate)
{
mAllowPeekDelegate = aAllowPeekDelegate;
mAllowPokeDelegate = aAllowPokeDelegate;
}
#endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
// ----------------------------------------------------------------------------
// MARK: Raw frame handling
// ----------------------------------------------------------------------------
void NcpBase::HandleRawFrame(const otRadioFrame *aFrame, void *aContext)
{
static_cast<NcpBase *>(aContext)->HandleRawFrame(aFrame);
}
void NcpBase::HandleRawFrame(const otRadioFrame *aFrame)
{
uint16_t flags = 0;
uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
if (!mIsRawStreamEnabled)
{
goto exit;
}
if (aFrame->mDidTx)
{
flags |= SPINEL_MD_FLAG_TX;
}
// Append frame header and frame length
SuccessOrExit(mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_STREAM_RAW));
SuccessOrExit(mEncoder.WriteUint16(aFrame->mLength));
// Append the frame contents
SuccessOrExit(mEncoder.WriteData(aFrame->mPsdu, aFrame->mLength));
// Append metadata (rssi, etc)
SuccessOrExit(mEncoder.WriteInt8(aFrame->mInfo.mRxInfo.mRssi)); // RSSI
SuccessOrExit(mEncoder.WriteInt8(-128)); // Noise floor (Currently unused)
SuccessOrExit(mEncoder.WriteUint16(flags)); // Flags
SuccessOrExit(mEncoder.OpenStruct()); // PHY-data
// Empty for now
SuccessOrExit(mEncoder.CloseStruct());
SuccessOrExit(mEncoder.OpenStruct()); // Vendor-data
// Empty for now
SuccessOrExit(mEncoder.CloseStruct());
SuccessOrExit(mEncoder.EndFrame());
exit:
return;
}
// ----------------------------------------------------------------------------
// MARK: Spinel Response Handling
// ----------------------------------------------------------------------------
uint8_t NcpBase::GetWrappedResponseQueueIndex(uint8_t aPosition)
{
while (aPosition >= kResponseQueueSize)
{
aPosition -= kResponseQueueSize;
}
return aPosition;
}
otError NcpBase::PrepareResponse(uint8_t aHeader, bool aIsLastStatus, bool aIsGetResponse, unsigned int aKeyOrStatus)
{
otError error = OT_ERROR_NONE;
spinel_tid_t tid = SPINEL_HEADER_GET_TID(aHeader);
ResponseEntry *entry;
if (tid == 0)
{
// No response is required for TID zero. But we may emit a
// `LAST_STATUS` error status (if not filtered) for TID
// zero (e.g., for a dropped `STREAM_NET` set command).
if (aIsLastStatus)
{
mChangedPropsSet.AddLastStatus(static_cast<spinel_status_t>(aKeyOrStatus));
}
ExitNow();
}
if ((mResponseQueueTail - mResponseQueueHead) >= kResponseQueueSize)
{
// If there is no room a for a response, emit an unsolicited
// `DROPPED` error status to indicate a spinel response was
// dropped.
mChangedPropsSet.AddLastStatus(SPINEL_STATUS_DROPPED);
ExitNow(error = OT_ERROR_NO_BUFS);
}
// Transaction IDs are expected to come in sequence, if however, we
// get an out of sequence TID, check if we already have a response
// queued for this TID and if so mark the old entry as deleted.
if (tid != mNextExpectedTid)
{
for (uint8_t cur = mResponseQueueHead; cur < mResponseQueueTail; cur++)
{
entry = &mResponseQueue[GetWrappedResponseQueueIndex(cur)];
if (entry->mIsInUse && (entry->mTid == tid))
{
// Entry is just marked here and will be removed
// from `SendQueuedResponses()`.
entry->mIsInUse = false;
break;
}
}
}
// Add the new entry in the queue at tail.
entry = &mResponseQueue[GetWrappedResponseQueueIndex(mResponseQueueTail)];
entry->mTid = tid;
entry->mIsInUse = true;
entry->mIsLastStatus = aIsLastStatus;
entry->mIsGetResponse = aIsGetResponse;
entry->mPropKeyOrStatus = aKeyOrStatus;
mResponseQueueTail++;
exit:
return error;
}
otError NcpBase::SendQueuedResponses(void)
{
otError error = OT_ERROR_NONE;
while (mResponseQueueHead != mResponseQueueTail)
{
ResponseEntry &entry = mResponseQueue[mResponseQueueHead];
if (entry.mIsInUse)
{
uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
header |= static_cast<uint8_t>(entry.mTid << SPINEL_HEADER_TID_SHIFT);
if (entry.mIsLastStatus)
{
spinel_status_t status = static_cast<spinel_status_t>(entry.mPropKeyOrStatus);
SuccessOrExit(error = WriteLastStatusFrame(header, status));
}
else
{
spinel_prop_key_t propKey = static_cast<spinel_prop_key_t>(entry.mPropKeyOrStatus);
SuccessOrExit(error = WritePropertyValueIsFrame(header, propKey, entry.mIsGetResponse));
}
}
// Remove the response entry.
entry.mIsInUse = false;
mResponseQueueHead++;
if (mResponseQueueHead == kResponseQueueSize)
{
// Only when `head` wraps, the `tail` will be wrapped as well.
//
// This ensures that `tail` is always bigger than `head` and
// `(tail - head)` to correctly give the number of items in
// the queue.
mResponseQueueHead = 0;
mResponseQueueTail = GetWrappedResponseQueueIndex(mResponseQueueTail);
}
}
exit:
return error;
}
// ----------------------------------------------------------------------------
// MARK: Property/Status Changed
// ----------------------------------------------------------------------------
void NcpBase::UpdateChangedProps(Tasklet &aTasklet)
{
OT_UNUSED_VARIABLE(aTasklet);
GetNcpInstance()->UpdateChangedProps();
}
void NcpBase::UpdateChangedProps(void)
{
uint8_t numEntries;
spinel_prop_key_t propKey;
const ChangedPropsSet::Entry *entry;
#if OPENTHREAD_MTD || OPENTHREAD_FTD
ProcessThreadChangedFlags();
#endif
VerifyOrExit(!mChangedPropsSet.IsEmpty());
entry = mChangedPropsSet.GetSupportedEntries(numEntries);
for (uint8_t index = 0; index < numEntries; index++, entry++)
{
if (!mChangedPropsSet.IsEntryChanged(index))
{
continue;
}
propKey = entry->mPropKey;
if (propKey == SPINEL_PROP_LAST_STATUS)
{
spinel_status_t status = entry->mStatus;
if (status == SPINEL_STATUS_RESET_UNKNOWN)
{
status = ResetReasonToSpinelStatus(otPlatGetResetReason(mInstance));
}
SuccessOrExit(WriteLastStatusFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, status));
}
else if (mDidInitialUpdates)
{
SuccessOrExit(WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, propKey));
}
mChangedPropsSet.RemoveEntry(index);
VerifyOrExit(!mChangedPropsSet.IsEmpty());
}
exit:
mDidInitialUpdates = true;
return;
}
// ----------------------------------------------------------------------------
// MARK: Inbound Command Handler
// ----------------------------------------------------------------------------
otError NcpBase::HandleCommand(uint8_t aHeader)
{
otError error = OT_ERROR_NONE;
unsigned int command;
SuccessOrExit(error = mDecoder.ReadUintPacked(command));
switch (command)
{
case SPINEL_CMD_NOOP:
error = CommandHandler_NOOP(aHeader);
break;
case SPINEL_CMD_RESET:
error = CommandHandler_RESET(aHeader);
break;
case SPINEL_CMD_PROP_VALUE_GET:
case SPINEL_CMD_PROP_VALUE_SET:
case SPINEL_CMD_PROP_VALUE_INSERT:
case SPINEL_CMD_PROP_VALUE_REMOVE:
error = CommandHandler_PROP_VALUE_update(aHeader, command);
break;
#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
case SPINEL_CMD_PEEK:
error = CommandHandler_PEEK(aHeader);
break;
case SPINEL_CMD_POKE:
error = CommandHandler_POKE(aHeader);
break;
#endif
#if OPENTHREAD_MTD || OPENTHREAD_FTD
case SPINEL_CMD_NET_SAVE:
error = CommandHandler_NET_SAVE(aHeader);
break;
case SPINEL_CMD_NET_CLEAR:
error = CommandHandler_NET_CLEAR(aHeader);
break;
case SPINEL_CMD_NET_RECALL:
error = CommandHandler_NET_RECALL(aHeader);
break;
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
default:
#if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK
if (command >= SPINEL_CMD_VENDOR__BEGIN && command < SPINEL_CMD_VENDOR__END)
{
error = VendorCommandHandler(aHeader, command);
break;
}
#endif
error = PrepareLastStatusResponse(aHeader, SPINEL_STATUS_INVALID_COMMAND);
break;
}
exit:
return error;
}
// ----------------------------------------------------------------------------
// MARK: Property Get/Set/Insert/Remove Commands
// ----------------------------------------------------------------------------
NcpBase::PropertyHandler NcpBase::FindPropertyHandler(spinel_prop_key_t aKey, const PropertyHandlerEntry *aTableEntry,
size_t aTableLen)
{
PropertyHandler handler = NULL;
while (aTableLen--)
{
if (aTableEntry->mPropKey == aKey)
{
handler = aTableEntry->mHandler;
break;
}
aTableEntry++;
}
return handler;
}
NcpBase::PropertyHandler NcpBase::FindGetPropertyHandler(spinel_prop_key_t aKey)
{
return FindPropertyHandler(aKey, mGetPropertyHandlerTable, OT_ARRAY_LENGTH(mGetPropertyHandlerTable));
}
NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey)
{
return FindPropertyHandler(aKey, mSetPropertyHandlerTable, OT_ARRAY_LENGTH(mSetPropertyHandlerTable));
}
NcpBase::PropertyHandler NcpBase::FindInsertPropertyHandler(spinel_prop_key_t aKey)
{
return FindPropertyHandler(aKey, mInsertPropertyHandlerTable, OT_ARRAY_LENGTH(mInsertPropertyHandlerTable));
}
NcpBase::PropertyHandler NcpBase::FindRemovePropertyHandler(spinel_prop_key_t aKey)
{
return FindPropertyHandler(aKey, mRemovePropertyHandlerTable, OT_ARRAY_LENGTH(mRemovePropertyHandlerTable));
}
// Returns `true` and updates the `aError` on success.
bool NcpBase::HandlePropertySetForSpecialProperties(uint8_t aHeader, spinel_prop_key_t aKey, otError &aError)
{
bool didHandle = true;
// Here the properties that require special treatment are handled.
// These properties are expected to form/write the response from
// their set handler directly.
switch (aKey)
{
case SPINEL_PROP_HOST_POWER_STATE:
ExitNow(aError = SetPropertyHandler_HOST_POWER_STATE(aHeader));
#if OPENTHREAD_ENABLE_DIAG
case SPINEL_PROP_NEST_STREAM_MFG:
ExitNow(aError = SetPropertyHandler_NEST_STREAM_MFG(aHeader));
#endif
#if OPENTHREAD_FTD && OPENTHREAD_ENABLE_COMMISSIONER
case SPINEL_PROP_THREAD_COMMISSIONER_ENABLED:
ExitNow(aError = SetPropertyHandler_THREAD_COMMISSIONER_ENABLED(aHeader));
#endif
#if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
case SPINEL_PROP_STREAM_RAW:
ExitNow(aError = SetPropertyHandler_STREAM_RAW(aHeader));
#endif
default:
didHandle = false;
break;
}
exit:
return didHandle;
}
otError NcpBase::HandleCommandPropertySet(uint8_t aHeader, spinel_prop_key_t aKey)
{
otError error = OT_ERROR_NONE;
PropertyHandler handler = FindSetPropertyHandler(aKey);
if (handler != NULL)
{
mDisableStreamWrite = false;
error = (this->*handler)();
mDisableStreamWrite = true;
}
else
{
// If there is no "set" handler, check if this property is one of the
// ones that require different treatment.
bool didHandle = HandlePropertySetForSpecialProperties(aHeader, aKey, error);
VerifyOrExit(!didHandle);
#if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK
if (aKey >= SPINEL_PROP_VENDOR__BEGIN && aKey < SPINEL_PROP_VENDOR__END)
{
mDisableStreamWrite = false;
error = VendorSetPropertyHandler(aKey);
mDisableStreamWrite = true;
// An `OT_ERROR_NOT_FOUND` status from vendor handler indicates
// that it does not support the given property key. In that
// case, `didHandle` is set to `false` so a `LAST_STATUS` with
// `PROP_NOT_FOUND` is emitted. Otherwise, we fall through to
// prepare the response.
didHandle = (error != OT_ERROR_NOT_FOUND);
}
#endif
VerifyOrExit(didHandle, error = PrepareLastStatusResponse(aHeader, SPINEL_STATUS_PROP_NOT_FOUND));
}
if (error == OT_ERROR_NONE)
{
error = PrepareSetResponse(aHeader, aKey);
}
else
{
error = PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(error));
}
exit:
return error;
}
otError NcpBase::HandleCommandPropertyInsertRemove(uint8_t aHeader, spinel_prop_key_t aKey, unsigned int aCommand)
{
otError error = OT_ERROR_NONE;
PropertyHandler handler = NULL;
unsigned int responseCommand = 0;
const uint8_t *valuePtr;
uint16_t valueLen;
switch (aCommand)
{
case SPINEL_CMD_PROP_VALUE_INSERT:
handler = FindInsertPropertyHandler(aKey);
responseCommand = SPINEL_CMD_PROP_VALUE_INSERTED;
break;
case SPINEL_CMD_PROP_VALUE_REMOVE:
handler = FindRemovePropertyHandler(aKey);
responseCommand = SPINEL_CMD_PROP_VALUE_REMOVED;
break;
default:
assert(false);
break;
}
VerifyOrExit(handler != NULL, error = PrepareLastStatusResponse(aHeader, SPINEL_STATUS_PROP_NOT_FOUND));
// Save current read position in the decoder. Read the entire
// content as a data blob (which is used in forming the response
// in case of success), then reset the read position back so
// that the `PropertyHandler` method can parse the content.
mDecoder.SavePosition();
mDecoder.ReadData(valuePtr, valueLen);
mDecoder.ResetToSaved();
mDisableStreamWrite = false;
error = (this->*handler)();
mDisableStreamWrite = true;
VerifyOrExit(error == OT_ERROR_NONE, error = PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(error)));
error = WritePropertyValueInsertedRemovedFrame(aHeader, responseCommand, aKey, valuePtr, valueLen);
// If the full response cannot be written now, instead prepare
// a `LAST_STATUS(STATUS_OK)` update as response.
if (error != OT_ERROR_NONE)
{
error = PrepareLastStatusResponse(aHeader, SPINEL_STATUS_OK);
}
exit:
return error;
}
// ----------------------------------------------------------------------------
// MARK: Outbound Frame Methods
// ----------------------------------------------------------------------------
otError NcpBase::WriteLastStatusFrame(uint8_t aHeader, spinel_status_t aLastStatus)
{
otError error = OT_ERROR_NONE;
if (SPINEL_HEADER_GET_IID(aHeader) == 0)
{
mLastStatus = aLastStatus;
}
SuccessOrExit(error = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_LAST_STATUS));
SuccessOrExit(error = mEncoder.WriteUintPacked(aLastStatus));
SuccessOrExit(error = mEncoder.EndFrame());
exit:
return error;
}
otError NcpBase::WritePropertyValueIsFrame(uint8_t aHeader, spinel_prop_key_t aPropKey, bool aIsGetResponse)
{
otError error = OT_ERROR_NONE;
PropertyHandler handler = FindGetPropertyHandler(aPropKey);
if (handler != NULL)
{
SuccessOrExit(error = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PROP_VALUE_IS, aPropKey));
SuccessOrExit(error = (this->*handler)());
ExitNow(error = mEncoder.EndFrame());
}
#if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK
if (aPropKey >= SPINEL_PROP_VENDOR__BEGIN && aPropKey < SPINEL_PROP_VENDOR__END)
{
SuccessOrExit(error = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PROP_VALUE_IS, aPropKey));
error = VendorGetPropertyHandler(aPropKey);
// An `OT_ERROR_NOT_FOUND` status from vendor handler indicates that
// it did not support the given property key. In that case, we fall
// through to prepare a `LAST_STATUS` response.
if (error != OT_ERROR_NOT_FOUND)
{
SuccessOrExit(error);
ExitNow(error = mEncoder.EndFrame());
}
}
#endif
if (aIsGetResponse)
{
SuccessOrExit(error = WriteLastStatusFrame(aHeader, SPINEL_STATUS_PROP_NOT_FOUND));
}
else
{
// Send a STATUS_OK for "set" response to a property that
// has no corresponding get handler.
SuccessOrExit(error = WriteLastStatusFrame(aHeader, SPINEL_STATUS_OK));
}
exit:
return error;
}
otError NcpBase::WritePropertyValueInsertedRemovedFrame(uint8_t aHeader, unsigned int aResponseCommand,
spinel_prop_key_t aPropKey, const uint8_t *aValuePtr,
uint16_t aValueLen)
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = mEncoder.BeginFrame(aHeader, aResponseCommand, aPropKey));
SuccessOrExit(error = mEncoder.WriteData(aValuePtr, aValueLen));
SuccessOrExit(error = mEncoder.EndFrame());
exit:
return error;
}
// ----------------------------------------------------------------------------
// MARK: Individual Command Handlers
// ----------------------------------------------------------------------------
otError NcpBase::CommandHandler_NOOP(uint8_t aHeader)
{
return PrepareLastStatusResponse(aHeader, SPINEL_STATUS_OK);
}
otError NcpBase::CommandHandler_RESET(uint8_t aHeader)
{
otError error = OT_ERROR_NONE;
OT_UNUSED_VARIABLE(aHeader);
// Signal a platform reset. If implemented, this function
// shouldn't return.
otInstanceReset(mInstance);
#if OPENTHREAD_MTD || OPENTHREAD_FTD
// We only get to this point if the
// platform doesn't support resetting.
// In such a case we fake it.
otThreadSetEnabled(mInstance, false);
otIp6SetEnabled(mInstance, false);
#endif
error = WriteLastStatusFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_RESET_SOFTWARE);
if (error != OT_ERROR_NONE)
{
mChangedPropsSet.AddLastStatus(SPINEL_STATUS_RESET_UNKNOWN);
mUpdateChangedPropsTask.Post();
}
return error;
}
otError NcpBase::CommandHandler_PROP_VALUE_update(uint8_t aHeader, unsigned int aCommand)
{
otError error = OT_ERROR_NONE;
unsigned int propKey = 0;
error = mDecoder.ReadUintPacked(propKey);
VerifyOrExit(error == OT_ERROR_NONE, error = PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(error)));
switch (aCommand)
{
case SPINEL_CMD_PROP_VALUE_GET:
error = PrepareGetResponse(aHeader, static_cast<spinel_prop_key_t>(propKey));
break;
case SPINEL_CMD_PROP_VALUE_SET:
error = HandleCommandPropertySet(aHeader, static_cast<spinel_prop_key_t>(propKey));
break;
case SPINEL_CMD_PROP_VALUE_INSERT:
case SPINEL_CMD_PROP_VALUE_REMOVE:
error = HandleCommandPropertyInsertRemove(aHeader, static_cast<spinel_prop_key_t>(propKey), aCommand);
break;
default:
break;
}
exit:
return error;
}
#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
otError NcpBase::CommandHandler_PEEK(uint8_t aHeader)
{
otError parseError = OT_ERROR_NONE;
otError responseError = OT_ERROR_NONE;
uint32_t address;
uint16_t count;
SuccessOrExit(parseError = mDecoder.ReadUint32(address));
SuccessOrExit(parseError = mDecoder.ReadUint16(count));
VerifyOrExit(count != 0, parseError = OT_ERROR_INVALID_ARGS);
if (mAllowPeekDelegate != NULL)
{
VerifyOrExit(mAllowPeekDelegate(address, count), parseError = OT_ERROR_INVALID_ARGS);
}
SuccessOrExit(responseError = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PEEK_RET));
SuccessOrExit(responseError = mEncoder.WriteUint32(address));
SuccessOrExit(responseError = mEncoder.WriteUint16(count));
SuccessOrExit(responseError = mEncoder.WriteData(reinterpret_cast<const uint8_t *>(address), count));
SuccessOrExit(responseError = mEncoder.EndFrame());
exit:
if (parseError != OT_ERROR_NONE)
{
responseError = PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(parseError));
}
return responseError;
}
otError NcpBase::CommandHandler_POKE(uint8_t aHeader)
{
otError parseError = OT_ERROR_NONE;
uint32_t address;
uint16_t count;
const uint8_t *dataPtr = NULL;
uint16_t dataLen;
SuccessOrExit(parseError = mDecoder.ReadUint32(address));
SuccessOrExit(parseError = mDecoder.ReadUint16(count));
SuccessOrExit(parseError = mDecoder.ReadData(dataPtr, dataLen));
VerifyOrExit(count != 0, parseError = OT_ERROR_INVALID_ARGS);
VerifyOrExit(count <= dataLen, parseError = OT_ERROR_INVALID_ARGS);
if (mAllowPokeDelegate != NULL)
{
VerifyOrExit(mAllowPokeDelegate(address, count), parseError = OT_ERROR_INVALID_ARGS);
}
memcpy(reinterpret_cast<uint8_t *>(address), dataPtr, count);
exit:
return PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(parseError));
}
#endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
// ----------------------------------------------------------------------------
// MARK: Individual Property Getters and Setters
// ----------------------------------------------------------------------------
#if OPENTHREAD_ENABLE_DIAG
otError NcpBase::SetPropertyHandler_NEST_STREAM_MFG(uint8_t aHeader)
{
const char *string = NULL;
const char *output = NULL;
otError error = OT_ERROR_NONE;
error = mDecoder.ReadUtf8(string);
VerifyOrExit(error == OT_ERROR_NONE, error = WriteLastStatusFrame(aHeader, ThreadErrorToSpinelStatus(error)));
output = otDiagProcessCmdLine(string);
// Prepare the response
SuccessOrExit(error = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_NEST_STREAM_MFG));
SuccessOrExit(error = mEncoder.WriteUtf8(output));
SuccessOrExit(error = mEncoder.EndFrame());
exit:
return error;
}
#endif // OPENTHREAD_ENABLE_DIAG
otError NcpBase::GetPropertyHandler_PHY_ENABLED(void)
{
#if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
return mEncoder.WriteBool(otLinkRawIsEnabled(mInstance));
#else
return mEncoder.WriteBool(false);
#endif
}
otError NcpBase::GetPropertyHandler_PHY_CHAN(void)
{
return mEncoder.WriteUint8(otLinkGetChannel(mInstance));
}
otError NcpBase::SetPropertyHandler_PHY_CHAN(void)
{
unsigned int channel = 0;
otError error = OT_ERROR_NONE;
SuccessOrExit(error = mDecoder.ReadUintPacked(channel));
error = otLinkSetChannel(mInstance, static_cast<uint8_t>(channel));
#if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
SuccessOrExit(error);
// Make sure we are update the receiving channel if raw link is enabled and we have raw
// stream enabled already
if (otLinkRawIsEnabled(mInstance) && mIsRawStreamEnabled)
{
error = otLinkRawReceive(mInstance, &NcpBase::LinkRawReceiveDone);
}
#endif // OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
exit:
return error;
}
otError NcpBase::GetPropertyHandler_MAC_PROMISCUOUS_MODE(void)
{
return mEncoder.WriteUint8(otPlatRadioGetPromiscuous(mInstance)
? SPINEL_MAC_PROMISCUOUS_MODE_FULL
: SPINEL_MAC_PROMISCUOUS_MODE_OFF
);
}
otError NcpBase::SetPropertyHandler_MAC_PROMISCUOUS_MODE(void)
{
uint8_t mode = 0;
otError error = OT_ERROR_NONE;
SuccessOrExit(error = mDecoder.ReadUint8(mode));
switch (mode)
{
case SPINEL_MAC_PROMISCUOUS_MODE_OFF:
otPlatRadioSetPromiscuous(mInstance, false);
break;
case SPINEL_MAC_PROMISCUOUS_MODE_NETWORK:
case SPINEL_MAC_PROMISCUOUS_MODE_FULL:
otPlatRadioSetPromiscuous(mInstance, true);
break;
default:
error = OT_ERROR_INVALID_ARGS;
break;
}
exit:
return error;
}
otError NcpBase::GetPropertyHandler_MAC_15_4_PANID(void)
{
return mEncoder.WriteUint16(otLinkGetPanId(mInstance));
}
otError NcpBase::SetPropertyHandler_MAC_15_4_PANID(void)
{
uint16_t panid;
otError error = OT_ERROR_NONE;
SuccessOrExit(error = mDecoder.ReadUint16(panid));
error = otLinkSetPanId(mInstance, panid);
exit:
return error;
}
otError NcpBase::GetPropertyHandler_MAC_15_4_LADDR(void)
{
return mEncoder.WriteEui64(*otLinkGetExtendedAddress(mInstance));
}
otError NcpBase::SetPropertyHandler_MAC_15_4_LADDR(void)
{
const otExtAddress *extAddress;
otError error = OT_ERROR_NONE;
SuccessOrExit(error = mDecoder.ReadEui64(extAddress));
error = otLinkSetExtendedAddress(mInstance, extAddress);
exit:
return error;
}
otError NcpBase::GetPropertyHandler_MAC_15_4_SADDR(void)
{
return mEncoder.WriteUint16(otLinkGetShortAddress(mInstance));
}
otError NcpBase::GetPropertyHandler_MAC_RAW_STREAM_ENABLED(void)
{
return mEncoder.WriteBool(mIsRawStreamEnabled);
}
otError NcpBase::SetPropertyHandler_MAC_RAW_STREAM_ENABLED(void)
{
bool enabled = false;
otError error = OT_ERROR_NONE;
SuccessOrExit(error = mDecoder.ReadBool(enabled));
#if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
if (otLinkRawIsEnabled(mInstance))
{
if (enabled)
{
error = otLinkRawReceive(mInstance, &NcpBase::LinkRawReceiveDone);
}
else
{
error = otLinkRawSleep(mInstance);
}
}
#endif // OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
mIsRawStreamEnabled = enabled;
exit:
return error;
}
otError NcpBase::GetPropertyHandler_UNSOL_UPDATE_FILTER(void)
{
otError error = OT_ERROR_NONE;
uint8_t numEntries;
const ChangedPropsSet::Entry *entry;
entry = mChangedPropsSet.GetSupportedEntries(numEntries);
for (uint8_t index = 0; index < numEntries; index++, entry++)
{
if (mChangedPropsSet.IsEntryFiltered(index))
{
SuccessOrExit(error = mEncoder.WriteUintPacked(entry->mPropKey));
}
}
exit:
return error;
}
otError NcpBase::SetPropertyHandler_UNSOL_UPDATE_FILTER(void)
{
unsigned int propKey;
otError error = OT_ERROR_NONE;
// First clear the current filter.
mChangedPropsSet.ClearFilter();
while (mDecoder.GetRemainingLengthInStruct() > 0)
{
SuccessOrExit(error = mDecoder.ReadUintPacked(propKey));
IgnoreReturnValue(mChangedPropsSet.EnablePropertyFilter(static_cast<spinel_prop_key_t>(propKey), true));
}
exit:
// If we had an error, we may have actually changed
// the state of the filter, So we need to report
// those incomplete changes via an asynchronous
// change event.
if (error != OT_ERROR_NONE)
{
WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_UNSOL_UPDATE_FILTER);
}
return error;
}
otError NcpBase::InsertPropertyHandler_UNSOL_UPDATE_FILTER(void)
{
otError error = OT_ERROR_NONE;
unsigned int propKey;
SuccessOrExit(error = mDecoder.ReadUintPacked(propKey));
error = mChangedPropsSet.EnablePropertyFilter(static_cast<spinel_prop_key_t>(propKey), true);
exit:
return error;
}
otError NcpBase::RemovePropertyHandler_UNSOL_UPDATE_FILTER(void)
{
otError error = OT_ERROR_NONE;
unsigned int propKey;
SuccessOrExit(error = mDecoder.ReadUintPacked(propKey));
error = mChangedPropsSet.EnablePropertyFilter(static_cast<spinel_prop_key_t>(propKey), false);
exit:
return error;
}
otError NcpBase::GetPropertyHandler_LAST_STATUS(void)
{
return mEncoder.WriteUintPacked(mLastStatus);
}
otError NcpBase::GetPropertyHandler_PROTOCOL_VERSION(void)
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROTOCOL_VERSION_THREAD_MAJOR));
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROTOCOL_VERSION_THREAD_MINOR));
exit:
return error;
}
otError NcpBase::GetPropertyHandler_INTERFACE_TYPE(void)
{
return mEncoder.WriteUintPacked(SPINEL_PROTOCOL_TYPE_THREAD);
}
otError NcpBase::GetPropertyHandler_VENDOR_ID(void)
{
return mEncoder.WriteUintPacked(0); // Vendor ID. Zero for unknown.
}
otError NcpBase::GetPropertyHandler_CAPS(void)
{
otError error = OT_ERROR_NONE;
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_COUNTERS));
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_UNSOL_UPDATE_FILTER));
#if OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_MCU_POWER_STATE));
#endif
#if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_MAC_RAW));
#endif
#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_NCP_SPINEL)
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_OPENTHREAD_LOG_METADATA));
#endif
#if OPENTHREAD_MTD || OPENTHREAD_FTD
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_NET_THREAD_1_0));
#if OPENTHREAD_ENABLE_MAC_FILTER
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_MAC_WHITELIST));
#endif
#if OPENTHREAD_ENABLE_JAM_DETECTION
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_JAM_DETECT));
#endif
#if OPENTHREAD_ENABLE_CHANNEL_MONITOR
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_CHANNEL_MONITOR));
#endif
#if OPENTHREAD_ENABLE_CHANNEL_MANAGER && OPENTHREAD_FTD
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_CHANNEL_MANAGER));
#endif
#if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_ERROR_RATE_TRACKING));
#endif
#if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_OOB_STEERING_DATA));
#endif
#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_PEEK_POKE));
#endif
// TODO: Somehow get the following capability from the radio.
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_802_15_4_2450MHZ_OQPSK));
#if OPENTHREAD_CONFIG_MAX_CHILDREN > 0
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_ROLE_ROUTER));
#endif
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_ROLE_SLEEPY));
#if OPENTHREAD_ENABLE_LEGACY
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_NEST_LEGACY_INTERFACE));
#endif
#if OPENTHREAD_ENABLE_TMF_PROXY
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_THREAD_TMF_PROXY));
#endif
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
exit:
return error;
}
otError NcpBase::GetPropertyHandler_NCP_VERSION(void)
{
return mEncoder.WriteUtf8(otGetVersionString());
}
otError NcpBase::GetPropertyHandler_INTERFACE_COUNT(void)
{
return mEncoder.WriteUint8(1); // Only one interface for now
}
#if OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
otError NcpBase::GetPropertyHandler_MCU_POWER_STATE(void)
{
spinel_mcu_power_state_t state = SPINEL_MCU_POWER_STATE_ON;
switch (otPlatGetMcuPowerState(mInstance))
{
case OT_PLAT_MCU_POWER_STATE_ON:
state = SPINEL_MCU_POWER_STATE_ON;
break;
case OT_PLAT_MCU_POWER_STATE_LOW_POWER:
state = SPINEL_MCU_POWER_STATE_LOW_POWER;
break;
case OT_PLAT_MCU_POWER_STATE_OFF:
state = SPINEL_MCU_POWER_STATE_OFF;
break;
}
return mEncoder.WriteUint8(state);
}
otError NcpBase::SetPropertyHandler_MCU_POWER_STATE(void)
{
otError error = OT_ERROR_NONE;
otPlatMcuPowerState powerState;
uint8_t state;
SuccessOrExit(error = mDecoder.ReadUint8(state));
switch (state)
{
case SPINEL_MCU_POWER_STATE_ON:
powerState = OT_PLAT_MCU_POWER_STATE_ON;
break;
case SPINEL_MCU_POWER_STATE_LOW_POWER:
powerState = OT_PLAT_MCU_POWER_STATE_LOW_POWER;
break;
case SPINEL_MCU_POWER_STATE_OFF:
powerState = OT_PLAT_MCU_POWER_STATE_OFF;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
SuccessOrExit(error = otPlatSetMcuPowerState(mInstance, powerState));
#if OPENTHREAD_FTD || OPENTHREAD_MTD
// If the call `otPlatSetMcuPowerState()` was successful and the desire
// state is `OFF`, ensure to disable Thread (MLE) operation (and stop
// legacy) and also bring the IPv6 interface down.
if (powerState == OT_PLAT_MCU_POWER_STATE_OFF)
{
if (otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED)
{
otThreadSetEnabled(mInstance, false);
StopLegacy();
}
if (otIp6IsEnabled(mInstance))
{
otIp6SetEnabled(mInstance, false);
}
}
#endif // #if OPENTHREAD_FTD || OPENTHREAD_MTD
exit:
return error;
}
#else // OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
otError NcpBase::GetPropertyHandler_MCU_POWER_STATE(void)
{
return mEncoder.WriteUint8(SPINEL_MCU_POWER_STATE_ON);
}
otError NcpBase::SetPropertyHandler_MCU_POWER_STATE(void)
{
return OT_ERROR_DISABLED_FEATURE;
}
#endif // OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL
otError NcpBase::GetPropertyHandler_POWER_STATE(void)
{
return mEncoder.WriteUint8(SPINEL_POWER_STATE_ONLINE);
}
otError NcpBase::SetPropertyHandler_POWER_STATE(void)
{
return OT_ERROR_NOT_IMPLEMENTED;
}
otError NcpBase::GetPropertyHandler_HWADDR(void)
{
otExtAddress hwAddr;
otPlatRadioGetIeeeEui64(mInstance, hwAddr.m8);
return mEncoder.WriteEui64(hwAddr);
}
otError NcpBase::GetPropertyHandler_LOCK(void)
{
// TODO: Implement property lock (Needs API!)
return mEncoder.OverwriteWithLastStatusError(SPINEL_STATUS_UNIMPLEMENTED);
}
otError NcpBase::GetPropertyHandler_HOST_POWER_STATE(void)
{
return mEncoder.WriteUint8(mHostPowerState);
}
// Setting `HOST_POWER_STATE` is treated and implemented differently from other
// handlers as it requires two special behaviors (a) the response frame for the
// set operation should be tracked and only when it is delivered we can assume
// that host is sleep (b) the response is critical so if there is no spinel
// buffer to prepare the response, the current spinel header is saved to
// prepare and send the response as soon as buffer space becomes available.
otError NcpBase::SetPropertyHandler_HOST_POWER_STATE(uint8_t aHeader)
{
uint8_t powerState;
otError error = OT_ERROR_NONE;
error = mDecoder.ReadUint8(powerState);
if (error == OT_ERROR_NONE)
{
switch (powerState)
{
case SPINEL_HOST_POWER_STATE_OFFLINE:
case SPINEL_HOST_POWER_STATE_DEEP_SLEEP:
case SPINEL_HOST_POWER_STATE_LOW_POWER:
case SPINEL_HOST_POWER_STATE_ONLINE:
// Adopt the requested power state.
mHostPowerState = static_cast<spinel_host_power_state_t>(powerState);
break;
case SPINEL_HOST_POWER_STATE_RESERVED:
// Per the specification, treat this as synonymous with SPINEL_HOST_POWER_STATE_DEEP_SLEEP.
mHostPowerState = SPINEL_HOST_POWER_STATE_DEEP_SLEEP;
break;
default:
// Per the specification, treat unrecognized values as synonymous with SPINEL_HOST_POWER_STATE_LOW_POWER.
mHostPowerState = SPINEL_HOST_POWER_STATE_LOW_POWER;
break;
}
mHostPowerStateHeader = 0;
error = WritePropertyValueIsFrame(aHeader, SPINEL_PROP_HOST_POWER_STATE);
if (mHostPowerState != SPINEL_HOST_POWER_STATE_ONLINE)
{
if (error == OT_ERROR_NONE)
{
mHostPowerReplyFrameTag = GetLastOutboundFrameTag();
}
else
{
mHostPowerReplyFrameTag = NcpFrameBuffer::kInvalidTag;
}
mHostPowerStateInProgress = true;
}
if (error != OT_ERROR_NONE)
{
mHostPowerStateHeader = aHeader;
// The reply will be queued when buffer space becomes available
// in the NCP tx buffer so we return `success` to avoid sending a
// NOMEM status for the same tid through `mDroppedReplyTid` list.
error = OT_ERROR_NONE;
}
}
else
{
error = WriteLastStatusFrame(aHeader, ThreadErrorToSpinelStatus(error));
}
return error;
}
otError NcpBase::GetPropertyHandler_UNSOL_UPDATE_LIST(void)
{
otError error = OT_ERROR_NONE;
uint8_t numEntries;
const ChangedPropsSet::Entry *entry;
entry = mChangedPropsSet.GetSupportedEntries(numEntries);
for (uint8_t index = 0; index < numEntries; index++, entry++)
{
if (entry->mFilterable)
{
SuccessOrExit(error = mEncoder.WriteUintPacked(entry->mPropKey));
}
}
exit:
return error;
}
otError NcpBase::GetPropertyHandler_PHY_RX_SENSITIVITY(void)
{
return mEncoder.WriteInt8(otPlatRadioGetReceiveSensitivity(mInstance));
}
otError NcpBase::GetPropertyHandler_PHY_TX_POWER(void)
{
int8_t power;
otError error;
error = otPlatRadioGetTransmitPower(mInstance, &power);
if (error == OT_ERROR_NONE)
{
error = mEncoder.WriteInt8(power);
}
else
{
error = mEncoder.OverwriteWithLastStatusError(ThreadErrorToSpinelStatus(error));
}
return error;
}
otError NcpBase::SetPropertyHandler_PHY_TX_POWER(void)
{
int8_t txPower = 0;
otError error = OT_ERROR_NONE;
SuccessOrExit(error = mDecoder.ReadInt8(txPower));
error = otPlatRadioSetTransmitPower(mInstance, txPower);
exit:
return error;
}
otError NcpBase::GetPropertyHandler_DEBUG_TEST_ASSERT(void)
{
assert(false);
// We only get to this point if `assert(false)`
// does not cause an NCP reset on the platform.
// In such a case we return `false` as the
// property value to indicate this.
OT_UNREACHABLE_CODE(
return mEncoder.WriteBool(false);
)
}
otError NcpBase::GetPropertyHandler_DEBUG_TEST_WATCHDOG(void)
{
while (true)
;
OT_UNREACHABLE_CODE(
return OT_ERROR_NONE;
)
}
otError NcpBase::GetPropertyHandler_DEBUG_NCP_LOG_LEVEL(void)
{
return mEncoder.WriteUint8(ConvertLogLevel(otGetDynamicLogLevel(mInstance)));
}
otError NcpBase::SetPropertyHandler_DEBUG_NCP_LOG_LEVEL(void)
{
uint8_t spinelNcpLogLevel = 0;
otLogLevel logLevel;
otError error = OT_ERROR_NONE;
SuccessOrExit(error = mDecoder.ReadUint8(spinelNcpLogLevel));
switch (spinelNcpLogLevel)
{
case SPINEL_NCP_LOG_LEVEL_EMERG:
case SPINEL_NCP_LOG_LEVEL_ALERT:
logLevel = OT_LOG_LEVEL_NONE;
break;
case SPINEL_NCP_LOG_LEVEL_CRIT:
logLevel = OT_LOG_LEVEL_CRIT;
break;
case SPINEL_NCP_LOG_LEVEL_ERR:
case SPINEL_NCP_LOG_LEVEL_WARN:
logLevel = OT_LOG_LEVEL_WARN;
break;
case SPINEL_NCP_LOG_LEVEL_NOTICE:
logLevel = OT_LOG_LEVEL_NOTE;
break;
case SPINEL_NCP_LOG_LEVEL_INFO:
logLevel = OT_LOG_LEVEL_INFO;
break;
case SPINEL_NCP_LOG_LEVEL_DEBUG:
logLevel = OT_LOG_LEVEL_DEBG;
break;
default:
ExitNow(error = OT_ERROR_INVALID_ARGS);
break;
}
error = otSetDynamicLogLevel(mInstance, logLevel);
exit:
return error;
}
} // namespace Ncp
} // namespace ot
// ----------------------------------------------------------------------------
// MARK: Peek/Poke delegate API
// ----------------------------------------------------------------------------
otError otNcpRegisterPeekPokeDelagates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate,
otNcpDelegateAllowPeekPoke aAllowPokeDelegate)
{
otError error = OT_ERROR_NONE;
#if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance();
if (ncp != NULL)
{
ncp->RegisterPeekPokeDelagates(aAllowPeekDelegate, aAllowPokeDelegate);
}
#else
OT_UNUSED_VARIABLE(aAllowPeekDelegate);
OT_UNUSED_VARIABLE(aAllowPokeDelegate);
error = OT_ERROR_DISABLED_FEATURE;
#endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE
return error;
}
// ----------------------------------------------------------------------------
// MARK: Virtual Datastream I/O (Public API)
// ----------------------------------------------------------------------------
otError otNcpStreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen)
{
otError error = OT_ERROR_INVALID_STATE;
ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance();
if (ncp != NULL)
{
error = ncp->StreamWrite(aStreamId, aDataPtr, aDataLen);
}
return error;
}
extern "C" void otNcpPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs)
{
char logString[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE];
int charsWritten;
if ((charsWritten = vsnprintf(logString, sizeof(logString), aFormat, aArgs)) > 0)
{
if (charsWritten > static_cast<int>(sizeof(logString) - 1))
{
charsWritten = static_cast<int>(sizeof(logString) - 1);
}
otNcpStreamWrite(0, reinterpret_cast<uint8_t *>(logString), charsWritten);
}
OT_UNUSED_VARIABLE(aLogLevel);
OT_UNUSED_VARIABLE(aLogRegion);
}
#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_NCP_SPINEL)
extern "C" void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
{
va_list args;
char logString[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE];
ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance();
va_start(args, aFormat);
if (vsnprintf(logString, sizeof(logString), aFormat, args) > 0)
{
if (ncp != NULL)
{
ncp->Log(aLogLevel, aLogRegion, logString);
}
}
va_end(args);
}
#endif // (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_NCP_SPINEL)