| /* |
| * 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. |
| */ |
| |
| #include <openthread/config.h> |
| |
| #include "ncp_base.hpp" |
| |
| #include <stdarg.h> |
| #include <stdlib.h> |
| |
| #if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD |
| #include <openthread/tmf_proxy.h> |
| #endif |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| #include <openthread/border_router.h> |
| #endif |
| |
| #if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD |
| #include <meshcop/commissioner.hpp> |
| #endif |
| |
| #include <openthread/diag.h> |
| #include <openthread/icmp6.h> |
| |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| #include <openthread/jam_detection.h> |
| #endif |
| |
| #include <openthread/ncp.h> |
| #include <openthread/openthread.h> |
| |
| #if OPENTHREAD_FTD |
| #include <openthread/thread_ftd.h> |
| #endif |
| |
| #include <openthread/platform/misc.h> |
| #include <openthread/platform/radio.h> |
| |
| #include "openthread-instance.h" |
| #include "common/code_utils.hpp" |
| #include "common/debug.hpp" |
| #include "net/ip6.hpp" |
| |
| namespace ot { |
| |
| #define NCP_INVALID_SCAN_CHANNEL (-1) |
| |
| #define NCP_CHANGED_PLATFORM_RESET (1U << 31) |
| #define NCP_CHANGED_THREAD_ON_MESH_NETS (1U << 30) |
| #define NCP_CHANGED_THREAD_OFF_MESH_ROUTES (1U << 29) |
| |
| #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 |
| // ---------------------------------------------------------------------------- |
| |
| #define NCP_CMD_HANDLER_ENTRY(name) { SPINEL_CMD_##name, &NcpBase::CommandHandler_##name } |
| |
| const NcpBase::CommandHandlerEntry NcpBase::mCommandHandlerTable[] = |
| { |
| NCP_CMD_HANDLER_ENTRY(NOOP), |
| NCP_CMD_HANDLER_ENTRY(RESET), |
| NCP_CMD_HANDLER_ENTRY(PROP_VALUE_GET), |
| NCP_CMD_HANDLER_ENTRY(PROP_VALUE_SET), |
| NCP_CMD_HANDLER_ENTRY(PROP_VALUE_INSERT), |
| NCP_CMD_HANDLER_ENTRY(PROP_VALUE_REMOVE), |
| NCP_CMD_HANDLER_ENTRY(NET_SAVE), |
| NCP_CMD_HANDLER_ENTRY(NET_CLEAR), |
| NCP_CMD_HANDLER_ENTRY(NET_RECALL), |
| #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE |
| NCP_CMD_HANDLER_ENTRY(PEEK), |
| NCP_CMD_HANDLER_ENTRY(POKE), |
| #endif |
| }; |
| |
| #define NCP_GET_PROP_HANDLER_ENTRY(name) { SPINEL_PROP_##name, &NcpBase::GetPropertyHandler_##name } |
| #define NCP_GET_PROP_HANDLER_ENTRY_METHOD(name, method) { SPINEL_PROP_##name, &NcpBase::GetPropertyHandler_##method } |
| |
| const NcpBase::GetPropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] = |
| { |
| NCP_GET_PROP_HANDLER_ENTRY(LAST_STATUS), |
| NCP_GET_PROP_HANDLER_ENTRY(PROTOCOL_VERSION), |
| NCP_GET_PROP_HANDLER_ENTRY(INTERFACE_TYPE), |
| NCP_GET_PROP_HANDLER_ENTRY(VENDOR_ID), |
| NCP_GET_PROP_HANDLER_ENTRY(CAPS), |
| NCP_GET_PROP_HANDLER_ENTRY(NCP_VERSION), |
| NCP_GET_PROP_HANDLER_ENTRY(INTERFACE_COUNT), |
| NCP_GET_PROP_HANDLER_ENTRY(POWER_STATE), |
| NCP_GET_PROP_HANDLER_ENTRY(HWADDR), |
| NCP_GET_PROP_HANDLER_ENTRY(LOCK), |
| NCP_GET_PROP_HANDLER_ENTRY(HOST_POWER_STATE), |
| NCP_GET_PROP_HANDLER_ENTRY(PHY_ENABLED), |
| NCP_GET_PROP_HANDLER_ENTRY(PHY_FREQ), |
| NCP_GET_PROP_HANDLER_ENTRY(PHY_CHAN_SUPPORTED), |
| NCP_GET_PROP_HANDLER_ENTRY(PHY_CHAN), |
| NCP_GET_PROP_HANDLER_ENTRY(PHY_RSSI), |
| NCP_GET_PROP_HANDLER_ENTRY(PHY_TX_POWER), |
| NCP_GET_PROP_HANDLER_ENTRY(PHY_RX_SENSITIVITY), |
| 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_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(MAC_EXTENDED_ADDR), |
| NCP_GET_PROP_HANDLER_ENTRY(MAC_DATA_POLL_PERIOD), |
| NCP_GET_PROP_HANDLER_ENTRY(NET_SAVED), |
| NCP_GET_PROP_HANDLER_ENTRY(NET_IF_UP), |
| NCP_GET_PROP_HANDLER_ENTRY(NET_STACK_UP), |
| NCP_GET_PROP_HANDLER_ENTRY(NET_ROLE), |
| NCP_GET_PROP_HANDLER_ENTRY(NET_NETWORK_NAME), |
| NCP_GET_PROP_HANDLER_ENTRY(NET_XPANID), |
| NCP_GET_PROP_HANDLER_ENTRY(NET_MASTER_KEY), |
| NCP_GET_PROP_HANDLER_ENTRY(NET_KEY_SEQUENCE_COUNTER), |
| NCP_GET_PROP_HANDLER_ENTRY(NET_PARTITION_ID), |
| NCP_GET_PROP_HANDLER_ENTRY(NET_KEY_SWITCH_GUARDTIME), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_LEADER_ADDR), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_PARENT), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_NEIGHBOR_TABLE), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_LEADER_RID), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_LEADER_WEIGHT), |
| #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_NETWORK_DATA_VERSION), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_STABLE_NETWORK_DATA_VERSION), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_LEADER_NETWORK_DATA), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_STABLE_LEADER_NETWORK_DATA), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_OFF_MESH_ROUTES), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_ASSISTING_PORTS), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_ALLOW_LOCAL_NET_DATA_CHANGE), |
| #if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_COMMISSIONER_ENABLED), |
| #endif |
| #if OPENTHREAD_ENABLE_MAC_WHITELIST |
| NCP_GET_PROP_HANDLER_ENTRY(MAC_WHITELIST), |
| NCP_GET_PROP_HANDLER_ENTRY(MAC_WHITELIST_ENABLED), |
| NCP_GET_PROP_HANDLER_ENTRY(MAC_BLACKLIST), |
| NCP_GET_PROP_HANDLER_ENTRY(MAC_BLACKLIST_ENABLED), |
| #endif |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_MODE), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_CHILD_TIMEOUT), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_RLOC16), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_ON_MESH_NETS), |
| NCP_GET_PROP_HANDLER_ENTRY(NET_REQUIRE_JOIN_EXISTING), |
| NCP_GET_PROP_HANDLER_ENTRY(IPV6_ML_PREFIX), |
| NCP_GET_PROP_HANDLER_ENTRY(IPV6_ML_ADDR), |
| NCP_GET_PROP_HANDLER_ENTRY(IPV6_LL_ADDR), |
| NCP_GET_PROP_HANDLER_ENTRY(IPV6_ADDRESS_TABLE), |
| NCP_GET_PROP_HANDLER_ENTRY(IPV6_ROUTE_TABLE), |
| NCP_GET_PROP_HANDLER_ENTRY(IPV6_ICMP_PING_OFFLOAD), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_RLOC16_DEBUG_PASSTHRU), |
| 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(STREAM_NET), |
| #if OPENTHREAD_FTD |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_CHILD_TABLE), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_LOCAL_LEADER_WEIGHT), |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_ROUTER_ROLE_ENABLED), |
| NCP_GET_PROP_HANDLER_ENTRY(NET_PSKC), |
| 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), |
| #endif |
| #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_TMF_PROXY && OPENTHREAD_FTD |
| NCP_GET_PROP_HANDLER_ENTRY(THREAD_TMF_PROXY_ENABLED), |
| #endif |
| // MAC counters |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_TOTAL, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_ACK_REQ, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_ACKED, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_NO_ACK_REQ, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_DATA, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_DATA_POLL, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_BEACON, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_BEACON_REQ, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_OTHER, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_RETRY, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_UNICAST, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_PKT_BROADCAST, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_ERR_CCA, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_ERR_ABORT, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_TOTAL, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_DATA, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_DATA_POLL, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_BEACON, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_BEACON_REQ, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_OTHER, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_FILT_WL, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_FILT_DA, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_UNICAST, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_BROADCAST, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_ERR_EMPTY, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_ERR_UKWN_NBR, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_ERR_NVLD_SADDR, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_ERR_SECURITY, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_ERR_BAD_FCS, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_ERR_OTHER, MAC_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_PKT_DUP, MAC_CNTR), |
| // NCP counters |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_IP_SEC_TOTAL, NCP_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_IP_INSEC_TOTAL, NCP_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_IP_DROPPED, NCP_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_IP_SEC_TOTAL, NCP_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_IP_INSEC_TOTAL, NCP_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_IP_DROPPED, NCP_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_TX_SPINEL_TOTAL, NCP_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_SPINEL_TOTAL, NCP_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_RX_SPINEL_ERR, NCP_CNTR), |
| // IP counters |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_IP_TX_SUCCESS, IP_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_IP_RX_SUCCESS, IP_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_IP_TX_FAILURE, IP_CNTR), |
| NCP_GET_PROP_HANDLER_ENTRY_METHOD(CNTR_IP_RX_FAILURE, IP_CNTR), |
| |
| NCP_GET_PROP_HANDLER_ENTRY(MSG_BUFFER_COUNTERS), |
| NCP_GET_PROP_HANDLER_ENTRY(DEBUG_TEST_ASSERT), |
| NCP_GET_PROP_HANDLER_ENTRY(DEBUG_NCP_LOG_LEVEL), |
| #if OPENTHREAD_ENABLE_LEGACY |
| NCP_GET_PROP_HANDLER_ENTRY(NEST_LEGACY_ULA_PREFIX), |
| #endif |
| }; |
| |
| #define NCP_SET_PROP_HANDLER_ENTRY(name) { SPINEL_PROP_##name, &NcpBase::SetPropertyHandler_##name } |
| |
| const NcpBase::SetPropertyHandlerEntry NcpBase::mSetPropertyHandlerTable[] = |
| { |
| NCP_SET_PROP_HANDLER_ENTRY(POWER_STATE), |
| NCP_SET_PROP_HANDLER_ENTRY(HOST_POWER_STATE), |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| NCP_SET_PROP_HANDLER_ENTRY(PHY_ENABLED), |
| NCP_SET_PROP_HANDLER_ENTRY(MAC_15_4_SADDR), |
| NCP_SET_PROP_HANDLER_ENTRY(STREAM_RAW), |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API |
| 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_SCAN_MASK), |
| NCP_SET_PROP_HANDLER_ENTRY(MAC_SCAN_STATE), |
| NCP_SET_PROP_HANDLER_ENTRY(MAC_SCAN_PERIOD), |
| NCP_SET_PROP_HANDLER_ENTRY(MAC_15_4_PANID), |
| NCP_SET_PROP_HANDLER_ENTRY(MAC_15_4_LADDR), |
| NCP_SET_PROP_HANDLER_ENTRY(MAC_DATA_POLL_PERIOD), |
| NCP_SET_PROP_HANDLER_ENTRY(MAC_RAW_STREAM_ENABLED), |
| 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(THREAD_RLOC16_DEBUG_PASSTHRU), |
| #if OPENTHREAD_ENABLE_MAC_WHITELIST |
| 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), |
| #endif |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| 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), |
| #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_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 |
| #endif // #if OPENTHREAD_FTD |
| #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_TMF_PROXY && OPENTHREAD_FTD |
| NCP_SET_PROP_HANDLER_ENTRY(THREAD_TMF_PROXY_ENABLED), |
| NCP_SET_PROP_HANDLER_ENTRY(THREAD_TMF_PROXY_STREAM), |
| #endif |
| #if OPENTHREAD_ENABLE_DIAG |
| NCP_SET_PROP_HANDLER_ENTRY(NEST_STREAM_MFG), |
| #endif |
| #if OPENTHREAD_ENABLE_LEGACY |
| NCP_SET_PROP_HANDLER_ENTRY(NEST_LEGACY_ULA_PREFIX), |
| #endif |
| |
| #if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD |
| NCP_SET_PROP_HANDLER_ENTRY(THREAD_COMMISSIONER_ENABLED), |
| #endif |
| NCP_SET_PROP_HANDLER_ENTRY(CNTR_RESET), |
| }; |
| |
| #define NCP_INSERT_PROP_HANDLER_ENTRY(name) { SPINEL_PROP_##name, &NcpBase::InsertPropertyHandler_##name } |
| |
| const NcpBase::InsertPropertyHandlerEntry NcpBase::mInsertPropertyHandlerTable[] = |
| { |
| #if 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 |
| NCP_INSERT_PROP_HANDLER_ENTRY(IPV6_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_COMMISSIONER && OPENTHREAD_FTD |
| NCP_INSERT_PROP_HANDLER_ENTRY(THREAD_JOINERS), |
| #endif |
| #if OPENTHREAD_ENABLE_MAC_WHITELIST |
| NCP_INSERT_PROP_HANDLER_ENTRY(MAC_WHITELIST), |
| NCP_INSERT_PROP_HANDLER_ENTRY(MAC_BLACKLIST), |
| #endif |
| }; |
| |
| #define NCP_REMOVE_PROP_HANDLER_ENTRY(name) { SPINEL_PROP_##name, &NcpBase::RemovePropertyHandler_##name } |
| |
| const NcpBase::RemovePropertyHandlerEntry NcpBase::mRemovePropertyHandlerTable[] = |
| { |
| #if 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 |
| NCP_REMOVE_PROP_HANDLER_ENTRY(IPV6_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_WHITELIST |
| NCP_REMOVE_PROP_HANDLER_ENTRY(MAC_WHITELIST), |
| NCP_REMOVE_PROP_HANDLER_ENTRY(MAC_BLACKLIST), |
| #endif |
| #if OPENTHREAD_FTD |
| NCP_REMOVE_PROP_HANDLER_ENTRY(THREAD_ACTIVE_ROUTER_IDS), |
| #endif |
| }; |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Utility Functions |
| // ---------------------------------------------------------------------------- |
| |
| static spinel_status_t 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; |
| } |
| |
| static uint8_t BorderRouterConfigToFlagByte(const otBorderRouterConfig &aConfig) |
| { |
| uint8_t flags(0); |
| |
| if (aConfig.mPreferred) |
| { |
| flags |= SPINEL_NET_FLAG_PREFERRED; |
| } |
| |
| if (aConfig.mSlaac) |
| { |
| flags |= SPINEL_NET_FLAG_SLAAC; |
| } |
| |
| if (aConfig.mDhcp) |
| { |
| flags |= SPINEL_NET_FLAG_DHCP; |
| } |
| |
| if (aConfig.mDefaultRoute) |
| { |
| flags |= SPINEL_NET_FLAG_DEFAULT_ROUTE; |
| } |
| |
| if (aConfig.mConfigure) |
| { |
| flags |= SPINEL_NET_FLAG_CONFIGURE; |
| } |
| |
| if (aConfig.mOnMesh) |
| { |
| flags |= SPINEL_NET_FLAG_ON_MESH; |
| } |
| |
| flags |= (aConfig.mPreference << SPINEL_NET_FLAG_PREFERENCE_OFFSET); |
| |
| return flags; |
| } |
| |
| static uint8_t LinkFlagsToFlagByte(bool aRxOnWhenIdle, bool aSecureDataRequests, bool aDeviceType, bool aNetworkData) |
| { |
| uint8_t flags(0); |
| |
| if (aRxOnWhenIdle) |
| { |
| flags |= SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE; |
| } |
| |
| if (aSecureDataRequests) |
| { |
| flags |= SPINEL_THREAD_MODE_SECURE_DATA_REQUEST; |
| } |
| |
| if (aDeviceType) |
| { |
| flags |= SPINEL_THREAD_MODE_FULL_FUNCTION_DEV; |
| } |
| |
| if (aNetworkData) |
| { |
| flags |= SPINEL_THREAD_MODE_FULL_NETWORK_DATA; |
| } |
| |
| return flags; |
| } |
| |
| static uint8_t ExternalRoutePreferenceToFlagByte(int aPreference) |
| { |
| uint8_t flags; |
| |
| switch (aPreference) |
| { |
| case OT_ROUTE_PREFERENCE_LOW: |
| flags = SPINEL_ROUTE_PREFERENCE_LOW; |
| break; |
| |
| case OT_ROUTE_PREFERENCE_MED: |
| flags = SPINEL_ROUTE_PREFERENCE_MEDIUM; |
| break; |
| |
| case OT_ROUTE_PREFERENCE_HIGH: |
| flags = SPINEL_ROUTE_PREFERENCE_HIGH; |
| break; |
| |
| default: |
| flags = SPINEL_ROUTE_PREFERENCE_MEDIUM; |
| break; |
| } |
| |
| return flags; |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| static int FlagByteToExternalRoutePreference(uint8_t aFlags) |
| { |
| int route_preference = 0; |
| |
| switch (aFlags & SPINEL_NET_FLAG_PREFERENCE_MASK) |
| { |
| case SPINEL_ROUTE_PREFERENCE_HIGH: |
| route_preference = OT_ROUTE_PREFERENCE_HIGH; |
| break; |
| |
| case SPINEL_ROUTE_PREFERENCE_MEDIUM: |
| route_preference = OT_ROUTE_PREFERENCE_MED; |
| break; |
| |
| case SPINEL_ROUTE_PREFERENCE_LOW: |
| route_preference = OT_ROUTE_PREFERENCE_LOW; |
| break; |
| } |
| |
| return route_preference; |
| } |
| |
| #endif // OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Class Boilerplate |
| // ---------------------------------------------------------------------------- |
| |
| NcpBase *NcpBase::sNcpInstance = NULL; |
| |
| NcpBase::NcpBase(otInstance *aInstance): |
| mInstance(aInstance), |
| mTxFrameBuffer(mTxBuffer, sizeof(mTxBuffer)), |
| 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->mIp6.mTaskletScheduler, &NcpBase::UpdateChangedProps, this), |
| mChangedFlags(NCP_CHANGED_PLATFORM_RESET), |
| mShouldSignalEndOfScan(false), |
| mHostPowerState(SPINEL_HOST_POWER_STATE_ONLINE), |
| mHostPowerStateInProgress(false), |
| mHostPowerReplyFrameTag(NcpFrameBuffer::kInvalidTag), |
| mHostPowerStateHeader(0), |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| mShouldSignalJamStateChange(false), |
| #endif |
| #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE |
| mAllowPeekDelegate(NULL), |
| mAllowPokeDelegate(NULL), |
| #endif |
| mDroppedReplyTid(0), |
| mDroppedReplyTidBitSet(0), |
| mNextExpectedTid(0), |
| mAllowLocalNetworkDataChange(false), |
| mRequireJoinExistingNetwork(false), |
| mIsRawStreamEnabled(false), |
| mDisableStreamWrite(false), |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| mCurTransmitTID(0), |
| mCurReceiveChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL), |
| mCurScanChannel(NCP_INVALID_SCAN_CHANNEL), |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| mFramingErrorCounter(0), |
| mRxSpinelFrameCounter(0), |
| mRxSpinelOutOfOrderTidCounter(0), |
| mTxSpinelFrameCounter(0), |
| mInboundSecureIpFrameCounter(0), |
| mInboundInsecureIpFrameCounter(0), |
| mOutboundSecureIpFrameCounter(0), |
| mOutboundInsecureIpFrameCounter(0), |
| mDroppedOutboundIpFrameCounter(0), |
| mDroppedInboundIpFrameCounter(0) |
| { |
| assert(mInstance != NULL); |
| |
| sNcpInstance = this; |
| |
| mTxFrameBuffer.SetFrameRemovedCallback(&NcpBase::HandleFrameRemovedFromNcpBuffer, this); |
| |
| otSetStateChangedCallback(mInstance, &NcpBase::HandleNetifStateChanged, this); |
| otIp6SetReceiveCallback(mInstance, &NcpBase::HandleDatagramFromStack, this); |
| otIp6SetReceiveFilterEnabled(mInstance, true); |
| otLinkSetPcapCallback(mInstance, &NcpBase::HandleRawFrame, static_cast<void *>(this)); |
| otIcmp6SetEchoEnabled(mInstance, false); |
| |
| mUpdateChangedPropsTask.Post(); |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| mLegacyNodeDidJoin = false; |
| mLegacyHandlers = NULL; |
| memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix)); |
| #endif |
| } |
| |
| NcpBase *NcpBase::GetNcpInstance(void) |
| { |
| return sNcpInstance; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Outbound Frame methods |
| // ---------------------------------------------------------------------------- |
| |
| otError NcpBase::OutboundFrameBegin(void) |
| { |
| return mTxFrameBuffer.InFrameBegin(); |
| } |
| |
| otError NcpBase::OutboundFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength) |
| { |
| return mTxFrameBuffer.InFrameFeedData(aDataBuffer, aDataBufferLength); |
| } |
| |
| otError NcpBase::OutboundFrameFeedMessage(otMessage *aMessage) |
| { |
| return mTxFrameBuffer.InFrameFeedMessage(aMessage); |
| } |
| |
| otError NcpBase::OutboundFrameEnd(void) |
| { |
| return mTxFrameBuffer.InFrameEnd(); |
| } |
| |
| NcpFrameBuffer::FrameTag NcpBase::GetLastOutboundFrameTag(void) |
| { |
| return mTxFrameBuffer.InFrameGetLastTag(); |
| } |
| |
| #if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD |
| void NcpBase::HandleTmfProxyStream(otMessage *aMessage, uint16_t aLocator, uint16_t aPort, void *aContext) |
| { |
| static_cast<NcpBase *>(aContext)->HandleTmfProxyStream(aMessage, aLocator, aPort); |
| } |
| |
| void NcpBase::HandleTmfProxyStream(otMessage *aMessage, uint16_t aLocator, uint16_t aPort) |
| { |
| otError error = OT_ERROR_NONE; |
| uint16_t length = otMessageGetLength(aMessage); |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S, |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_THREAD_TMF_PROXY_STREAM, |
| length |
| )); |
| |
| SuccessOrExit(error = OutboundFrameFeedMessage(aMessage)); |
| |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_UINT16_S, |
| aLocator, |
| aPort |
| )); |
| |
| // Set the `aMessage` pointer to NULL to indicate that it does |
| // not need to be freed at the exit. The `aMessage` is now owned |
| // by the OutboundFrame and will be freed when the frame is either |
| // successfully sent and then removed, or if the frame gets |
| // discarded. |
| aMessage = NULL; |
| |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| |
| if (aMessage != NULL) |
| { |
| otMessageFree(aMessage); |
| } |
| |
| if (error != OT_ERROR_NONE) |
| { |
| SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_DROPPED); |
| } |
| } |
| #endif // OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Outbound Datagram Handling |
| // ---------------------------------------------------------------------------- |
| |
| void NcpBase::HandleDatagramFromStack(otMessage *aMessage, void *aContext) |
| { |
| static_cast<NcpBase *>(aContext)->HandleDatagramFromStack(aMessage); |
| } |
| |
| void NcpBase::HandleDatagramFromStack(otMessage *aMessage) |
| { |
| otError error = OT_ERROR_NONE; |
| bool isSecure = otMessageIsLinkSecurityEnabled(aMessage); |
| uint16_t length = otMessageGetLength(aMessage); |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S, |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| isSecure ? SPINEL_PROP_STREAM_NET : SPINEL_PROP_STREAM_NET_INSECURE, |
| length |
| )); |
| |
| SuccessOrExit(error = 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(error = OutboundFrameSend()); |
| |
| exit: |
| |
| if (aMessage != NULL) |
| { |
| otMessageFree(aMessage); |
| } |
| |
| if (error != OT_ERROR_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 otRadioFrame *aFrame, void *aContext) |
| { |
| static_cast<NcpBase *>(aContext)->HandleRawFrame(aFrame); |
| } |
| |
| void NcpBase::HandleRawFrame(const otRadioFrame *aFrame) |
| { |
| uint16_t flags = 0; |
| |
| if (!mIsRawStreamEnabled) |
| { |
| goto exit; |
| } |
| |
| SuccessOrExit(OutboundFrameBegin()); |
| |
| if (aFrame->mDidTX) |
| { |
| flags |= SPINEL_MD_FLAG_TX; |
| } |
| |
| // Append frame header and frame length |
| SuccessOrExit( |
| OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S, |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_STREAM_RAW, |
| aFrame->mLength |
| )); |
| |
| // Append the frame contents |
| SuccessOrExit(OutboundFrameFeedData(aFrame->mPsdu, aFrame->mLength)); |
| |
| // Append metadata (rssi, etc) |
| SuccessOrExit( |
| OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_INT8_S |
| SPINEL_DATATYPE_INT8_S |
| SPINEL_DATATYPE_UINT16_S |
| SPINEL_DATATYPE_STRUCT_S( // PHY-data |
| SPINEL_DATATYPE_NULL_S // Empty for now |
| ) |
| SPINEL_DATATYPE_STRUCT_S( // Vendor-data |
| SPINEL_DATATYPE_NULL_S // Empty for now |
| ), |
| aFrame->mPower, // TX Power |
| -128, // Noise Floor (Currently unused) |
| flags // Flags |
| |
| // Skip PHY and Vendor data for now |
| )); |
| |
| SuccessOrExit(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 *aResult) |
| { |
| otError error; |
| |
| if (aResult) |
| { |
| uint8_t flags = static_cast<uint8_t>(aResult->mVersion << SPINEL_BEACON_THREAD_FLAG_VERSION_SHIFT); |
| |
| if (aResult->mIsJoinable) |
| { |
| flags |= SPINEL_BEACON_THREAD_FLAG_JOINABLE; |
| } |
| |
| if (aResult->mIsNative) |
| { |
| flags |= SPINEL_BEACON_THREAD_FLAG_NATIVE; |
| } |
| |
| SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| SPINEL_PROP_MAC_SCAN_BEACON, |
| SPINEL_DATATYPE_MAC_SCAN_RESULT_S( |
| SPINEL_802_15_4_DATATYPE_MAC_SCAN_RESULT_V1_S, |
| SPINEL_NET_DATATYPE_MAC_SCAN_RESULT_V2_S |
| ), |
| aResult->mChannel, // Channel |
| aResult->mRssi, // RSSI |
| // "mac-layer data" |
| aResult->mExtAddress.m8, // laddr |
| 0xFFFF, // saddr, not given |
| aResult->mPanId, // panid |
| aResult->mLqi, // lqi |
| // "net-layer data" |
| SPINEL_PROTOCOL_TYPE_THREAD, // type |
| flags, // flags |
| aResult->mNetworkName.m8, // network name |
| aResult->mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE, // xpanid |
| aResult->mSteeringData.m8, aResult->mSteeringData.mLength // steering data |
| ); |
| } |
| else |
| { |
| // We are finished with the scan, so send out |
| // a property update indicating such. |
| error = 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 (error != OT_ERROR_NONE) |
| { |
| mShouldSignalEndOfScan = true; |
| } |
| } |
| } |
| |
| void NcpBase::HandleEnergyScanResult_Jump(otEnergyScanResult *aResult, void *aContext) |
| { |
| static_cast<NcpBase *>(aContext)->HandleEnergyScanResult(aResult); |
| } |
| |
| void NcpBase::HandleEnergyScanResult(otEnergyScanResult *aResult) |
| { |
| otError error; |
| |
| if (aResult) |
| { |
| SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| SPINEL_PROP_MAC_ENERGY_SCAN_RESULT, |
| ( |
| SPINEL_DATATYPE_UINT8_S // Channel |
| SPINEL_DATATYPE_INT8_S // Rssi |
| ), |
| aResult->mChannel, |
| aResult->mMaxRssi |
| ); |
| } |
| else |
| { |
| // We are finished with the scan, so send out |
| // a property update indicating such. |
| error = 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 (error != OT_ERROR_NONE) |
| { |
| mShouldSignalEndOfScan = true; |
| } |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Raw Link-Layer Datapath Glue |
| // ---------------------------------------------------------------------------- |
| |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| void NcpBase::LinkRawReceiveDone(otInstance *, otRadioFrame *aFrame, otError aError) |
| { |
| sNcpInstance->LinkRawReceiveDone(aFrame, aError); |
| } |
| |
| void NcpBase::LinkRawReceiveDone(otRadioFrame *aFrame, otError aError) |
| { |
| uint16_t flags = 0; |
| |
| SuccessOrExit(OutboundFrameBegin()); |
| |
| if (aFrame->mDidTX) |
| { |
| flags |= SPINEL_MD_FLAG_TX; |
| } |
| |
| // Append frame header and frame length |
| SuccessOrExit( |
| OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_UINT16_S, |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_STREAM_RAW, |
| (aError == OT_ERROR_NONE) ? aFrame->mLength : 0 |
| )); |
| |
| if (aError == OT_ERROR_NONE) |
| { |
| // Append the frame contents |
| SuccessOrExit(OutboundFrameFeedData(aFrame->mPsdu, aFrame->mLength)); |
| } |
| |
| // Append metadata (rssi, etc) |
| SuccessOrExit( |
| OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_INT8_S |
| SPINEL_DATATYPE_INT8_S |
| SPINEL_DATATYPE_UINT16_S |
| SPINEL_DATATYPE_STRUCT_S( // PHY-data |
| SPINEL_DATATYPE_UINT8_S // 802.15.4 channel |
| SPINEL_DATATYPE_UINT8_S // 802.15.4 LQI |
| ) |
| SPINEL_DATATYPE_STRUCT_S( // Vendor-data |
| SPINEL_DATATYPE_UINT_PACKED_S |
| ), |
| aFrame->mPower, // TX Power |
| -128, // Noise Floor (Currently unused) |
| flags, // Flags |
| aFrame->mChannel, // Receive channel |
| aFrame->mLqi, // Link quality indicator |
| aError // Receive error |
| )); |
| |
| SuccessOrExit(OutboundFrameSend()); |
| |
| exit: |
| return; |
| } |
| |
| void NcpBase::LinkRawTransmitDone(otInstance *, otRadioFrame *aFrame, bool aFramePending, otError aError) |
| { |
| sNcpInstance->LinkRawTransmitDone(aFrame, aFramePending, aError); |
| } |
| |
| void NcpBase::LinkRawTransmitDone(otRadioFrame *, bool aFramePending, otError aError) |
| { |
| if (mCurTransmitTID) |
| { |
| SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | mCurTransmitTID, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_LAST_STATUS, |
| SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_BOOL_S, |
| ThreadErrorToSpinelStatus(aError), |
| aFramePending |
| ); |
| |
| // Clear cached transmit TID |
| mCurTransmitTID = 0; |
| } |
| } |
| |
| void NcpBase::LinkRawEnergyScanDone(otInstance *, int8_t aEnergyScanMaxRssi) |
| { |
| sNcpInstance->LinkRawEnergyScanDone(aEnergyScanMaxRssi); |
| } |
| |
| void NcpBase::LinkRawEnergyScanDone(int8_t aEnergyScanMaxRssi) |
| { |
| SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_MAC_ENERGY_SCAN_RESULT, |
| SPINEL_DATATYPE_UINT8_S |
| SPINEL_DATATYPE_INT8_S, |
| mCurScanChannel, |
| aEnergyScanMaxRssi |
| ); |
| |
| // Clear current scan channel |
| mCurScanChannel = NCP_INVALID_SCAN_CHANNEL; |
| |
| // Make sure we are back listening on the original receive channel, |
| // since the energy scan could have been on a different channel. |
| otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone); |
| |
| // We are finished with the scan, so send out |
| // a property update indicating such. |
| SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_MAC_SCAN_STATE, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_SCAN_STATE_IDLE |
| ); |
| } |
| |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Address Table Changed Glue |
| // ---------------------------------------------------------------------------- |
| |
| void NcpBase::HandleNetifStateChanged(uint32_t aFlags, void *aContext) |
| { |
| NcpBase *obj = static_cast<NcpBase *>(aContext); |
| |
| obj->mChangedFlags |= aFlags; |
| |
| obj->mUpdateChangedPropsTask.Post(); |
| } |
| |
| void NcpBase::UpdateChangedProps(Tasklet &aTasklet) |
| { |
| OT_UNUSED_VARIABLE(aTasklet); |
| GetNcpInstance()->UpdateChangedProps(); |
| } |
| |
| void NcpBase::UpdateChangedProps(void) |
| { |
| while (mChangedFlags != 0) |
| { |
| if ((mChangedFlags & NCP_CHANGED_PLATFORM_RESET) != 0) |
| { |
| SuccessOrExit( |
| SendLastStatus( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| ResetReasonToSpinelStatus(otPlatGetResetReason(mInstance)) |
| )); |
| |
| mChangedFlags &= ~static_cast<uint32_t>(NCP_CHANGED_PLATFORM_RESET); |
| } |
| else if ((mChangedFlags & OT_CHANGED_THREAD_LL_ADDR) != 0) |
| { |
| SuccessOrExit( |
| HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_IPV6_LL_ADDR |
| )); |
| |
| mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_LL_ADDR); |
| } |
| else if ((mChangedFlags & OT_CHANGED_THREAD_ML_ADDR) != 0) |
| { |
| SuccessOrExit( |
| HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_IPV6_ML_ADDR |
| )); |
| |
| mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_ML_ADDR); |
| } |
| else if ((mChangedFlags & OT_CHANGED_THREAD_ROLE) != 0) |
| { |
| if (mRequireJoinExistingNetwork) |
| { |
| switch (otThreadGetDeviceRole(mInstance)) |
| { |
| case OT_DEVICE_ROLE_DETACHED: |
| case OT_DEVICE_ROLE_DISABLED: |
| break; |
| |
| default: |
| mRequireJoinExistingNetwork = false; |
| break; |
| } |
| |
| if ((otThreadGetDeviceRole(mInstance) == OT_DEVICE_ROLE_LEADER) |
| && otThreadIsSingleton(mInstance) |
| #if OPENTHREAD_ENABLE_LEGACY |
| && !mLegacyNodeDidJoin |
| #endif |
| ) |
| { |
| mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_PARTITION_ID); |
| otThreadSetEnabled(mInstance, false); |
| |
| // TODO: It would be nice to be able to indicate |
| // something more specific than SPINEL_STATUS_JOIN_FAILURE |
| // here, but it isn't clear how that would work |
| // with the current OpenThread API. |
| |
| SuccessOrExit( |
| SendLastStatus( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_STATUS_JOIN_FAILURE |
| )); |
| |
| SuccessOrExit( |
| HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_NET_STACK_UP |
| )); |
| } |
| |
| SuccessOrExit( |
| HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING |
| )); |
| } |
| |
| SuccessOrExit( |
| HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_NET_ROLE |
| )); |
| |
| mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_ROLE); |
| } |
| else if ((mChangedFlags & OT_CHANGED_THREAD_PARTITION_ID) != 0) |
| { |
| SuccessOrExit( |
| HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_NET_PARTITION_ID |
| )); |
| |
| mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_PARTITION_ID); |
| } |
| else if ((mChangedFlags & OT_CHANGED_THREAD_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_CHANGED_THREAD_KEY_SEQUENCE_COUNTER); |
| } |
| else if ((mChangedFlags & (OT_CHANGED_IP6_ADDRESS_ADDED | OT_CHANGED_IP6_ADDRESS_REMOVED)) != 0) |
| { |
| SuccessOrExit( |
| HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_IPV6_ADDRESS_TABLE |
| )); |
| |
| mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_IP6_ADDRESS_ADDED | OT_CHANGED_IP6_ADDRESS_REMOVED); |
| } |
| else if ((mChangedFlags & (OT_CHANGED_THREAD_CHILD_ADDED | OT_CHANGED_THREAD_CHILD_REMOVED)) != 0) |
| { |
| SuccessOrExit( |
| HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_THREAD_CHILD_TABLE |
| )); |
| |
| mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_CHILD_ADDED | OT_CHANGED_THREAD_CHILD_REMOVED); |
| } |
| else if ((mChangedFlags & OT_CHANGED_THREAD_NETDATA) != 0) |
| { |
| SuccessOrExit( |
| HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_THREAD_LEADER_NETWORK_DATA |
| )); |
| |
| mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_NETDATA); |
| |
| // If the network data is updated, after successfully sending (or queuing) the |
| // network data spinel message, we add `NCP_CHANGED_THREAD_ON_MESH_NETS` and |
| // `NCP_CHANGED_THREAD_OFF_MESH_ROUTES` to the `mChangedFlags` so that we |
| // separately send the list of on-mesh prefixes and off-mesh routes. |
| |
| mChangedFlags |= NCP_CHANGED_THREAD_ON_MESH_NETS | NCP_CHANGED_THREAD_OFF_MESH_ROUTES; |
| } |
| else if ((mChangedFlags & NCP_CHANGED_THREAD_ON_MESH_NETS) != 0) |
| { |
| SuccessOrExit( |
| HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_THREAD_ON_MESH_NETS |
| )); |
| |
| mChangedFlags &= ~static_cast<uint32_t>(NCP_CHANGED_THREAD_ON_MESH_NETS); |
| } |
| else if ((mChangedFlags & NCP_CHANGED_THREAD_OFF_MESH_ROUTES) != 0) |
| { |
| SuccessOrExit( |
| HandleCommandPropertyGet( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_PROP_THREAD_OFF_MESH_ROUTES |
| )); |
| |
| mChangedFlags &= ~static_cast<uint32_t>(NCP_CHANGED_THREAD_OFF_MESH_ROUTES); |
| } |
| else if ((mChangedFlags & (OT_CHANGED_THREAD_RLOC_ADDED | OT_CHANGED_THREAD_RLOC_REMOVED)) != 0) |
| { |
| mChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_RLOC_ADDED | OT_CHANGED_THREAD_RLOC_REMOVED); |
| } |
| } |
| |
| exit: |
| return; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Serial Traffic Glue |
| // ---------------------------------------------------------------------------- |
| |
| otError NcpBase::OutboundFrameSend(void) |
| { |
| otError error; |
| |
| SuccessOrExit(error = OutboundFrameEnd()); |
| |
| mTxSpinelFrameCounter++; |
| |
| exit: |
| return error; |
| } |
| |
| void NcpBase::HandleReceive(const uint8_t *aBuf, uint16_t aBufLength) |
| { |
| uint8_t header = 0; |
| unsigned int command = 0; |
| spinel_ssize_t parsedLength; |
| const uint8_t *argPtr = NULL; |
| unsigned int argLen = 0; |
| otError error = OT_ERROR_NONE; |
| spinel_tid_t tid = 0; |
| |
| parsedLength = spinel_datatype_unpack( |
| aBuf, |
| aBufLength, |
| SPINEL_DATATYPE_COMMAND_S SPINEL_DATATYPE_DATA_S, |
| &header, |
| &command, |
| &argPtr, |
| &argLen |
| ); |
| |
| tid = SPINEL_HEADER_GET_TID(header); |
| |
| // 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; |
| |
| if (parsedLength == aBufLength) |
| { |
| error = HandleCommand(header, command, argPtr, static_cast<uint16_t>(argLen)); |
| |
| // Check if we may have missed a `tid` in the sequence. |
| if ((mNextExpectedTid != 0) && (tid != mNextExpectedTid)) |
| { |
| mRxSpinelOutOfOrderTidCounter++; |
| } |
| |
| mNextExpectedTid = SPINEL_GET_NEXT_TID(tid); |
| } |
| else |
| { |
| error = SendLastStatus(header, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| if (error == OT_ERROR_NO_BUFS) |
| { |
| // 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::HandleFrameRemovedFromNcpBuffer(void *aContext, NcpFrameBuffer::FrameTag aFrameTag, |
| NcpFrameBuffer *aNcpBuffer) |
| { |
| OT_UNUSED_VARIABLE(aNcpBuffer); |
| static_cast<NcpBase *>(aContext)->HandleFrameRemovedFromNcpBuffer(aFrameTag); |
| } |
| |
| void NcpBase::HandleFrameRemovedFromNcpBuffer(NcpFrameBuffer::FrameTag aFrameTag) |
| { |
| if (mHostPowerStateInProgress == true) |
| { |
| if (aFrameTag == mHostPowerReplyFrameTag) |
| { |
| mHostPowerStateInProgress = false; |
| } |
| } |
| |
| // Space is now available in ncp tx frame buffer. |
| |
| while (mDroppedReplyTid != 0) |
| { |
| SuccessOrExit( |
| SendLastStatus( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | mDroppedReplyTid, |
| SPINEL_STATUS_NOMEM |
| )); |
| |
| mDroppedReplyTidBitSet &= ~(1 << mDroppedReplyTid); |
| |
| if (mDroppedReplyTidBitSet == 0) |
| { |
| mDroppedReplyTid = 0; |
| |
| break; |
| } |
| |
| do |
| { |
| mDroppedReplyTid = SPINEL_GET_NEXT_TID(mDroppedReplyTid); |
| } |
| while ((mDroppedReplyTidBitSet & (1 << mDroppedReplyTid)) == 0); |
| } |
| |
| if (mShouldSignalEndOfScan) |
| { |
| SuccessOrExit( |
| SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_MAC_SCAN_STATE, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_SCAN_STATE_IDLE |
| )); |
| |
| mShouldSignalEndOfScan = false; |
| } |
| |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| |
| if (mShouldSignalJamStateChange) |
| { |
| SuccessOrExit( |
| SendPropertyUpdate( |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_JAM_DETECTED, |
| SPINEL_DATATYPE_BOOL_S, |
| otJamDetectionGetState(mInstance) |
| )); |
| |
| mShouldSignalJamStateChange = false; |
| } |
| |
| #endif // OPENTHREAD_ENABLE_JAM_DETECTION |
| |
| if (mHostPowerStateHeader) |
| { |
| SuccessOrExit( |
| GetPropertyHandler_HOST_POWER_STATE( |
| mHostPowerStateHeader, |
| SPINEL_PROP_HOST_POWER_STATE |
| )); |
| |
| mHostPowerStateHeader = 0; |
| |
| if (mHostPowerState != SPINEL_HOST_POWER_STATE_ONLINE) |
| { |
| mHostPowerReplyFrameTag = GetLastOutboundFrameTag(); |
| mHostPowerStateInProgress = true; |
| } |
| } |
| |
| 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++; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Inbound Command Handlers |
| // ---------------------------------------------------------------------------- |
| |
| otError NcpBase::HandleCommand(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, uint16_t aArgLen) |
| { |
| unsigned i; |
| otError error = OT_ERROR_NONE; |
| |
| // Skip if this isn't a spinel frame |
| VerifyOrExit((SPINEL_HEADER_FLAG & aHeader) == SPINEL_HEADER_FLAG, error = OT_ERROR_INVALID_ARGS); |
| |
| // We only support IID zero for now. |
| VerifyOrExit( |
| SPINEL_HEADER_GET_IID(aHeader) == 0, |
| error = SendLastStatus(aHeader, SPINEL_STATUS_INVALID_INTERFACE) |
| ); |
| |
| for (i = 0; i < sizeof(mCommandHandlerTable) / sizeof(mCommandHandlerTable[0]); i++) |
| { |
| if (mCommandHandlerTable[i].mCommand == aCommand) |
| { |
| break; |
| } |
| } |
| |
| if (i < sizeof(mCommandHandlerTable) / sizeof(mCommandHandlerTable[0])) |
| { |
| error = (this->*mCommandHandlerTable[i].mHandler)(aHeader, aCommand, aArgPtr, aArgLen); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, SPINEL_STATUS_INVALID_COMMAND); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::HandleCommandPropertyGet(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| unsigned i; |
| otError error = OT_ERROR_NONE; |
| |
| for (i = 0; i < sizeof(mGetPropertyHandlerTable) / sizeof(mGetPropertyHandlerTable[0]); i++) |
| { |
| if (mGetPropertyHandlerTable[i].mPropKey == aKey) |
| { |
| break; |
| } |
| } |
| |
| if (i < sizeof(mGetPropertyHandlerTable) / sizeof(mGetPropertyHandlerTable[0])) |
| { |
| error = (this->*mGetPropertyHandlerTable[i].mHandler)(aHeader, aKey); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, SPINEL_STATUS_PROP_NOT_FOUND); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::HandleCommandPropertySet(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| unsigned i; |
| otError error = OT_ERROR_NONE; |
| |
| for (i = 0; i < sizeof(mSetPropertyHandlerTable) / sizeof(mSetPropertyHandlerTable[0]); i++) |
| { |
| if (mSetPropertyHandlerTable[i].mPropKey == aKey) |
| { |
| break; |
| } |
| } |
| |
| if (i < sizeof(mSetPropertyHandlerTable) / sizeof(mSetPropertyHandlerTable[0])) |
| { |
| error = (this->*mSetPropertyHandlerTable[i].mHandler)(aHeader, aKey, aValuePtr, aValueLen); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, SPINEL_STATUS_PROP_NOT_FOUND); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::HandleCommandPropertyInsert(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| unsigned i; |
| otError error = OT_ERROR_NONE; |
| |
| for (i = 0; i < sizeof(mInsertPropertyHandlerTable) / sizeof(mInsertPropertyHandlerTable[0]); i++) |
| { |
| if (mInsertPropertyHandlerTable[i].mPropKey == aKey) |
| { |
| break; |
| } |
| } |
| |
| if (i < sizeof(mInsertPropertyHandlerTable) / sizeof(mInsertPropertyHandlerTable[0])) |
| { |
| error = (this->*mInsertPropertyHandlerTable[i].mHandler)(aHeader, aKey, aValuePtr, aValueLen); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, SPINEL_STATUS_PROP_NOT_FOUND); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::HandleCommandPropertyRemove(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| unsigned i; |
| otError error = OT_ERROR_NONE; |
| |
| for (i = 0; i < sizeof(mRemovePropertyHandlerTable) / sizeof(mRemovePropertyHandlerTable[0]); i++) |
| { |
| if (mRemovePropertyHandlerTable[i].mPropKey == aKey) |
| { |
| break; |
| } |
| } |
| |
| if (i < sizeof(mRemovePropertyHandlerTable) / sizeof(mRemovePropertyHandlerTable[0])) |
| { |
| error = (this->*mRemovePropertyHandlerTable[i].mHandler)(aHeader, aKey, aValuePtr, aValueLen); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, SPINEL_STATUS_PROP_NOT_FOUND); |
| } |
| |
| return error; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Outbound Command Handlers |
| // ---------------------------------------------------------------------------- |
| |
| |
| otError NcpBase::SendLastStatus(uint8_t aHeader, spinel_status_t aLastStatus) |
| { |
| if (SPINEL_HEADER_GET_IID(aHeader) == 0) |
| { |
| mLastStatus = aLastStatus; |
| } |
| |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_LAST_STATUS, |
| SPINEL_DATATYPE_UINT_PACKED_S, |
| aLastStatus |
| ); |
| } |
| |
| otError NcpBase::SendPropertyUpdate(uint8_t aHeader, uint8_t aCommand, spinel_prop_key_t aKey, |
| const char *aPackFormat, ...) |
| { |
| otError error = OT_ERROR_NONE; |
| va_list args; |
| |
| va_start(args, aPackFormat); |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, aCommand, aKey)); |
| SuccessOrExit(error = OutboundFrameFeedVPacked(aPackFormat, args)); |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| va_end(args); |
| return error; |
| } |
| |
| otError NcpBase::SendPropertyUpdate(uint8_t aHeader, uint8_t aCommand, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, aCommand, aKey)); |
| SuccessOrExit(error = OutboundFrameFeedData(aValuePtr, aValueLen)); |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::SendPropertyUpdate(uint8_t aHeader, uint8_t aCommand, spinel_prop_key_t aKey, otMessage *aMessage) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, aCommand, aKey)); |
| SuccessOrExit(error = 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; |
| |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| |
| if (aMessage != NULL) |
| { |
| otMessageFree(aMessage); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::OutboundFrameFeedVPacked(const char *aPackFormat, va_list aArgs) |
| { |
| uint8_t buf[96]; |
| otError error = OT_ERROR_NO_BUFS; |
| spinel_ssize_t packedLen; |
| |
| packedLen = spinel_datatype_vpack(buf, sizeof(buf), aPackFormat, aArgs); |
| |
| if ((packedLen > 0) && (packedLen <= static_cast<spinel_ssize_t>(sizeof(buf)))) |
| { |
| error = OutboundFrameFeedData(buf, static_cast<uint16_t>(packedLen)); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::OutboundFrameFeedPacked(const char *aPackFormat, ...) |
| { |
| otError error; |
| va_list args; |
| |
| va_start(args, aPackFormat); |
| error = OutboundFrameFeedVPacked(aPackFormat, args); |
| va_end(args); |
| |
| return error; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Individual Command Handlers |
| // ---------------------------------------------------------------------------- |
| |
| otError NcpBase::CommandHandler_NOOP(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, uint16_t aArgLen) |
| { |
| OT_UNUSED_VARIABLE(aCommand); |
| OT_UNUSED_VARIABLE(aArgPtr); |
| OT_UNUSED_VARIABLE(aArgLen); |
| |
| return SendLastStatus(aHeader, SPINEL_STATUS_OK); |
| } |
| |
| otError NcpBase::CommandHandler_RESET(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, |
| uint16_t aArgLen) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| // We aren't using any of the arguments to this function. |
| OT_UNUSED_VARIABLE(aHeader); |
| OT_UNUSED_VARIABLE(aCommand); |
| OT_UNUSED_VARIABLE(aArgPtr); |
| OT_UNUSED_VARIABLE(aArgLen); |
| |
| // Signal a platform reset. If implemented, this function |
| // shouldn't return. |
| otInstanceReset(mInstance); |
| |
| // We only get to this point if the |
| // platform doesn't support resetting. |
| // In such a case we fake it. |
| |
| otThreadSetEnabled(mInstance, false); |
| otIp6SetEnabled(mInstance, false); |
| |
| error = SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_RESET_SOFTWARE); |
| |
| if (error != OT_ERROR_NONE) |
| { |
| mChangedFlags |= NCP_CHANGED_PLATFORM_RESET; |
| mUpdateChangedPropsTask.Post(); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::CommandHandler_PROP_VALUE_GET(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, |
| uint16_t aArgLen) |
| { |
| unsigned int propKey = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack(aArgPtr, aArgLen, SPINEL_DATATYPE_UINT_PACKED_S, &propKey); |
| |
| if (parsedLength > 0) |
| { |
| error = HandleCommandPropertyGet(aHeader, static_cast<spinel_prop_key_t>(propKey)); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| OT_UNUSED_VARIABLE(aCommand); |
| |
| return error; |
| } |
| |
| otError NcpBase::CommandHandler_PROP_VALUE_SET(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, |
| uint16_t aArgLen) |
| { |
| unsigned int propKey = 0; |
| spinel_ssize_t parsedLength; |
| const uint8_t *valuePtr; |
| unsigned int valueLen; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aArgPtr, |
| aArgLen, |
| SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_DATA_S, |
| &propKey, |
| &valuePtr, |
| &valueLen |
| ); |
| |
| if (parsedLength == aArgLen) |
| { |
| error = HandleCommandPropertySet( |
| aHeader, |
| static_cast<spinel_prop_key_t>(propKey), |
| valuePtr, |
| static_cast<uint16_t>(valueLen) |
| ); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| OT_UNUSED_VARIABLE(aCommand); |
| |
| return error; |
| } |
| |
| otError NcpBase::CommandHandler_PROP_VALUE_INSERT(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, |
| uint16_t aArgLen) |
| { |
| unsigned int propKey = 0; |
| spinel_ssize_t parsedLength; |
| const uint8_t *valuePtr; |
| unsigned int valueLen; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aArgPtr, |
| aArgLen, |
| SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_DATA_S, |
| &propKey, |
| &valuePtr, |
| &valueLen |
| ); |
| |
| if (parsedLength == aArgLen) |
| { |
| error = HandleCommandPropertyInsert( |
| aHeader, |
| static_cast<spinel_prop_key_t>(propKey), |
| valuePtr, |
| static_cast<uint16_t>(valueLen) |
| ); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| OT_UNUSED_VARIABLE(aCommand); |
| |
| return error; |
| } |
| |
| otError NcpBase::CommandHandler_PROP_VALUE_REMOVE(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, |
| uint16_t aArgLen) |
| { |
| unsigned int propKey = 0; |
| spinel_ssize_t parsedLength; |
| const uint8_t *valuePtr; |
| unsigned int valueLen; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aArgPtr, |
| aArgLen, |
| SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_DATA_S, |
| &propKey, |
| &valuePtr, |
| &valueLen |
| ); |
| |
| if (parsedLength == aArgLen) |
| { |
| error = HandleCommandPropertyRemove( |
| aHeader, |
| static_cast<spinel_prop_key_t>(propKey), |
| valuePtr, |
| static_cast<uint16_t>(valueLen) |
| ); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| OT_UNUSED_VARIABLE(aCommand); |
| |
| return error; |
| } |
| |
| otError NcpBase::CommandHandler_NET_SAVE(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, |
| uint16_t aArgLen) |
| { |
| OT_UNUSED_VARIABLE(aCommand); |
| OT_UNUSED_VARIABLE(aArgPtr); |
| OT_UNUSED_VARIABLE(aArgLen); |
| |
| return SendLastStatus(aHeader, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| otError NcpBase::CommandHandler_NET_CLEAR(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, |
| uint16_t aArgLen) |
| { |
| OT_UNUSED_VARIABLE(aCommand); |
| OT_UNUSED_VARIABLE(aArgPtr); |
| OT_UNUSED_VARIABLE(aArgLen); |
| |
| return SendLastStatus(aHeader, ThreadErrorToSpinelStatus(otInstanceErasePersistentInfo(mInstance))); |
| } |
| |
| otError NcpBase::CommandHandler_NET_RECALL(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, |
| uint16_t aArgLen) |
| { |
| OT_UNUSED_VARIABLE(aCommand); |
| OT_UNUSED_VARIABLE(aArgPtr); |
| OT_UNUSED_VARIABLE(aArgLen); |
| |
| return SendLastStatus(aHeader, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE |
| |
| otError NcpBase::CommandHandler_PEEK(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, uint16_t aArgLen) |
| { |
| spinel_ssize_t parsedLength; |
| uint32_t address; |
| uint16_t count; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aArgPtr, |
| aArgLen, |
| ( |
| SPINEL_DATATYPE_UINT32_S // Address |
| SPINEL_DATATYPE_UINT16_S // Count |
| ), |
| &address, |
| &count |
| ); |
| |
| VerifyOrExit(parsedLength == aArgLen, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| VerifyOrExit(count != 0, spinelError = SPINEL_STATUS_INVALID_ARGUMENT); |
| |
| if (mAllowPeekDelegate != NULL) |
| { |
| VerifyOrExit(mAllowPeekDelegate(address, count), spinelError = SPINEL_STATUS_INVALID_ARGUMENT); |
| } |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| ( |
| SPINEL_DATATYPE_COMMAND_S // Header and Command |
| SPINEL_DATATYPE_UINT32_S // Address |
| SPINEL_DATATYPE_UINT16_S // Count |
| ), |
| aHeader, |
| SPINEL_CMD_PEEK_RET, |
| address, |
| count |
| )); |
| SuccessOrExit(error = OutboundFrameFeedData(reinterpret_cast<const uint8_t *>(address), count)); |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| OT_UNUSED_VARIABLE(aCommand); |
| |
| return error; |
| } |
| |
| otError NcpBase::CommandHandler_POKE(uint8_t aHeader, unsigned int aCommand, const uint8_t *aArgPtr, uint16_t aArgLen) |
| { |
| spinel_ssize_t parsedLength; |
| uint32_t address; |
| uint16_t count; |
| const uint8_t *dataPtr = NULL; |
| spinel_size_t dataLen; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aArgPtr, |
| aArgLen, |
| ( |
| SPINEL_DATATYPE_UINT32_S // Address |
| SPINEL_DATATYPE_UINT16_S // Count |
| SPINEL_DATATYPE_DATA_S // Bytes |
| ), |
| &address, |
| &count, |
| &dataPtr, |
| &dataLen |
| ); |
| |
| VerifyOrExit(parsedLength == aArgLen, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| VerifyOrExit(count != 0, spinelError = SPINEL_STATUS_INVALID_ARGUMENT); |
| VerifyOrExit(count <= dataLen, spinelError = SPINEL_STATUS_INVALID_ARGUMENT); |
| |
| if (mAllowPokeDelegate != NULL) |
| { |
| VerifyOrExit(mAllowPokeDelegate(address, count), spinelError = SPINEL_STATUS_INVALID_ARGUMENT); |
| } |
| |
| memcpy(reinterpret_cast<uint8_t *>(address), dataPtr, count); |
| |
| exit: |
| error = SendLastStatus(aHeader, spinelError); |
| |
| OT_UNUSED_VARIABLE(aCommand); |
| |
| return error; |
| } |
| |
| #endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Individual Property Getters |
| // ---------------------------------------------------------------------------- |
| |
| |
| otError NcpBase::GetPropertyHandler_LAST_STATUS(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate(aHeader, SPINEL_CMD_PROP_VALUE_IS, aKey, SPINEL_DATATYPE_UINT_PACKED_S, mLastStatus); |
| } |
| |
| otError NcpBase::GetPropertyHandler_PROTOCOL_VERSION(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| ( |
| SPINEL_DATATYPE_UINT_PACKED_S // Major |
| SPINEL_DATATYPE_UINT_PACKED_S // Minor |
| ), |
| SPINEL_PROTOCOL_VERSION_THREAD_MAJOR, |
| SPINEL_PROTOCOL_VERSION_THREAD_MINOR |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_INTERFACE_TYPE(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT_PACKED_S, |
| SPINEL_PROTOCOL_TYPE_THREAD |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_VENDOR_ID(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT_PACKED_S, |
| 0 // Vendor ID. Zero for unknown. |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CAPS(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S, |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey |
| )); |
| |
| // Begin adding capabilities ////////////////////////////////////////////// |
| |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_NET_THREAD_1_0)); |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_COUNTERS)); |
| |
| #if OPENTHREAD_ENABLE_MAC_WHITELIST |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_MAC_WHITELIST)); |
| #endif |
| |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_MAC_RAW)); |
| #endif |
| |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_JAM_DETECT)); |
| #endif |
| |
| #if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_OOB_STEERING_DATA)); |
| #endif |
| |
| #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_PEEK_POKE)); |
| #endif |
| |
| // TODO: Somehow get the following capability from the radio. |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_802_15_4_2450MHZ_OQPSK)); |
| |
| #if OPENTHREAD_CONFIG_MAX_CHILDREN > 0 |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_ROLE_ROUTER)); |
| #endif |
| |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_ROLE_SLEEPY)); |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_NEST_LEGACY_INTERFACE)); |
| #endif |
| |
| #if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT_PACKED_S, SPINEL_CAP_THREAD_TMF_PROXY)); |
| #endif |
| |
| // End adding capabilities ///////////////////////////////////////////////// |
| |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_NCP_VERSION(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UTF8_S, |
| otGetVersionString() |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_INTERFACE_COUNT(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| 1 // Only one interface for now |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_POWER_STATE(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| // Always online at the moment |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_POWER_STATE_ONLINE |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_HWADDR(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otExtAddress hwAddr; |
| otLinkGetFactoryAssignedIeeeEui64(mInstance, &hwAddr); |
| |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_EUI64_S, |
| hwAddr.m8 |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_LOCK(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| // TODO: Implement property lock (Needs API!) |
| OT_UNUSED_VARIABLE(aKey); |
| |
| return SendLastStatus(aHeader, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| otError NcpBase::GetPropertyHandler_HOST_POWER_STATE(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| mHostPowerState |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_PHY_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| otLinkRawIsEnabled(mInstance) |
| #else |
| false |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_PHY_FREQ(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| uint32_t freq_khz(0); |
| const uint8_t chan(otLinkGetChannel(mInstance)); |
| |
| if (chan == 0) |
| { |
| freq_khz = 868300; |
| } |
| else if (chan < 11) |
| { |
| freq_khz = 906000 - (2000 * 1) + 2000 * (chan); |
| } |
| else if (chan < 26) |
| { |
| freq_khz = 2405000 - (5000 * 11) + 5000 * (chan); |
| } |
| |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT32_S, |
| freq_khz |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_PHY_CHAN_SUPPORTED(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return GetPropertyHandler_ChannelMaskHelper(aHeader, aKey, mSupportedChannelMask); |
| } |
| |
| otError NcpBase::GetPropertyHandler_PHY_CHAN(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| otLinkGetChannel(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_PHY_RSSI(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_INT8_S, |
| otPlatRadioGetRssi(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_PHY_TX_POWER(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_INT8_S, |
| otLinkGetMaxTransmitPower(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_PHY_RX_SENSITIVITY(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_INT8_S, |
| otPlatRadioGetReceiveSensitivity(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_SCAN_STATE(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| if (otLinkRawIsEnabled(mInstance)) |
| { |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| mCurScanChannel == NCP_INVALID_SCAN_CHANNEL |
| ? SPINEL_SCAN_STATE_IDLE |
| : SPINEL_SCAN_STATE_ENERGY |
| ); |
| } |
| else |
| |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| { |
| if (otLinkIsActiveScanInProgress(mInstance)) |
| { |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_SCAN_STATE_BEACON |
| ); |
| } |
| else if (otLinkIsEnergyScanInProgress(mInstance)) |
| { |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_SCAN_STATE_ENERGY |
| ); |
| } |
| else if (otThreadIsDiscoverInProgress(mInstance)) |
| { |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_SCAN_STATE_DISCOVER |
| ); |
| } |
| else |
| { |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| SPINEL_SCAN_STATE_IDLE |
| ); |
| } |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_SCAN_PERIOD(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT16_S, |
| mScanPeriod |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_ChannelMaskHelper(uint8_t aHeader, spinel_prop_key_t aKey, uint32_t aChannelMask) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S, |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey |
| )); |
| |
| for (int i = 0; i < 32; i++) |
| { |
| if (0 != (aChannelMask & (1 << i))) |
| { |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT8_S, i)); |
| } |
| } |
| |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_SCAN_MASK(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return GetPropertyHandler_ChannelMaskHelper(aHeader, aKey, mChannelMask); |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_15_4_PANID(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT16_S, |
| otLinkGetPanId(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_PROMISCUOUS_MODE(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_INT8_S, |
| otPlatRadioGetPromiscuous(mInstance) |
| ? SPINEL_MAC_PROMISCUOUS_MODE_FULL |
| : SPINEL_MAC_PROMISCUOUS_MODE_OFF |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_15_4_LADDR(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_EUI64_S, |
| otLinkGetExtendedAddress(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_15_4_SADDR(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT16_S, |
| otLinkGetShortAddress(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_EXTENDED_ADDR(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_EUI64_S, |
| otLinkGetExtendedAddress(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_DATA_POLL_PERIOD(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT32_S, |
| otLinkGetPollPeriod(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_RAW_STREAM_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| mIsRawStreamEnabled |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_SAVED(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| otDatasetIsCommissioned(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_IF_UP(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| otIp6IsEnabled(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_STACK_UP(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_ROLE(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| spinel_net_role_t role(SPINEL_NET_ROLE_DETACHED); |
| |
| switch (otThreadGetDeviceRole(mInstance)) |
| { |
| case OT_DEVICE_ROLE_DISABLED: |
| case OT_DEVICE_ROLE_DETACHED: |
| role = SPINEL_NET_ROLE_DETACHED; |
| break; |
| |
| case OT_DEVICE_ROLE_CHILD: |
| role = SPINEL_NET_ROLE_CHILD; |
| break; |
| |
| case OT_DEVICE_ROLE_ROUTER: |
| role = SPINEL_NET_ROLE_ROUTER; |
| break; |
| |
| case OT_DEVICE_ROLE_LEADER: |
| role = SPINEL_NET_ROLE_LEADER; |
| break; |
| } |
| |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| role |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_NETWORK_NAME(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UTF8_S, |
| otThreadGetNetworkName(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_XPANID(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_DATA_S, |
| otThreadGetExtendedPanId(mInstance), |
| sizeof(spinel_net_xpanid_t) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_MASTER_KEY(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_DATA_S, |
| otThreadGetMasterKey(mInstance)->m8, |
| OT_MASTER_KEY_SIZE |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_KEY_SEQUENCE_COUNTER(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT32_S, |
| otThreadGetKeySequenceCounter(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_PARTITION_ID(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT32_S, |
| otThreadGetPartitionId(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_KEY_SWITCH_GUARDTIME(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT32_S, |
| otThreadGetKeySwitchGuardTime(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA_VERSION(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| otNetDataGetVersion(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA_VERSION(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| otNetDataGetStableVersion(mInstance) |
| ); |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| otError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t networkData[255]; |
| uint8_t networkDataLen = 255; |
| |
| otBorderRouterGetNetData( |
| mInstance, |
| false, // Stable? |
| networkData, |
| &networkDataLen |
| ); |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S, |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey |
| )); |
| SuccessOrExit(error = OutboundFrameFeedData(networkData, networkDataLen)); |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t networkData[255]; |
| uint8_t networkDataLen = 255; |
| |
| otBorderRouterGetNetData( |
| mInstance, |
| true, // Stable? |
| networkData, |
| &networkDataLen |
| ); |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S, |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey |
| )); |
| SuccessOrExit(error = OutboundFrameFeedData(networkData, networkDataLen)); |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| return error; |
| } |
| #endif // OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| otError NcpBase::GetPropertyHandler_THREAD_LEADER_NETWORK_DATA(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t networkData[255]; |
| uint8_t networkDataLen = 255; |
| |
| otNetDataGet( |
| mInstance, |
| false, // Stable? |
| networkData, |
| &networkDataLen |
| ); |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S, |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey |
| )); |
| SuccessOrExit(error = OutboundFrameFeedData(networkData, networkDataLen)); |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_STABLE_LEADER_NETWORK_DATA(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t networkData[255]; |
| uint8_t networkDataLen = 255; |
| |
| otNetDataGet( |
| mInstance, |
| true, // Stable? |
| networkData, |
| &networkDataLen |
| ); |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S, |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey |
| )); |
| SuccessOrExit(error = OutboundFrameFeedData(networkData, networkDataLen)); |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_LEADER_RID(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| otThreadGetLeaderRouterId(mInstance) |
| ); |
| } |
| |
| #if OPENTHREAD_FTD |
| otError NcpBase::GetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| otThreadGetLocalLeaderWeight(mInstance) |
| ); |
| } |
| #endif // OPENTHREAD_FTD |
| |
| otError NcpBase::GetPropertyHandler_THREAD_LEADER_WEIGHT(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| otThreadGetLeaderWeight(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_LEADER_ADDR(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| otIp6Address address; |
| |
| error = otThreadGetLeaderRloc(mInstance, &address); |
| |
| if (error == OT_ERROR_NONE) |
| { |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_IPv6ADDR_S, |
| &address |
| ); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error)); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_PARENT(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| otRouterInfo parentInfo; |
| |
| error = otThreadGetParentInfo(mInstance, &parentInfo); |
| |
| if (error == OT_ERROR_NONE) |
| { |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| ( |
| SPINEL_DATATYPE_EUI64_S // Parent's extended address |
| SPINEL_DATATYPE_UINT16_S // Parent's rloc16 |
| ), |
| parentInfo.mExtAddress.m8, |
| parentInfo.mRloc16 |
| ); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error)); |
| } |
| |
| return error; |
| } |
| |
| #if OPENTHREAD_FTD |
| otError NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| otChildInfo childInfo; |
| uint8_t maxChildren; |
| uint8_t modeFlags; |
| |
| mDisableStreamWrite = true; |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S, |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey |
| )); |
| |
| maxChildren = otThreadGetMaxAllowedChildren(mInstance); |
| |
| for (uint8_t index = 0; index < maxChildren; index++) |
| { |
| error = otThreadGetChildInfoByIndex(mInstance, index, &childInfo); |
| |
| if (error != OT_ERROR_NONE) |
| { |
| continue; |
| } |
| |
| modeFlags = LinkFlagsToFlagByte(childInfo.mRxOnWhenIdle, |
| childInfo.mSecureDataRequest, |
| childInfo.mFullFunction, |
| childInfo.mFullNetworkData); |
| |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_STRUCT_S( |
| SPINEL_DATATYPE_EUI64_S // EUI64 Address |
| SPINEL_DATATYPE_UINT16_S // Rloc16 |
| SPINEL_DATATYPE_UINT32_S // Timeout |
| SPINEL_DATATYPE_UINT32_S // Age |
| SPINEL_DATATYPE_UINT8_S // Network Data Version |
| SPINEL_DATATYPE_UINT8_S // Link Quality In |
| SPINEL_DATATYPE_INT8_S // Average RSS |
| SPINEL_DATATYPE_UINT8_S // Mode (flags) |
| SPINEL_DATATYPE_INT8_S // Most recent RSS |
| ), |
| childInfo.mExtAddress.m8, |
| childInfo.mRloc16, |
| childInfo.mTimeout, |
| childInfo.mAge, |
| childInfo.mNetworkDataVersion, |
| childInfo.mLinkQualityIn, |
| childInfo.mAverageRssi, |
| modeFlags, |
| childInfo.mLastRssi |
| )); |
| } |
| |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| mDisableStreamWrite = false; |
| return error; |
| } |
| #endif // OPENTHREAD_FTD |
| |
| otError NcpBase::GetPropertyHandler_THREAD_NEIGHBOR_TABLE(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| otNeighborInfoIterator iter = OT_NEIGHBOR_INFO_ITERATOR_INIT; |
| otNeighborInfo neighInfo; |
| uint8_t modeFlags; |
| |
| mDisableStreamWrite = true; |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, SPINEL_CMD_PROP_VALUE_IS, |
| aKey)); |
| |
| while (otThreadGetNextNeighborInfo(mInstance, &iter, &neighInfo) == OT_ERROR_NONE) |
| { |
| modeFlags = LinkFlagsToFlagByte(neighInfo.mRxOnWhenIdle, |
| neighInfo.mSecureDataRequest, |
| neighInfo.mFullFunction, |
| neighInfo.mFullNetworkData); |
| |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_STRUCT_S( |
| SPINEL_DATATYPE_EUI64_S // EUI64 Address |
| SPINEL_DATATYPE_UINT16_S // Rloc16 |
| SPINEL_DATATYPE_UINT32_S // Age |
| SPINEL_DATATYPE_UINT8_S // Link Quality In |
| SPINEL_DATATYPE_INT8_S // Average RSS |
| SPINEL_DATATYPE_UINT8_S // Mode (flags) |
| SPINEL_DATATYPE_BOOL_S // Is Child |
| SPINEL_DATATYPE_UINT32_S // Link Frame Counter |
| SPINEL_DATATYPE_UINT32_S // MLE Frame Counter |
| SPINEL_DATATYPE_INT8_S // Most recent RSS |
| ), |
| neighInfo.mExtAddress.m8, |
| neighInfo.mRloc16, |
| neighInfo.mAge, |
| neighInfo.mLinkQualityIn, |
| neighInfo.mAverageRssi, |
| modeFlags, |
| neighInfo.mIsChild, |
| neighInfo.mLinkFrameCounter, |
| neighInfo.mMleFrameCounter, |
| neighInfo.mLastRssi |
| )); |
| } |
| |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| mDisableStreamWrite = false; |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t numEntries = 0; |
| const uint16_t *ports = otIp6GetUnsecurePorts(mInstance, &numEntries); |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_COMMAND_PROP_S, aHeader, SPINEL_CMD_PROP_VALUE_IS, |
| aKey)); |
| |
| for (; numEntries != 0; ports++, numEntries--) |
| { |
| SuccessOrExit(error = OutboundFrameFeedPacked(SPINEL_DATATYPE_UINT16_S, *ports)); |
| } |
| |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| mAllowLocalNetworkDataChange |
| ); |
| } |
| |
| #if OPENTHREAD_FTD |
| otError NcpBase::GetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| otThreadIsRouterRoleEnabled(mInstance) |
| ); |
| } |
| #endif // OPENTHREAD_FTD |
| |
| otError NcpBase::GetPropertyHandler_THREAD_ON_MESH_NETS(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| otBorderRouterConfig borderRouterConfig; |
| uint8_t flags; |
| |
| mDisableStreamWrite = true; |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S, |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey |
| )); |
| |
| // Fill from non-local network data first |
| for (otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT ;;) |
| { |
| error = otNetDataGetNextOnMeshPrefix(mInstance, &iter, &borderRouterConfig); |
| |
| if (error != OT_ERROR_NONE) |
| { |
| break; |
| } |
| |
| flags = BorderRouterConfigToFlagByte(borderRouterConfig); |
| |
| SuccessOrExit(error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_STRUCT_S( |
| SPINEL_DATATYPE_IPv6ADDR_S // IPv6 Prefix |
| SPINEL_DATATYPE_UINT8_S // Prefix Length (in bits) |
| SPINEL_DATATYPE_BOOL_S // isStable |
| SPINEL_DATATYPE_UINT8_S // Flags |
| SPINEL_DATATYPE_BOOL_S // isLocal |
| ), |
| &borderRouterConfig.mPrefix, |
| 64, |
| borderRouterConfig.mStable, |
| flags, |
| false |
| )); |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| // Fill from local network data last |
| for (otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT ;;) |
| { |
| error = otBorderRouterGetNextOnMeshPrefix(mInstance, &iter, &borderRouterConfig); |
| |
| if (error != OT_ERROR_NONE) |
| { |
| break; |
| } |
| |
| flags = BorderRouterConfigToFlagByte(borderRouterConfig); |
| |
| SuccessOrExit(error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_STRUCT_S( |
| SPINEL_DATATYPE_IPv6ADDR_S // IPv6 Prefix |
| SPINEL_DATATYPE_UINT8_S // Prefix Length (in bits) |
| SPINEL_DATATYPE_BOOL_S // isStable |
| SPINEL_DATATYPE_UINT8_S // Flags |
| SPINEL_DATATYPE_BOOL_S // isLocal |
| ), |
| &borderRouterConfig.mPrefix, |
| 64, |
| borderRouterConfig.mStable, |
| flags, |
| true |
| )); |
| } |
| #endif // OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| mDisableStreamWrite = false; |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_DISCOVERY_SCAN_JOINER_FLAG(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| mDiscoveryScanJoinerFlag |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| mDiscoveryScanEnableFiltering |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_DISCOVERY_SCAN_PANID(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT16_S, |
| mDiscoveryScanPanId |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_IPV6_ML_PREFIX(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| const uint8_t *mlPrefix = otThreadGetMeshLocalPrefix(mInstance); |
| |
| if (mlPrefix) |
| { |
| otIp6Address addr; |
| |
| memcpy(addr.mFields.m8, mlPrefix, 8); |
| |
| // Zero out the last 8 bytes. |
| memset(addr.mFields.m8 + 8, 0, 8); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| ( |
| SPINEL_DATATYPE_IPv6ADDR_S // Mesh-local IPv6 address |
| SPINEL_DATATYPE_UINT8_S // Prefix length (in bits) |
| ), |
| &addr, |
| 64 |
| ); |
| } |
| else |
| { |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_VOID_S |
| ); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_IPV6_ML_ADDR(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| const otIp6Address *ml64 = otThreadGetMeshLocalEid(mInstance); |
| |
| if (ml64) |
| { |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_IPv6ADDR_S, |
| ml64 |
| ); |
| } |
| else |
| { |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_VOID_S |
| ); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_IPV6_LL_ADDR(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| const otIp6Address *address = otThreadGetLinkLocalIp6Address(mInstance); |
| |
| if (address) |
| { |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_IPv6ADDR_S, |
| address |
| ); |
| } |
| else |
| { |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_VOID_S |
| ); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_IPV6_ADDRESS_TABLE(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| mDisableStreamWrite = true; |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S, |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey |
| )); |
| |
| for (const otNetifAddress *address = otIp6GetUnicastAddresses(mInstance); address; address = address->mNext) |
| { |
| |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_STRUCT_S( |
| SPINEL_DATATYPE_IPv6ADDR_S // IPv6 address |
| SPINEL_DATATYPE_UINT8_S // Prefix length (in bits) |
| SPINEL_DATATYPE_UINT32_S // Preferred lifetime |
| SPINEL_DATATYPE_UINT32_S // Valid lifetime |
| ), |
| &address->mAddress, |
| address->mPrefixLength, |
| address->mPreferred ? 0xffffffff : 0, |
| address->mValid ? 0xffffffff : 0 |
| )); |
| } |
| |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| mDisableStreamWrite = false; |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_IPV6_ROUTE_TABLE(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| // TODO: Implement get route table |
| OT_UNUSED_VARIABLE(aKey); |
| |
| return SendLastStatus(aHeader, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| otError NcpBase::GetPropertyHandler_IPV6_ICMP_PING_OFFLOAD(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| otIcmp6IsEchoEnabled(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| // Note reverse logic: passthru enabled = filter disabled |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| !otIp6IsReceiveFilterEnabled(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_OFF_MESH_ROUTES(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| otExternalRouteConfig external_route_config; |
| otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT; |
| |
| mDisableStreamWrite = true; |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S, |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey |
| )); |
| |
| while (otNetDataGetNextRoute(mInstance, &iter, &external_route_config) == OT_ERROR_NONE) |
| { |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_STRUCT_S( |
| SPINEL_DATATYPE_IPv6ADDR_S // IPv6 Prefix |
| SPINEL_DATATYPE_UINT8_S // Prefix Length (in bits) |
| SPINEL_DATATYPE_BOOL_S // IsStable |
| SPINEL_DATATYPE_UINT8_S // Route Preference Flags |
| SPINEL_DATATYPE_BOOL_S // IsLocal |
| SPINEL_DATATYPE_BOOL_S // NextHopIsThisDevice |
| ), |
| &external_route_config.mPrefix.mPrefix, |
| external_route_config.mPrefix.mLength, |
| external_route_config.mStable, |
| ExternalRoutePreferenceToFlagByte(external_route_config.mPreference), |
| false, |
| external_route_config.mNextHopIsThisDevice |
| )); |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| while (otBorderRouterGetNextRoute(mInstance, &iter, &external_route_config) == OT_ERROR_NONE) |
| { |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_STRUCT_S( |
| SPINEL_DATATYPE_IPv6ADDR_S // IPv6 Prefix |
| SPINEL_DATATYPE_UINT8_S // Prefix Length (in bits) |
| SPINEL_DATATYPE_BOOL_S // IsStable |
| SPINEL_DATATYPE_UINT8_S // Route Preference Flags |
| SPINEL_DATATYPE_BOOL_S // IsLocal |
| SPINEL_DATATYPE_BOOL_S // NextHopIsThisDevice |
| ), |
| &external_route_config.mPrefix.mPrefix, |
| external_route_config.mPrefix.mLength, |
| external_route_config.mStable, |
| ExternalRoutePreferenceToFlagByte(external_route_config.mPreference), |
| true, |
| external_route_config.mNextHopIsThisDevice |
| )); |
| } |
| #endif // OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| mDisableStreamWrite = false; |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_STREAM_NET(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| // TODO: Implement explicit data poll. |
| OT_UNUSED_VARIABLE(aKey); |
| |
| return SendLastStatus(aHeader, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| #if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD |
| otError NcpBase::GetPropertyHandler_THREAD_TMF_PROXY_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| otTmfProxyIsEnabled(mInstance) |
| ); |
| } |
| #endif // OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD |
| |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| |
| otError NcpBase::GetPropertyHandler_JAM_DETECT_ENABLE(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| otJamDetectionIsEnabled(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_JAM_DETECTED(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| otJamDetectionGetState(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_INT8_S, |
| otJamDetectionGetRssiThreshold(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_JAM_DETECT_WINDOW(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| otJamDetectionGetWindow(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_JAM_DETECT_BUSY(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| otJamDetectionGetBusyPeriod(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_JAM_DETECT_HISTORY_BITMAP(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| uint64_t historyBitmap = otJamDetectionGetHistoryBitmap(mInstance); |
| |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| ( |
| SPINEL_DATATYPE_UINT32_S // History bitmap - bits 0-31 |
| SPINEL_DATATYPE_UINT32_S // History bitmap - bits 32-63 |
| ), |
| static_cast<uint32_t>(historyBitmap & 0xffffffff), |
| static_cast<uint32_t>(historyBitmap >> 32) |
| ); |
| } |
| |
| #endif // OPENTHREAD_ENABLE_JAM_DETECTION |
| |
| otError NcpBase::GetPropertyHandler_MAC_CNTR(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| uint32_t value; |
| const otMacCounters *macCounters; |
| otError error = OT_ERROR_NONE; |
| |
| macCounters = otLinkGetCounters(mInstance); |
| |
| assert(macCounters != NULL); |
| |
| switch (aKey) |
| { |
| case SPINEL_PROP_CNTR_TX_PKT_TOTAL: |
| value = macCounters->mTxTotal; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_ACK_REQ: |
| value = macCounters->mTxAckRequested; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_ACKED: |
| value = macCounters->mTxAcked; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_NO_ACK_REQ: |
| value = macCounters->mTxNoAckRequested; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_DATA: |
| value = macCounters->mTxData; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_DATA_POLL: |
| value = macCounters->mTxDataPoll; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_BEACON: |
| value = macCounters->mTxBeacon; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_BEACON_REQ: |
| value = macCounters->mTxBeaconRequest; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_OTHER: |
| value = macCounters->mTxOther; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_RETRY: |
| value = macCounters->mTxRetry; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_ERR_CCA: |
| value = macCounters->mTxErrCca; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_UNICAST: |
| value = macCounters->mTxUnicast; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_PKT_BROADCAST: |
| value = macCounters->mTxBroadcast; |
| break; |
| |
| case SPINEL_PROP_CNTR_TX_ERR_ABORT: |
| value = macCounters->mTxErrAbort; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_TOTAL: |
| value = macCounters->mRxTotal; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_DATA: |
| value = macCounters->mRxData; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_DATA_POLL: |
| value = macCounters->mRxDataPoll; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_BEACON: |
| value = macCounters->mRxBeacon; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_BEACON_REQ: |
| value = macCounters->mRxBeaconRequest; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_OTHER: |
| value = macCounters->mRxOther; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_FILT_WL: |
| value = macCounters->mRxWhitelistFiltered; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_FILT_DA: |
| value = macCounters->mRxDestAddrFiltered; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_DUP: |
| value = macCounters->mRxDuplicated; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_UNICAST: |
| value = macCounters->mRxUnicast; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_PKT_BROADCAST: |
| value = macCounters->mRxBroadcast; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_ERR_EMPTY: |
| value = macCounters->mRxErrNoFrame; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_ERR_UKWN_NBR: |
| value = macCounters->mRxErrUnknownNeighbor; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_ERR_NVLD_SADDR: |
| value = macCounters->mRxErrInvalidSrcAddr; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_ERR_SECURITY: |
| value = macCounters->mRxErrSec; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_ERR_BAD_FCS: |
| value = macCounters->mRxErrFcs; |
| break; |
| |
| case SPINEL_PROP_CNTR_RX_ERR_OTHER: |
| value = macCounters->mRxErrOther; |
| break; |
| |
| default: |
| error = SendLastStatus(aHeader, SPINEL_STATUS_INTERNAL_ERROR); |
| ExitNow(); |
| } |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT32_S, |
| value |
| ); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_NCP_CNTR(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| uint32_t value; |
| otError error = OT_ERROR_NONE; |
| |
| switch (aKey) |
| { |
| 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: |
| error = SendLastStatus(aHeader, SPINEL_STATUS_INTERNAL_ERROR); |
| ExitNow(); |
| } |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT32_S, |
| value |
| ); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_IP_CNTR(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| uint32_t value; |
| otError error = OT_ERROR_NONE; |
| |
| const otIpCounters *counters = otThreadGetIp6Counters(mInstance); |
| |
| switch (aKey) |
| { |
| case SPINEL_PROP_CNTR_IP_TX_SUCCESS: |
| value = counters->mTxSuccess; |
| break; |
| |
| case SPINEL_PROP_CNTR_IP_RX_SUCCESS: |
| value = counters->mRxSuccess; |
| break; |
| |
| case SPINEL_PROP_CNTR_IP_TX_FAILURE: |
| value = counters->mTxFailure; |
| break; |
| |
| case SPINEL_PROP_CNTR_IP_RX_FAILURE: |
| value = counters->mRxFailure; |
| break; |
| |
| default: |
| error = SendLastStatus(aHeader, SPINEL_STATUS_INTERNAL_ERROR); |
| ExitNow(); |
| break; |
| } |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT32_S, |
| value |
| ); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_MSG_BUFFER_COUNTERS(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otError error = OT_ERROR_NONE; |
| otBufferInfo bufferInfo; |
| |
| otMessageGetBufferInfo(mInstance, &bufferInfo); |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S, |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey |
| )); |
| |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| ( |
| SPINEL_DATATYPE_UINT16_S // Total buffers |
| SPINEL_DATATYPE_UINT16_S // Free buffers |
| SPINEL_DATATYPE_UINT16_S // Lowpan send messages |
| SPINEL_DATATYPE_UINT16_S // Lowpan send buffers |
| SPINEL_DATATYPE_UINT16_S // Lowpan reassembly messages |
| SPINEL_DATATYPE_UINT16_S // Lowpan reassembly buffers |
| SPINEL_DATATYPE_UINT16_S // Ip6 messages |
| SPINEL_DATATYPE_UINT16_S // Ip6 buffers |
| SPINEL_DATATYPE_UINT16_S // Mpl messages |
| SPINEL_DATATYPE_UINT16_S // Mpl buffers |
| SPINEL_DATATYPE_UINT16_S // Mle messages |
| SPINEL_DATATYPE_UINT16_S // Mle buffers |
| SPINEL_DATATYPE_UINT16_S // Arp messages |
| SPINEL_DATATYPE_UINT16_S // Arp buffers |
| SPINEL_DATATYPE_UINT16_S // Coap messages |
| SPINEL_DATATYPE_UINT16_S // Coap buffers |
| ), |
| 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.mCoapMessages, |
| bufferInfo.mCoapBuffers |
| )); |
| |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_DEBUG_TEST_ASSERT(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| 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 SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| false |
| ); |
| ) |
| } |
| |
| otError NcpBase::GetPropertyHandler_DEBUG_NCP_LOG_LEVEL(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| uint8_t logLevel = 0; |
| |
| switch (otGetDynamicLogLevel(mInstance)) |
| { |
| case OT_LOG_LEVEL_NONE: |
| logLevel = SPINEL_NCP_LOG_LEVEL_EMERG; |
| break; |
| |
| case OT_LOG_LEVEL_CRIT: |
| logLevel = SPINEL_NCP_LOG_LEVEL_CRIT; |
| break; |
| |
| case OT_LOG_LEVEL_WARN: |
| logLevel = SPINEL_NCP_LOG_LEVEL_WARN; |
| break; |
| |
| case OT_LOG_LEVEL_INFO: |
| logLevel = SPINEL_NCP_LOG_LEVEL_INFO; |
| break; |
| |
| case OT_LOG_LEVEL_DEBG: |
| logLevel = SPINEL_NCP_LOG_LEVEL_DEBUG; |
| break; |
| } |
| |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| logLevel |
| ); |
| } |
| |
| #if OPENTHREAD_ENABLE_MAC_WHITELIST |
| |
| otError NcpBase::GetPropertyHandler_MAC_WHITELIST(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otMacWhitelistEntry entry; |
| otError error = OT_ERROR_NONE; |
| |
| mDisableStreamWrite = true; |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S, |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey |
| )); |
| |
| for (uint8_t i = 0; (i != 255) && (error == OT_ERROR_NONE); i++) |
| { |
| error = otLinkGetWhitelistEntry(mInstance, i, &entry); |
| |
| if (error != OT_ERROR_NONE) |
| { |
| break; |
| } |
| |
| if (entry.mValid) |
| { |
| if (!entry.mFixedRssi) |
| { |
| entry.mRssi = RSSI_OVERRIDE_DISABLED; |
| } |
| |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_STRUCT_S( |
| SPINEL_DATATYPE_EUI64_S // Extended address |
| SPINEL_DATATYPE_INT8_S // Rssi |
| ), |
| entry.mExtAddress.m8, |
| entry.mRssi |
| )); |
| } |
| } |
| |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| mDisableStreamWrite = false; |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_WHITELIST_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| otLinkIsWhitelistEnabled(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_BLACKLIST(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| otMacBlacklistEntry entry; |
| otError error = OT_ERROR_NONE; |
| |
| mDisableStreamWrite = true; |
| |
| SuccessOrExit(error = OutboundFrameBegin()); |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S, |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey |
| )); |
| |
| for (uint8_t i = 0; (i != 255) && (error == OT_ERROR_NONE); i++) |
| { |
| error = otLinkGetBlacklistEntry(mInstance, i, &entry); |
| |
| if (error != OT_ERROR_NONE) |
| { |
| break; |
| } |
| |
| if (entry.mValid) |
| { |
| SuccessOrExit( |
| error = OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_STRUCT_S( |
| SPINEL_DATATYPE_EUI64_S // Extended address |
| ), |
| entry.mExtAddress.m8 |
| )); |
| } |
| } |
| |
| SuccessOrExit(error = OutboundFrameSend()); |
| |
| exit: |
| mDisableStreamWrite = false; |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_BLACKLIST_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| otLinkIsBlacklistEnabled(mInstance) |
| ); |
| } |
| |
| |
| #endif // OPENTHREAD_ENABLE_MAC_WHITELIST |
| |
| #if OPENTHREAD_FTD |
| otError NcpBase::GetPropertyHandler_NET_PSKC(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_DATA_S, |
| otThreadGetPSKc(mInstance), |
| sizeof(spinel_net_pskc_t) |
| ); |
| } |
| #endif // OPENTHREAD_FTD |
| |
| otError NcpBase::GetPropertyHandler_THREAD_MODE(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| uint8_t numericMode; |
| otLinkModeConfig modeConfig = otThreadGetLinkMode(mInstance); |
| |
| numericMode = LinkFlagsToFlagByte(modeConfig.mRxOnWhenIdle, |
| modeConfig.mSecureDataRequests, |
| modeConfig.mDeviceType, |
| modeConfig.mNetworkData); |
| |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| numericMode |
| ); |
| } |
| |
| #if OPENTHREAD_FTD |
| otError NcpBase::GetPropertyHandler_THREAD_CHILD_COUNT_MAX(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| otThreadGetMaxAllowedChildren(mInstance) |
| ); |
| } |
| #endif // OPENTHREAD_FTD |
| |
| otError NcpBase::GetPropertyHandler_THREAD_CHILD_TIMEOUT(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT32_S, |
| otThreadGetChildTimeout(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_RLOC16(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT16_S, |
| otThreadGetRloc16(mInstance) |
| ); |
| } |
| |
| #if OPENTHREAD_FTD |
| otError NcpBase::GetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| otThreadGetRouterUpgradeThreshold(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| otThreadGetRouterDowngradeThreshold(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| otThreadGetRouterSelectionJitter(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT32_S, |
| otThreadGetContextIdReuseDelay(mInstance) |
| ); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| otThreadGetNetworkIdTimeout(mInstance) |
| ); |
| } |
| #endif // OPENTHREAD_FTD |
| |
| #if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD |
| otError NcpBase::GetPropertyHandler_THREAD_COMMISSIONER_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| bool enabled = false; |
| |
| if (otCommissionerGetState(mInstance) == OT_COMMISSIONER_STATE_ACTIVE) |
| { |
| enabled = true; |
| } |
| |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| enabled |
| ); |
| } |
| #endif |
| |
| otError NcpBase::GetPropertyHandler_NET_REQUIRE_JOIN_EXISTING(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_BOOL_S, |
| mRequireJoinExistingNetwork |
| ); |
| } |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| otError NcpBase::GetPropertyHandler_NEST_LEGACY_ULA_PREFIX(uint8_t aHeader, spinel_prop_key_t aKey) |
| { |
| return SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_DATA_S, |
| mLegacyUlaPrefix, |
| sizeof(mLegacyUlaPrefix) |
| ); |
| } |
| #endif // OPENTHREAD_ENABLE_LEGACY |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Individual Property Setters |
| // ---------------------------------------------------------------------------- |
| |
| otError NcpBase::SendSetPropertyResponse(uint8_t aHeader, spinel_prop_key_t aKey, otError aError) |
| { |
| if (aError == OT_ERROR_NONE) |
| { |
| aError = HandleCommandPropertyGet(aHeader, aKey); |
| } |
| else |
| { |
| aError = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(aError)); |
| } |
| |
| return aError; |
| } |
| |
| otError NcpBase::SetPropertyHandler_POWER_STATE(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| // TODO: Implement POWER_STATE |
| OT_UNUSED_VARIABLE(aKey); |
| OT_UNUSED_VARIABLE(aValuePtr); |
| OT_UNUSED_VARIABLE(aValueLen); |
| |
| return SendLastStatus(aHeader, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| otError NcpBase::SetPropertyHandler_HOST_POWER_STATE(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| uint8_t value; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &value |
| ); |
| |
| if (parsedLength > 0) |
| { |
| switch (value) |
| { |
| 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>(value); |
| 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; |
| } |
| } |
| else |
| { |
| error = OT_ERROR_PARSE; |
| } |
| |
| if (error == OT_ERROR_NONE) |
| { |
| mHostPowerStateHeader = 0; |
| |
| error = HandleCommandPropertyGet(aHeader, aKey); |
| |
| 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 = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error)); |
| } |
| |
| return error; |
| } |
| |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| otError NcpBase::SetPropertyHandler_PHY_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| bool value = false; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &value |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| if (value == false) |
| { |
| // If we have raw stream enabled stop receiving |
| if (mIsRawStreamEnabled) |
| { |
| otLinkRawSleep(mInstance); |
| } |
| |
| error = otLinkRawSetEnable(mInstance, false); |
| } |
| else |
| { |
| error = otLinkRawSetEnable(mInstance, true); |
| |
| // If we have raw stream enabled already, start receiving |
| if (error == OT_ERROR_NONE && mIsRawStreamEnabled) |
| { |
| error = otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone); |
| } |
| } |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| otError NcpBase::SetPropertyHandler_PHY_TX_POWER(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| int8_t value = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_INT8_S, |
| &value |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otLinkSetMaxTransmitPower(mInstance, value); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_PHY_CHAN(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| unsigned int channel = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT_PACKED_S, |
| &channel |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| error = otLinkSetChannel(mInstance, static_cast<uint8_t>(channel)); |
| |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| SuccessOrExit(error); |
| |
| // Cache the channel. If the raw link layer isn't enabled yet, the otSetChannel call |
| // doesn't call into the radio layer to set the channel. We will have to do it |
| // manually whenever the radios are enabled and/or raw stream is enabled. |
| mCurReceiveChannel = static_cast<uint8_t>(channel); |
| |
| // 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, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone); |
| } |
| |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_PROMISCUOUS_MODE(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint8_t mode = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &mode |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| 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 SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_SCAN_MASK(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| uint32_t newMask = 0; |
| otError error = OT_ERROR_NONE; |
| |
| for (; aValueLen != 0; aValueLen--, aValuePtr++) |
| { |
| VerifyOrExit(aValuePtr[0] <= 31, error = OT_ERROR_INVALID_ARGS); |
| VerifyOrExit((mSupportedChannelMask & (1 << aValuePtr[0])) != 0, error = OT_ERROR_INVALID_ARGS); |
| |
| newMask |= (1 << aValuePtr[0]); |
| } |
| |
| mChannelMask = newMask; |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_SCAN_PERIOD(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| uint16_t period = mScanPeriod; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT16_S, |
| &period |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| mScanPeriod = period; |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_REQUIRE_JOIN_EXISTING(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| bool value = mRequireJoinExistingNetwork; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &value |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| mRequireJoinExistingNetwork = value; |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| static bool HasOnly1BitSet(uint32_t aValue) |
| { |
| return aValue != 0 && ((aValue & (aValue - 1)) == 0); |
| } |
| |
| static uint8_t IndexOfMSB(uint32_t aValue) |
| { |
| uint8_t index = 0; |
| |
| while (aValue >>= 1) |
| { |
| index++; |
| } |
| |
| return index; |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_SCAN_STATE(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| uint8_t state = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &state |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| switch (state) |
| { |
| case SPINEL_SCAN_STATE_IDLE: |
| error = OT_ERROR_NONE; |
| break; |
| |
| case SPINEL_SCAN_STATE_BEACON: |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| if (otLinkRawIsEnabled(mInstance)) |
| { |
| error = OT_ERROR_NOT_IMPLEMENTED; |
| } |
| else |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API |
| { |
| error = otLinkActiveScan( |
| mInstance, |
| mChannelMask, |
| mScanPeriod, |
| &HandleActiveScanResult_Jump, |
| this |
| ); |
| } |
| |
| SuccessOrExit(error); |
| mShouldSignalEndOfScan = false; |
| break; |
| |
| case SPINEL_SCAN_STATE_ENERGY: |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| if (otLinkRawIsEnabled(mInstance)) |
| { |
| uint8_t scanChannel; |
| |
| // Make sure we aren't already scanning and that we have |
| // only 1 bit set for the channel mask. |
| VerifyOrExit(mCurScanChannel == NCP_INVALID_SCAN_CHANNEL, error = OT_ERROR_INVALID_STATE); |
| VerifyOrExit(HasOnly1BitSet(mChannelMask), error = OT_ERROR_INVALID_ARGS); |
| |
| scanChannel = IndexOfMSB(mChannelMask); |
| mCurScanChannel = (int8_t)scanChannel; |
| |
| error = otLinkRawEnergyScan( |
| mInstance, |
| scanChannel, |
| mScanPeriod, |
| LinkRawEnergyScanDone |
| ); |
| } |
| else |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API |
| { |
| error = otLinkEnergyScan( |
| mInstance, |
| mChannelMask, |
| mScanPeriod, |
| &HandleEnergyScanResult_Jump, |
| this |
| ); |
| } |
| |
| SuccessOrExit(error); |
| mShouldSignalEndOfScan = false; |
| break; |
| |
| case SPINEL_SCAN_STATE_DISCOVER: |
| error = otThreadDiscover( |
| mInstance, |
| mChannelMask, |
| mDiscoveryScanPanId, |
| mDiscoveryScanJoinerFlag, |
| mDiscoveryScanEnableFiltering, |
| &HandleActiveScanResult_Jump, |
| this |
| ); |
| |
| SuccessOrExit(error); |
| mShouldSignalEndOfScan = false; |
| break; |
| |
| default: |
| error = OT_ERROR_INVALID_ARGS; |
| break; |
| } |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_15_4_PANID(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| uint16_t panid; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT16_S, |
| &panid |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| error = otLinkSetPanId(mInstance, panid); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_15_4_LADDR(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| otExtAddress *extAddress; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_EUI64_S, |
| &extAddress |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| error = otLinkSetExtendedAddress(mInstance, extAddress); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_DATA_POLL_PERIOD(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint32_t pollPeriod; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT32_S, |
| &pollPeriod |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otLinkSetPollPeriod(mInstance, pollPeriod); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_RAW_STREAM_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| bool value = false; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &value |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| if (otLinkRawIsEnabled(mInstance)) |
| { |
| if (value) |
| { |
| error = otLinkRawReceive(mInstance, mCurReceiveChannel, &NcpBase::LinkRawReceiveDone); |
| } |
| else |
| { |
| error = otLinkRawSleep(mInstance); |
| } |
| } |
| |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| mIsRawStreamEnabled = value; |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| otError NcpBase::SetPropertyHandler_MAC_15_4_SADDR(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| uint16_t shortAddress; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT16_S, |
| &shortAddress |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| error = otLinkRawSetShortAddress(mInstance, shortAddress); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_STREAM_RAW(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| uint8_t *frame_buffer = NULL; |
| otRadioFrame *frame; |
| unsigned int frameLen = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(otLinkRawIsEnabled(mInstance), error = OT_ERROR_INVALID_STATE); |
| |
| frame = otLinkRawGetTransmitBuffer(mInstance); |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_DATA_WLEN_S |
| SPINEL_DATATYPE_UINT8_S |
| SPINEL_DATATYPE_INT8_S, |
| &frame_buffer, |
| &frameLen, |
| &frame->mChannel, |
| &frame->mPower |
| ); |
| |
| VerifyOrExit(parsedLength > 0 && frameLen <= OT_RADIO_FRAME_MAX_SIZE, error = OT_ERROR_PARSE); |
| |
| // Cache the transaction ID for async response |
| mCurTransmitTID = SPINEL_HEADER_GET_TID(aHeader); |
| |
| // Update frame buffer and length |
| frame->mLength = static_cast<uint8_t>(frameLen); |
| memcpy(frame->mPsdu, frame_buffer, frame->mLength); |
| |
| // TODO: This should be later added in the STREAM_RAW argument to allow user to directly specify it. |
| frame->mMaxTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_DIRECT; |
| |
| // Pass frame to the radio layer. Note, this fails if we |
| // haven't enabled raw stream or are already transmitting. |
| error = otLinkRawTransmit(mInstance, frame, &NcpBase::LinkRawTransmitDone); |
| |
| exit: |
| |
| if (error == OT_ERROR_NONE) |
| { |
| // Don't do anything here yet. We will complete the transaction when we get a transmit done callback |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error)); |
| } |
| |
| OT_UNUSED_VARIABLE(aKey); |
| |
| return error; |
| } |
| |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| otError NcpBase::SetPropertyHandler_NET_IF_UP(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| bool enabled = false; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &enabled |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| error = otIp6SetEnabled(mInstance, enabled); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_STACK_UP(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| bool enabled = false; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &enabled |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| // If the value has changed... |
| if ((enabled != false) != (otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED)) |
| { |
| if (enabled != false) |
| { |
| error = otThreadSetEnabled(mInstance, true); |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| mLegacyNodeDidJoin = false; |
| |
| if ((mLegacyHandlers != NULL) && (mLegacyHandlers->mStartLegacy != NULL)) |
| { |
| mLegacyHandlers->mStartLegacy(); |
| } |
| #endif // OPENTHREAD_ENABLE_LEGACY |
| } |
| else |
| { |
| error = otThreadSetEnabled(mInstance, false); |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| mLegacyNodeDidJoin = false; |
| |
| if ((mLegacyHandlers != NULL) && (mLegacyHandlers->mStopLegacy != NULL)) |
| { |
| mLegacyHandlers->mStopLegacy(); |
| } |
| #endif // OPENTHREAD_ENABLE_LEGACY |
| } |
| } |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_ROLE(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| unsigned int role = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT_PACKED_S, |
| &role |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| |
| switch (role) |
| { |
| case SPINEL_NET_ROLE_DETACHED: |
| error = otThreadBecomeDetached(mInstance); |
| break; |
| |
| #if OPENTHREAD_FTD |
| case SPINEL_NET_ROLE_ROUTER: |
| error = otThreadBecomeRouter(mInstance); |
| break; |
| |
| case SPINEL_NET_ROLE_LEADER: |
| error = otThreadBecomeLeader(mInstance); |
| break; |
| #endif // OPENTHREAD_FTD |
| |
| case SPINEL_NET_ROLE_CHILD: |
| error = otThreadBecomeChild(mInstance); |
| break; |
| } |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_NETWORK_NAME(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| const char *string = NULL; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UTF8_S, |
| &string |
| ); |
| |
| VerifyOrExit((parsedLength > 0) && (string != NULL), error = OT_ERROR_PARSE); |
| |
| error = otThreadSetNetworkName(mInstance, string); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_XPANID(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| const uint8_t *ptr = NULL; |
| spinel_size_t len; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_DATA_S, |
| &ptr, |
| &len |
| ); |
| |
| VerifyOrExit((parsedLength > 0) && (len == sizeof(spinel_net_xpanid_t)), error = OT_ERROR_PARSE); |
| |
| error = otThreadSetExtendedPanId(mInstance, ptr); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_MASTER_KEY(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| const uint8_t *ptr = NULL; |
| spinel_size_t len; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_DATA_S, |
| &ptr, |
| &len |
| ); |
| |
| VerifyOrExit((parsedLength > 0) && (len == OT_MASTER_KEY_SIZE), error = OT_ERROR_PARSE); |
| |
| error = otThreadSetMasterKey(mInstance, reinterpret_cast<const otMasterKey *>(ptr)); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_KEY_SEQUENCE_COUNTER(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| unsigned int keySeqCounter; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT32_S, |
| &keySeqCounter |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otThreadSetKeySequenceCounter(mInstance, keySeqCounter); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_KEY_SWITCH_GUARDTIME(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| unsigned int keyGuardTime; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT32_S, |
| &keyGuardTime |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otThreadSetKeySwitchGuardTime(mInstance, keyGuardTime); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| #if OPENTHREAD_FTD |
| otError NcpBase::SetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint8_t weight; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &weight |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otThreadSetLocalLeaderWeight(mInstance, weight); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| #endif // OPENTHREAD_FTD |
| |
| otError NcpBase::SetPropertyHandler_STREAM_NET_INSECURE(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| const uint8_t *framePtr = NULL; |
| unsigned int frameLen = 0; |
| const uint8_t *metaPtr = NULL; |
| unsigned int metaLen = 0; |
| otMessage *message; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| // STREAM_NET_INSECURE packets are not secured at layer 2. |
| message = otIp6NewMessage(mInstance, false); |
| VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS); |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| ( |
| SPINEL_DATATYPE_DATA_WLEN_S // Frame data |
| SPINEL_DATATYPE_DATA_S // Meta data |
| ), |
| &framePtr, |
| &frameLen, |
| &metaPtr, |
| &metaLen |
| ); |
| |
| // We ignore metadata for now. |
| // May later include TX power, allow retransmits, etc... |
| OT_UNUSED_VARIABLE(metaPtr); |
| OT_UNUSED_VARIABLE(metaLen); |
| OT_UNUSED_VARIABLE(parsedLength); |
| |
| SuccessOrExit(error = otMessageAppend(message, framePtr, static_cast<uint16_t>(frameLen))); |
| |
| // Ensure the insecure message is forwarded using direct transmission. |
| otMessageSetDirectTransmission(message, true); |
| |
| error = otIp6Send(mInstance, message); |
| |
| // `otIp6Send()` takes ownership of `message` (in both success or |
| // failure cases). `message` is set to NULL so it is not freed at |
| // exit. |
| message = NULL; |
| |
| exit: |
| if (message != NULL) |
| { |
| otMessageFree(message); |
| } |
| |
| if (error == OT_ERROR_NONE) |
| { |
| mInboundInsecureIpFrameCounter++; |
| |
| if (SPINEL_HEADER_GET_TID(aHeader) != 0) |
| { |
| // Only send a successful status update if |
| // there was a transaction id in the aHeader. |
| error = SendLastStatus(aHeader, SPINEL_STATUS_OK); |
| } |
| } |
| else |
| { |
| mDroppedInboundIpFrameCounter++; |
| |
| error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error)); |
| } |
| |
| OT_UNUSED_VARIABLE(aKey); |
| |
| return error; |
| } |
| |
| #if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD |
| otError NcpBase::SetPropertyHandler_THREAD_TMF_PROXY_STREAM(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| const uint8_t *framePtr = NULL; |
| unsigned int frameLen = 0; |
| uint16_t locator; |
| uint16_t port; |
| otMessage *message; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| // THREAD_TMF_PROXY_STREAM requires layer 2 security. |
| message = otIp6NewMessage(mInstance, true); |
| VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS); |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| ( |
| SPINEL_DATATYPE_DATA_WLEN_S // Frame data |
| SPINEL_DATATYPE_UINT16_S // Locator |
| SPINEL_DATATYPE_UINT16_S // Port |
| ), |
| &framePtr, |
| &frameLen, |
| &locator, |
| &port |
| ); |
| |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| SuccessOrExit(error = otMessageAppend(message, framePtr, static_cast<uint16_t>(frameLen))); |
| |
| error = otTmfProxySend(mInstance, message, locator, port); |
| |
| // `otTmfProxySend()` takes ownership of `message` (in both success |
| // or failure cases). `message` is set to NULL so it is not freed at |
| // exit. |
| message = NULL; |
| |
| exit: |
| if (message != NULL) |
| { |
| otMessageFree(message); |
| } |
| |
| if (error == OT_ERROR_NONE) |
| { |
| if (SPINEL_HEADER_GET_TID(aHeader) != 0) |
| { |
| // Only send a successful status update if |
| // there was a transaction id in the aHeader. |
| error = SendLastStatus(aHeader, SPINEL_STATUS_OK); |
| } |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error)); |
| } |
| |
| OT_UNUSED_VARIABLE(aKey); |
| |
| return error; |
| } |
| #endif // OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD |
| |
| otError NcpBase::SetPropertyHandler_STREAM_NET(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| const uint8_t *framePtr = NULL; |
| unsigned int frameLen = 0; |
| const uint8_t *metaPtr = NULL; |
| unsigned int metaLen = 0; |
| otMessage *message; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| // STREAM_NET requires layer 2 security. |
| message = otIp6NewMessage(mInstance, true); |
| VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS); |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| ( |
| SPINEL_DATATYPE_DATA_WLEN_S // Frame data |
| SPINEL_DATATYPE_DATA_S // Meta data |
| ), |
| &framePtr, |
| &frameLen, |
| &metaPtr, |
| &metaLen |
| ); |
| |
| // We ignore metadata for now. |
| // May later include TX power, allow retransmits, etc... |
| OT_UNUSED_VARIABLE(metaPtr); |
| OT_UNUSED_VARIABLE(metaLen); |
| OT_UNUSED_VARIABLE(parsedLength); |
| |
| SuccessOrExit(error = otMessageAppend(message, framePtr, static_cast<uint16_t>(frameLen))); |
| |
| error = otIp6Send(mInstance, message); |
| |
| // `otIp6Send()` takes ownership of `message` (in both success or |
| // failure cases). `message` is set to NULL so it is not freed at |
| // exit. |
| message = NULL; |
| |
| exit: |
| |
| if (message != NULL) |
| { |
| otMessageFree(message); |
| } |
| |
| if (error == OT_ERROR_NONE) |
| { |
| mInboundSecureIpFrameCounter++; |
| |
| if (SPINEL_HEADER_GET_TID(aHeader) != 0) |
| { |
| // Only send a successful status update if |
| // there was a transaction id in the aHeader. |
| error = SendLastStatus(aHeader, SPINEL_STATUS_OK); |
| } |
| } |
| else |
| { |
| mDroppedInboundIpFrameCounter++; |
| |
| error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error)); |
| } |
| |
| OT_UNUSED_VARIABLE(aKey); |
| |
| return error; |
| } |
| |
| otError NcpBase::SetPropertyHandler_IPV6_ML_PREFIX(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aValueLen >= 8, error = OT_ERROR_PARSE); |
| |
| error = otThreadSetMeshLocalPrefix(mInstance, aValuePtr); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_IPV6_ICMP_PING_OFFLOAD(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| bool enabled = false; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &enabled |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otIcmp6SetEchoEnabled(mInstance, enabled); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| bool enabled = false; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &enabled |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| // Note reverse logic: passthru enabled = filter disabled |
| otIp6SetReceiveFilterEnabled(mInstance, !enabled); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_DISCOVERY_SCAN_JOINER_FLAG(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| bool joinerFlag = false; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &joinerFlag |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| mDiscoveryScanJoinerFlag = joinerFlag; |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| bool enabled = false; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &enabled |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| mDiscoveryScanEnableFiltering = enabled; |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_DISCOVERY_SCAN_PANID(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint16_t panid; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT16_S, |
| &panid |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| mDiscoveryScanPanId = panid; |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint8_t numEntries = 0; |
| const uint16_t *ports = otIp6GetUnsecurePorts(mInstance, &numEntries); |
| spinel_ssize_t parsedLength; |
| bool portsChanged = false; |
| otError error = OT_ERROR_NONE; |
| |
| // First, we need to remove all of the current assisting ports. |
| for (; numEntries != 0; ports++, numEntries--) |
| { |
| SuccessOrExit(error = otIp6RemoveUnsecurePort(mInstance, *ports)); |
| portsChanged = true; |
| } |
| |
| while (aValueLen >= 2) |
| { |
| uint16_t port; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT16_S, |
| &port |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| SuccessOrExit(error = otIp6AddUnsecurePort(mInstance, port)); |
| |
| aValuePtr += parsedLength; |
| aValueLen -= parsedLength; |
| |
| portsChanged = true; |
| } |
| |
| // No error happened so the new state of ports will |
| // be reported in the response. |
| portsChanged = false; |
| |
| exit: |
| error = SendSetPropertyResponse(aHeader, aKey, error); |
| |
| if (portsChanged) |
| { |
| // 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, aKey); |
| } |
| |
| return error; |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| otError NcpBase::SetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| bool value = false; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| bool shouldRegisterWithLeader = false; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &value |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| // Register any net data changes on transition from `true` to `false`. |
| shouldRegisterWithLeader = (mAllowLocalNetworkDataChange == true) && (value == false); |
| |
| mAllowLocalNetworkDataChange = value; |
| |
| exit: |
| error = SendSetPropertyResponse(aHeader, aKey, error); |
| |
| if (shouldRegisterWithLeader) |
| { |
| otBorderRouterRegister(mInstance); |
| } |
| |
| return error; |
| } |
| #endif // OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| #if OPENTHREAD_FTD |
| otError NcpBase::SetPropertyHandler_THREAD_ROUTER_ROLE_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| bool enabled; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &enabled |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otThreadSetRouterRoleEnabled(mInstance, enabled); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| #if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB |
| otError NcpBase::SetPropertyHandler_THREAD_STEERING_DATA(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| otExtAddress *extAddress; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_EUI64_S, |
| &extAddress |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| error = otThreadSetSteeringData(mInstance, extAddress); |
| |
| exit: |
| |
| if (error == OT_ERROR_NONE) |
| { |
| // Note that there is no get handler for this property. |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_EUI64_S, |
| extAddress->m8 |
| ); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error)); |
| } |
| |
| return error; |
| } |
| #endif // #if OPENTHREAD_CONFIG_ENABLE_STEERING_DATA_SET_OOB |
| #endif // #if OPENTHREAD_FTD |
| |
| otError NcpBase::SetPropertyHandler_CNTR_RESET(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| uint8_t value = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &value |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| VerifyOrExit(value == 1, error = OT_ERROR_INVALID_ARGS); |
| |
| // TODO: Implement counter reset! |
| error = OT_ERROR_NOT_IMPLEMENTED; |
| |
| exit: |
| OT_UNUSED_VARIABLE(aKey); |
| |
| // 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(aHeader, ThreadErrorToSpinelStatus(error)); |
| } |
| |
| #if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD |
| |
| otError NcpBase::SetPropertyHandler_THREAD_COMMISSIONER_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| bool enabled = false; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &enabled |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| if (enabled == false) |
| { |
| error = otCommissionerStop(mInstance); |
| } |
| else |
| { |
| error = otCommissionerStart(mInstance); |
| } |
| |
| exit: |
| OT_UNUSED_VARIABLE(aKey); |
| |
| return SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error)); |
| } |
| |
| #endif // OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD |
| |
| #if OPENTHREAD_ENABLE_MAC_WHITELIST |
| |
| otError NcpBase::SetPropertyHandler_MAC_WHITELIST(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| bool reportAsync; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| // First, clear the whitelist. |
| otLinkClearWhitelist(mInstance); |
| |
| while (aValueLen > 0) |
| { |
| otExtAddress *extAddress = NULL; |
| int8_t rssi = RSSI_OVERRIDE_DISABLED; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_STRUCT_S( |
| SPINEL_DATATYPE_EUI64_S |
| SPINEL_DATATYPE_INT8_S |
| ), |
| &extAddress, |
| &rssi |
| ); |
| |
| if (parsedLength <= 0) |
| { |
| rssi = RSSI_OVERRIDE_DISABLED; |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_STRUCT_S( |
| SPINEL_DATATYPE_EUI64_S |
| ), |
| &extAddress |
| ); |
| } |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| if (rssi == RSSI_OVERRIDE_DISABLED) |
| { |
| SuccessOrExit(error = otLinkAddWhitelist(mInstance, extAddress->m8)); |
| } |
| else |
| { |
| SuccessOrExit(error = otLinkAddWhitelistRssi(mInstance, extAddress->m8, rssi)); |
| } |
| |
| aValuePtr += parsedLength; |
| aValueLen -= parsedLength; |
| } |
| |
| exit: |
| // If we had an error, we may have actually changed |
| // the state of the whitelist---so we need to report |
| // those incomplete changes via an asynchronous |
| // change event. |
| reportAsync = (error != OT_ERROR_NONE); |
| |
| error = SendSetPropertyResponse(aHeader, aKey, error); |
| |
| if (reportAsync) |
| { |
| HandleCommandPropertyGet(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, aKey); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_WHITELIST_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| bool enabled; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &enabled |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otLinkSetWhitelistEnabled(mInstance, enabled); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_BLACKLIST(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| bool reportAsync; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| // First, clear the blacklist. |
| otLinkClearBlacklist(mInstance); |
| |
| while (aValueLen > 0) |
| { |
| otExtAddress *ext_addr = NULL; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_STRUCT_S( |
| SPINEL_DATATYPE_EUI64_S |
| ), |
| &ext_addr |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| SuccessOrExit(error = otLinkAddBlacklist(mInstance, ext_addr->m8)); |
| |
| aValuePtr += parsedLength; |
| aValueLen -= parsedLength; |
| } |
| |
| exit: |
| // If we had an error, we may have actually changed |
| // the state of the blacklist---so we need to report |
| // those incomplete changes via an asynchronous |
| // change event. |
| reportAsync = (error != OT_ERROR_NONE); |
| |
| error = SendSetPropertyResponse(aHeader, aKey, error); |
| |
| if (reportAsync) |
| { |
| HandleCommandPropertyGet(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, aKey); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_BLACKLIST_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| bool enabled; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &enabled |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otLinkSetBlacklistEnabled(mInstance, enabled); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| #endif // OPENTHREAD_ENABLE_MAC_WHITELIST |
| |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| otError NcpBase::SetPropertyHandler_MAC_SRC_MATCH_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| bool enabled; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &enabled |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| error = otLinkRawSrcMatchEnable(mInstance, enabled); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| const uint8_t *data = aValuePtr; |
| uint16_t dataLen = aValueLen; |
| |
| // Clear the list first |
| error = otLinkRawSrcMatchClearShortEntries(mInstance); |
| |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| // Loop through the addresses and add them |
| while (dataLen >= sizeof(uint16_t)) |
| { |
| spinel_ssize_t parsedLength; |
| uint16_t short_address; |
| |
| parsedLength = spinel_datatype_unpack( |
| data, |
| dataLen, |
| SPINEL_DATATYPE_UINT16_S, |
| &short_address |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| data += parsedLength; |
| dataLen -= (uint16_t)parsedLength; |
| |
| error = otLinkRawSrcMatchAddShortEntry(mInstance, short_address); |
| |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| } |
| |
| error = |
| SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| const uint8_t *data = aValuePtr; |
| uint16_t dataLen = aValueLen; |
| |
| // Clear the list first |
| error = otLinkRawSrcMatchClearExtEntries(mInstance); |
| |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| // Loop through the addresses and add them |
| while (dataLen >= sizeof(otExtAddress)) |
| { |
| spinel_ssize_t parsedLength; |
| uint8_t *extAddress; |
| |
| parsedLength = spinel_datatype_unpack( |
| data, |
| dataLen, |
| SPINEL_DATATYPE_EUI64_S, |
| &extAddress |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| data += parsedLength; |
| dataLen -= (uint16_t)parsedLength; |
| |
| error = otLinkRawSrcMatchAddExtEntry(mInstance, extAddress); |
| |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| } |
| |
| error = |
| SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| #endif |
| |
| #if OPENTHREAD_FTD |
| |
| otError NcpBase::SetPropertyHandler_NET_PSKC(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| const uint8_t *ptr = NULL; |
| spinel_size_t len; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_DATA_S, |
| &ptr, |
| &len |
| ); |
| |
| VerifyOrExit((parsedLength > 0) && (len == sizeof(spinel_net_pskc_t)), error = OT_ERROR_PARSE); |
| |
| error = otThreadSetPSKc(mInstance, ptr); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| #endif // OPENTHREAD_FTD |
| |
| otError NcpBase::SetPropertyHandler_THREAD_MODE(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| uint8_t numericMode = 0; |
| otLinkModeConfig modeConfig; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &numericMode |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| modeConfig.mRxOnWhenIdle = ((numericMode & SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE) == SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE); |
| modeConfig.mSecureDataRequests = |
| ((numericMode & SPINEL_THREAD_MODE_SECURE_DATA_REQUEST) == SPINEL_THREAD_MODE_SECURE_DATA_REQUEST); |
| modeConfig.mDeviceType = ((numericMode & SPINEL_THREAD_MODE_FULL_FUNCTION_DEV) == SPINEL_THREAD_MODE_FULL_FUNCTION_DEV); |
| modeConfig.mNetworkData = ((numericMode & SPINEL_THREAD_MODE_FULL_NETWORK_DATA) == SPINEL_THREAD_MODE_FULL_NETWORK_DATA); |
| |
| error = otThreadSetLinkMode(mInstance, modeConfig); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| #if OPENTHREAD_FTD |
| |
| otError NcpBase::SetPropertyHandler_THREAD_CHILD_COUNT_MAX(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint8_t maxChildren = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &maxChildren |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| error = otThreadSetMaxAllowedChildren(mInstance, maxChildren); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_CHILD_TIMEOUT(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint32_t timeout = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT32_S, |
| &timeout |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otThreadSetChildTimeout(mInstance, timeout); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_ROUTER_UPGRADE_THRESHOLD(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint8_t threshold = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &threshold |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otThreadSetRouterUpgradeThreshold(mInstance, threshold); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_ROUTER_DOWNGRADE_THRESHOLD(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint8_t threshold = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &threshold |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otThreadSetRouterDowngradeThreshold(mInstance, threshold); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_ROUTER_SELECTION_JITTER(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint8_t jitter = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &jitter |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otThreadSetRouterSelectionJitter(mInstance, jitter); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_PREFERRED_ROUTER_ID(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint8_t routerId = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &routerId |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| error = otThreadSetPreferredRouterId(mInstance, routerId); |
| |
| exit: |
| if (error == OT_ERROR_NONE) |
| { |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| SPINEL_DATATYPE_UINT8_S, |
| routerId |
| ); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, ThreadErrorToSpinelStatus(error)); |
| } |
| |
| return error; |
| } |
| |
| #endif // OPENTHREAD_FTD |
| |
| otError NcpBase::SetPropertyHandler_DEBUG_NCP_LOG_LEVEL(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint8_t spinelNcpLogLevel = 0; |
| otLogLevel logLevel; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &spinelNcpLogLevel |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| 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: |
| 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 SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| #if OPENTHREAD_FTD |
| |
| otError NcpBase::SetPropertyHandler_THREAD_CONTEXT_REUSE_DELAY(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint32_t delay = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT32_S, |
| &delay |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otThreadSetContextIdReuseDelay(mInstance, delay); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_NETWORK_ID_TIMEOUT(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint8_t timeout = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &timeout |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| otThreadSetNetworkIdTimeout(mInstance, timeout); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| #endif // OPENTHREAD_FTD |
| |
| #if OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD |
| |
| otError NcpBase::SetPropertyHandler_THREAD_TMF_PROXY_ENABLED(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| bool enabled; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &enabled |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| if (enabled) |
| { |
| error = otTmfProxyStart(mInstance, &NcpBase::HandleTmfProxyStream, this); |
| } |
| else |
| { |
| error = otTmfProxyStop(mInstance); |
| } |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| #endif // OPENTHREAD_ENABLE_TMF_PROXY && OPENTHREAD_FTD |
| |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| |
| otError NcpBase::SetPropertyHandler_JAM_DETECT_ENABLE(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| bool enabled; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_BOOL_S, |
| &enabled |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| if (enabled) |
| { |
| otJamDetectionStart(mInstance, &NcpBase::HandleJamStateChange_Jump, this); |
| } |
| else |
| { |
| otJamDetectionStop(mInstance); |
| } |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| int8_t threshold = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_INT8_S, |
| &threshold |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| error = otJamDetectionSetRssiThreshold(mInstance, threshold); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_JAM_DETECT_WINDOW(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| uint8_t window = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &window |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| error = otJamDetectionSetWindow(mInstance, window); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| otError NcpBase::SetPropertyHandler_JAM_DETECT_BUSY(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| uint8_t busy = 0; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &busy |
| ); |
| |
| VerifyOrExit(parsedLength > 0, error = OT_ERROR_PARSE); |
| |
| error = otJamDetectionSetBusyPeriod(mInstance, busy); |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| void NcpBase::HandleJamStateChange_Jump(bool aJamState, void *aContext) |
| { |
| static_cast<NcpBase *>(aContext)->HandleJamStateChange(aJamState); |
| } |
| |
| void NcpBase::HandleJamStateChange(bool aJamState) |
| { |
| otError error; |
| |
| error = 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 (error != OT_ERROR_NONE) |
| { |
| mShouldSignalJamStateChange = true; |
| } |
| } |
| |
| #endif // OPENTHREAD_ENABLE_JAM_DETECTION |
| |
| #if OPENTHREAD_ENABLE_DIAG |
| |
| otError NcpBase::SetPropertyHandler_NEST_STREAM_MFG(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, |
| uint16_t aValueLen) |
| { |
| char *string = NULL; |
| char *output = NULL; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UTF8_S, |
| &string |
| ); |
| |
| if ((parsedLength > 0) && (string != NULL)) |
| { |
| // all diagnostics related features are processed within diagnostics module |
| output = otDiagProcessCmdLine(string); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_IS, |
| aKey, |
| reinterpret_cast<uint8_t *>(output), |
| static_cast<uint16_t>(strlen(output) + 1) |
| ); |
| } |
| else |
| { |
| error = SendLastStatus(aHeader, SPINEL_STATUS_PARSE_ERROR); |
| } |
| |
| return error; |
| } |
| |
| #endif // OPENTHREAD_ENABLE_DIAG |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| |
| otError NcpBase::SetPropertyHandler_NEST_LEGACY_ULA_PREFIX(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| const uint8_t *ptr = NULL; |
| spinel_size_t len; |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_DATA_S, |
| &ptr, |
| &len |
| ); |
| |
| VerifyOrExit((parsedLength > 0) && (len <= sizeof(mLegacyUlaPrefix)), error = OT_ERROR_PARSE); |
| |
| memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix)); |
| memcpy(mLegacyUlaPrefix, ptr, len); |
| |
| if ((mLegacyHandlers != NULL) && (mLegacyHandlers->mSetLegacyUlaPrefix != NULL)) |
| { |
| mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix); |
| } |
| |
| exit: |
| return SendSetPropertyResponse(aHeader, aKey, error); |
| } |
| |
| #endif // OPENTHREAD_ENABLE_LEGACY |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Individual Property Inserters |
| // ---------------------------------------------------------------------------- |
| |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| otError NcpBase::InsertPropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| uint16_t short_address; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT16_S, |
| &short_address |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| error = otLinkRawSrcMatchAddShortEntry(mInstance, short_address); |
| |
| VerifyOrExit(error == OT_ERROR_NONE, |
| spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::InsertPropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| uint8_t *extAddress = NULL; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_EUI64_S, |
| &extAddress |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| error = otLinkRawSrcMatchAddExtEntry(mInstance, extAddress); |
| |
| VerifyOrExit(error == OT_ERROR_NONE, |
| spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| #endif |
| |
| otError NcpBase::InsertPropertyHandler_IPV6_ADDRESS_TABLE(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| otNetifAddress netifAddr; |
| otIp6Address *addrPtr; |
| uint32_t preferredLifetime; |
| uint32_t validLifetime; |
| uint8_t prefixLen; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| ( |
| SPINEL_DATATYPE_IPv6ADDR_S // IPv6 address |
| SPINEL_DATATYPE_UINT8_S // Prefix length (in bits) |
| SPINEL_DATATYPE_UINT32_S // Preferred lifetime |
| SPINEL_DATATYPE_UINT32_S // Valid lifetime |
| ), |
| &addrPtr, |
| &prefixLen, |
| &preferredLifetime, |
| &validLifetime |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| netifAddr.mAddress = *addrPtr; |
| netifAddr.mPrefixLength = prefixLen; |
| netifAddr.mPreferred = preferredLifetime != 0; |
| netifAddr.mValid = validLifetime != 0; |
| |
| error = otIp6AddUnicastAddress(mInstance, &netifAddr); |
| |
| VerifyOrExit(error == OT_ERROR_NONE, |
| spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| spinelError = SPINEL_STATUS_OK; |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| otError NcpBase::InsertPropertyHandler_THREAD_OFF_MESH_ROUTES(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| otExternalRouteConfig routeConfig; |
| otIp6Address *addrPtr; |
| bool stable = false; |
| uint8_t flags = 0; |
| |
| memset(&routeConfig, 0, sizeof(otExternalRouteConfig)); |
| |
| VerifyOrExit(mAllowLocalNetworkDataChange == true, spinelError = SPINEL_STATUS_INVALID_STATE); |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| ( |
| SPINEL_DATATYPE_IPv6ADDR_S // Route prefix |
| SPINEL_DATATYPE_UINT8_S // Prefix length (in bits) |
| SPINEL_DATATYPE_BOOL_S // Stable |
| SPINEL_DATATYPE_UINT8_S // Flags (Route Preference) |
| ), |
| &addrPtr, |
| &routeConfig.mPrefix.mLength, |
| &stable, |
| &flags |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| routeConfig.mPrefix.mPrefix = *addrPtr; |
| routeConfig.mStable = stable; |
| routeConfig.mPreference = FlagByteToExternalRoutePreference(flags); |
| |
| error = otBorderRouterAddRoute(mInstance, &routeConfig); |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::InsertPropertyHandler_THREAD_ON_MESH_NETS(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| otBorderRouterConfig borderRouterConfig; |
| otIp6Address *addrPtr; |
| bool stable = false; |
| uint8_t flags = 0; |
| |
| memset(&borderRouterConfig, 0, sizeof(otBorderRouterConfig)); |
| |
| VerifyOrExit(mAllowLocalNetworkDataChange == true, spinelError = SPINEL_STATUS_INVALID_STATE); |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| ( |
| SPINEL_DATATYPE_IPv6ADDR_S // On-mesh prefix |
| SPINEL_DATATYPE_UINT8_S // Prefix length (in bits) |
| SPINEL_DATATYPE_BOOL_S // Stable |
| SPINEL_DATATYPE_UINT8_S // Flags |
| ), |
| &addrPtr, |
| &borderRouterConfig.mPrefix.mLength, |
| &stable, |
| &flags |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| borderRouterConfig.mPrefix.mPrefix = *addrPtr; |
| borderRouterConfig.mStable = stable; |
| borderRouterConfig.mPreference = |
| ((flags & SPINEL_NET_FLAG_PREFERENCE_MASK) >> SPINEL_NET_FLAG_PREFERENCE_OFFSET); |
| borderRouterConfig.mPreferred = ((flags & SPINEL_NET_FLAG_PREFERRED) != 0); |
| borderRouterConfig.mSlaac = ((flags & SPINEL_NET_FLAG_SLAAC) != 0); |
| borderRouterConfig.mDhcp = ((flags & SPINEL_NET_FLAG_DHCP) != 0); |
| borderRouterConfig.mConfigure = ((flags & SPINEL_NET_FLAG_CONFIGURE) != 0); |
| borderRouterConfig.mDefaultRoute = ((flags & SPINEL_NET_FLAG_DEFAULT_ROUTE) != 0); |
| borderRouterConfig.mOnMesh = ((flags & SPINEL_NET_FLAG_ON_MESH) != 0); |
| |
| error = otBorderRouterAddOnMeshPrefix(mInstance, &borderRouterConfig); |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| #endif // OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| otError NcpBase::InsertPropertyHandler_THREAD_ASSISTING_PORTS(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| uint16_t port; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT16_S, |
| &port |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| error = otIp6AddUnsecurePort(mInstance, port); |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| #if OPENTHREAD_ENABLE_MAC_WHITELIST |
| |
| otError NcpBase::InsertPropertyHandler_MAC_WHITELIST(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| otExtAddress *extAddress = NULL; |
| int8_t rssi = RSSI_OVERRIDE_DISABLED; |
| |
| if (aValueLen > static_cast<spinel_ssize_t>(sizeof(otExtAddress))) |
| { |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_INT8_S, |
| &extAddress, |
| &rssi |
| ); |
| } |
| else |
| { |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_EUI64_S, |
| &extAddress |
| ); |
| } |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| if (rssi == RSSI_OVERRIDE_DISABLED) |
| { |
| error = otLinkAddWhitelist(mInstance, extAddress->m8); |
| } |
| else |
| { |
| error = otLinkAddWhitelistRssi(mInstance, extAddress->m8, rssi); |
| } |
| |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::InsertPropertyHandler_MAC_BLACKLIST(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| otExtAddress *extAddress = NULL; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_EUI64_S, |
| &extAddress |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| error = otLinkAddBlacklist(mInstance, extAddress->m8); |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| #endif // OPENTHREAD_ENABLE_MAC_WHITELIST |
| |
| #if OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD |
| otError NcpBase::InsertPropertyHandler_THREAD_JOINERS(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| otExtAddress *extAddress = NULL; |
| const char *aPSKd = NULL; |
| uint32_t joinerTimeout = 0; |
| |
| VerifyOrExit(mAllowLocalNetworkDataChange == true, spinelError = SPINEL_STATUS_INVALID_STATE); |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| ( |
| SPINEL_DATATYPE_UTF8_S // PSK |
| SPINEL_DATATYPE_UINT32_S // Timeout |
| SPINEL_DATATYPE_EUI64_S // Extended address |
| ), |
| &aPSKd, |
| &joinerTimeout, |
| &extAddress |
| ); |
| |
| if (parsedLength <= 0) |
| { |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| ( |
| SPINEL_DATATYPE_UTF8_S // PSK |
| SPINEL_DATATYPE_UINT32_S // Timeout |
| ), |
| &aPSKd, |
| &joinerTimeout |
| ); |
| extAddress = NULL; |
| } |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| error = otCommissionerAddJoiner(mInstance, extAddress, aPSKd, joinerTimeout); |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_INSERTED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| #endif // OPENTHREAD_ENABLE_COMMISSIONER && OPENTHREAD_FTD |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Individual Property Removers |
| // ---------------------------------------------------------------------------- |
| |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| otError NcpBase::RemovePropertyHandler_MAC_SRC_MATCH_SHORT_ADDRESSES(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| uint16_t shortAddress; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT16_S, |
| &shortAddress |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| error = otLinkRawSrcMatchClearShortEntry(mInstance, shortAddress); |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::RemovePropertyHandler_MAC_SRC_MATCH_EXTENDED_ADDRESSES(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| uint8_t *extAddress; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_EUI64_S, |
| &extAddress |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| error = otLinkRawSrcMatchClearExtEntry(mInstance, extAddress); |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| #endif // #if OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| otError NcpBase::RemovePropertyHandler_IPV6_ADDRESS_TABLE(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| otIp6Address *addrPtr; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_IPv6ADDR_S, |
| &addrPtr |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| error = otIp6RemoveUnicastAddress(mInstance, addrPtr); |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| otError NcpBase::RemovePropertyHandler_THREAD_OFF_MESH_ROUTES(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| otIp6Prefix ip6Prefix; |
| otIp6Address *addrPtr; |
| |
| memset(&ip6Prefix, 0, sizeof(otIp6Prefix)); |
| |
| VerifyOrExit(mAllowLocalNetworkDataChange == true, spinelError = SPINEL_STATUS_INVALID_STATE); |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| ( |
| SPINEL_DATATYPE_IPv6ADDR_S // Route prefix |
| SPINEL_DATATYPE_UINT8_S // Prefix length (in bits) |
| ), |
| &addrPtr, |
| &ip6Prefix.mLength |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| ip6Prefix.mPrefix = *addrPtr; |
| |
| error = otBorderRouterRemoveRoute(mInstance, &ip6Prefix); |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::RemovePropertyHandler_THREAD_ON_MESH_NETS(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| otIp6Prefix ip6Prefix; |
| otIp6Address *addrPtr; |
| |
| memset(&ip6Prefix, 0, sizeof(otIp6Prefix)); |
| |
| VerifyOrExit(mAllowLocalNetworkDataChange == true, spinelError = SPINEL_STATUS_INVALID_STATE); |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| ( |
| SPINEL_DATATYPE_IPv6ADDR_S // On-mesh prefix |
| SPINEL_DATATYPE_UINT8_S // Prefix length (in bits) |
| ), |
| &addrPtr, |
| &ip6Prefix.mLength |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| ip6Prefix.mPrefix = *addrPtr; |
| |
| error = otBorderRouterRemoveOnMeshPrefix(mInstance, &ip6Prefix); |
| |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| #endif |
| |
| otError NcpBase::RemovePropertyHandler_THREAD_ASSISTING_PORTS(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| uint16_t port; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT16_S, |
| &port |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| error = otIp6RemoveUnsecurePort(mInstance, port); |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| #if OPENTHREAD_FTD |
| otError NcpBase::RemovePropertyHandler_THREAD_ACTIVE_ROUTER_IDS(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| uint8_t routerId; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_UINT8_S, |
| &routerId |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| error = otThreadReleaseRouterId(mInstance, routerId); |
| VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| #endif // OPENTHREAD_FTD |
| |
| #if OPENTHREAD_ENABLE_MAC_WHITELIST |
| |
| otError NcpBase::RemovePropertyHandler_MAC_WHITELIST(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| otExtAddress *extAddress = NULL; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_EUI64_S, |
| &extAddress |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| otLinkRemoveWhitelist(mInstance, extAddress->m8); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::RemovePropertyHandler_MAC_BLACKLIST(uint8_t aHeader, spinel_prop_key_t aKey, |
| const uint8_t *aValuePtr, uint16_t aValueLen) |
| { |
| spinel_ssize_t parsedLength; |
| otError error = OT_ERROR_NONE; |
| spinel_status_t spinelError = SPINEL_STATUS_OK; |
| otExtAddress *extAddress = NULL; |
| |
| parsedLength = spinel_datatype_unpack( |
| aValuePtr, |
| aValueLen, |
| SPINEL_DATATYPE_EUI64_S, |
| &extAddress |
| ); |
| |
| VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); |
| |
| otLinkRemoveBlacklist(mInstance, extAddress->m8); |
| |
| error = SendPropertyUpdate( |
| aHeader, |
| SPINEL_CMD_PROP_VALUE_REMOVED, |
| aKey, |
| aValuePtr, |
| aValueLen |
| ); |
| |
| exit: |
| |
| if (spinelError != SPINEL_STATUS_OK) |
| { |
| error = SendLastStatus(aHeader, spinelError); |
| } |
| |
| return error; |
| } |
| |
| #endif // OPENTHREAD_ENABLE_MAC_WHITELIST |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| |
| void NcpBase::RegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers) |
| { |
| mLegacyHandlers = aHandlers; |
| bool isEnabled; |
| |
| VerifyOrExit(mLegacyHandlers != NULL); |
| |
| isEnabled = (otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED); |
| |
| if (isEnabled) |
| { |
| if (mLegacyHandlers->mStartLegacy) |
| { |
| mLegacyHandlers->mStartLegacy(); |
| } |
| } |
| else |
| { |
| if (mLegacyHandlers->mStopLegacy) |
| { |
| mLegacyHandlers->mStopLegacy(); |
| } |
| } |
| |
| if (mLegacyHandlers->mSetLegacyUlaPrefix) |
| { |
| mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix); |
| } |
| |
| exit: |
| return; |
| } |
| |
| void NcpBase::HandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix) |
| { |
| memcpy(mLegacyUlaPrefix, aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH); |
| |
| SuccessOrExit(OutboundFrameBegin()); |
| |
| SuccessOrExit( |
| OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_DATA_S, |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_NEST_LEGACY_ULA_PREFIX, |
| aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH |
| )); |
| |
| SuccessOrExit(OutboundFrameSend()); |
| |
| exit: |
| return; |
| } |
| |
| void NcpBase::HandleLegacyNodeDidJoin(const otExtAddress *aExtAddr) |
| { |
| mLegacyNodeDidJoin = true; |
| |
| SuccessOrExit(OutboundFrameBegin()); |
| |
| SuccessOrExit( |
| OutboundFrameFeedPacked( |
| SPINEL_DATATYPE_COMMAND_PROP_S SPINEL_DATATYPE_EUI64_S, |
| SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, |
| SPINEL_CMD_PROP_VALUE_IS, |
| SPINEL_PROP_NEST_LEGACY_JOINED_NODE, |
| aExtAddr->m8 |
| )); |
| |
| SuccessOrExit(OutboundFrameSend()); |
| |
| exit: |
| return; |
| } |
| |
| #endif // OPENTHREAD_ENABLE_LEGACY |
| |
| otError NcpBase::StreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aStreamId == 0) |
| { |
| aStreamId = SPINEL_PROP_STREAM_DEBUG; |
| } |
| |
| VerifyOrExit(!mDisableStreamWrite, error = OT_ERROR_INVALID_STATE); |
| |
| error = 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) |
| ); |
| |
| exit: |
| return error; |
| } |
| |
| #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 |
| |
| } // namespace ot |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Virtual Datastream I/O (Public API) |
| // ---------------------------------------------------------------------------- |
| |
| otError otNcpStreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen) |
| { |
| otError error = OT_ERROR_INVALID_STATE; |
| ot::NcpBase *ncp = ot::NcpBase::GetNcpInstance(); |
| |
| if (ncp != NULL) |
| { |
| error = ncp->StreamWrite(aStreamId, aDataPtr, aDataLen); |
| } |
| |
| return error; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Peek/Poke delegate API |
| // ---------------------------------------------------------------------------- |
| |
| otError otNcpRegisterPeekPokeDelagates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, |
| otNcpDelegateAllowPeekPoke aAllowPokeDelegate) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE |
| ot::NcpBase *ncp = ot::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: Legacy network APIs |
| // ---------------------------------------------------------------------------- |
| |
| void otNcpRegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers) |
| { |
| #if OPENTHREAD_ENABLE_LEGACY |
| ot::NcpBase *ncp = ot::NcpBase::GetNcpInstance(); |
| |
| if (ncp != NULL) |
| { |
| ncp->RegisterLegacyHandlers(aHandlers); |
| } |
| |
| #else |
| OT_UNUSED_VARIABLE(aHandlers); |
| #endif |
| } |
| |
| void otNcpHandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix) |
| { |
| #if OPENTHREAD_ENABLE_LEGACY |
| ot::NcpBase *ncp = ot::NcpBase::GetNcpInstance(); |
| |
| if (ncp != NULL) |
| { |
| ncp->HandleDidReceiveNewLegacyUlaPrefix(aUlaPrefix); |
| } |
| |
| #else |
| OT_UNUSED_VARIABLE(aUlaPrefix); |
| #endif |
| } |
| |
| void otNcpHandleLegacyNodeDidJoin(const otExtAddress *aExtAddr) |
| { |
| #if OPENTHREAD_ENABLE_LEGACY |
| ot::NcpBase *ncp = ot::NcpBase::GetNcpInstance(); |
| |
| if (ncp != NULL) |
| { |
| ncp->HandleLegacyNodeDidJoin(aExtAddr); |
| } |
| |
| #else |
| OT_UNUSED_VARIABLE(aExtAddr); |
| #endif |
| } |