<commit_before>/*
 *
 * Copyright (c) 2016 <NAME>, Inc.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include "SpinelNCPInstance.h"
#include "time-utils.h"
#include "assert-macros.h"
#include <syslog.h>
#include <errno.h>
#include "socket-utils.h"
#include <stdexcept>
#include <sys/file.h>
#include "SuperSocket.h"
#include "SpinelNCPTask.h"
#include "SpinelNCPTaskWake.h"
#include "SpinelNCPTaskSendCommand.h"
#include "SpinelNCPTaskJoin.h"
#include "SpinelNCPTaskGetNetworkTopology.h"
#include "SpinelNCPTaskGetMsgBufferCounters.h"
#include "any-to.h"
#include "spinel-extra.h"

#define kWPANTUNDProperty_Spinel_CounterPrefix		"NCP:Counter:"

using namespace nl;
using namespace wpantund;

WPANTUND_DEFINE_NCPINSTANCE_PLUGIN(spinel, SpinelNCPInstance);

void
SpinelNCPInstance::handle_ncp_log(const uint8_t* data_ptr, int data_len)
{
	static char linebuffer[NCP_DEBUG_LINE_LENGTH_MAX + 1];
	static int linepos = 0;
	while (data_len--) {
		char nextchar = *data_ptr++;

		if ((nextchar == '\t') || (nextchar >= 32)) {
			linebuffer[linepos++] = nextchar;
		}

		if ( (linepos != 0)
		  && ( (nextchar == '\n')
			|| (nextchar == '\r')
			|| (linepos >= (sizeof(linebuffer) - 1))
		  )
		)
		{
			// flush.
			linebuffer[linepos] = 0;
			syslog(LOG_WARNING, "NCP => %s\n", linebuffer);
			linepos = 0;
		}
	}
}

void
SpinelNCPInstance::start_new_task(const boost::shared_ptr<SpinelNCPTask> &task)
{
	if (ncp_state_is_detached_from_ncp(get_ncp_state())) {
		task->finish(kWPANTUNDStatus_InvalidWhenDisabled);
	} else if (PT_SCHEDULE(task->process_event(EVENT_STARTING_TASK))) {

		if (ncp_state_is_sleeping(get_ncp_state())
			&& (dynamic_cast<const SpinelNCPTaskWake*>(task.get()) == NULL)
		) {
			start_new_task(boost::shared_ptr<SpinelNCPTask>(new SpinelNCPTaskWake(this, NilReturn())));
		}
		mTaskQueue.push_back(task);
	}
}

int
nl::wpantund::spinel_status_to_wpantund_status(int spinel_status)
{
	wpantund_status_t ret;
	switch (spinel_status) {
	case SPINEL_STATUS_ALREADY:
		ret = kWPANTUNDStatus_Already;
		break;
	case SPINEL_STATUS_BUSY:
		ret = kWPANTUNDStatus_Busy;
		break;
	case SPINEL_STATUS_IN_PROGRESS:
		ret = kWPANTUNDStatus_InProgress;
		break;
	case SPINEL_STATUS_JOIN_FAILURE:
		ret = kWPANTUNDStatus_JoinFailedUnknown;
		break;
	case SPINEL_STATUS_JOIN_INCOMPATIBLE:
		ret = kWPANTUNDStatus_JoinFailedAtScan;
		break;
	case SPINEL_STATUS_JOIN_SECURITY:
		ret = kWPANTUNDStatus_JoinFailedAtAuthenticate;
		break;
	case SPINEL_STATUS_OK:
		ret = kWPANTUNDStatus_Ok;
		break;
	case SPINEL_STATUS_PROP_NOT_FOUND:
		ret = kWPANTUNDStatus_PropertyNotFound;
		break;
	case SPINEL_STATUS_INVALID_ARGUMENT:
		ret = kWPANTUNDStatus_NCP_InvalidArgument;
		break;
	case SPINEL_STATUS_INVALID_STATE:
		ret = kWPANTUNDStatus_InvalidForCurrentState;
		break;
	default:
		ret = WPANTUND_NCPERROR_TO_STATUS(spinel_status);
		break;
	}

	return ret;
}

int
nl::wpantund::peek_ncp_callback_status(int event, va_list args)
{
	int ret = 0;

	if (EVENT_NCP_PROP_VALUE_IS == event) {
		va_list tmp;
		va_copy(tmp, args);
		unsigned int key = va_arg(tmp, unsigned int);
		if (SPINEL_PROP_LAST_STATUS == key) {
			const uint8_t* spinel_data_ptr = va_arg(tmp, const uint8_t*);
			spinel_size_t spinel_data_len = va_arg(tmp, spinel_size_t);

			if (spinel_datatype_unpack(spinel_data_ptr, spinel_data_len, "i", &ret) <= 0) {
				ret = SPINEL_STATUS_PARSE_ERROR;
			}
		}
		va_end(tmp);
	} else if (EVENT_NCP_RESET == event) {
		va_list tmp;
		va_copy(tmp, args);
		ret = va_arg(tmp, int);
		va_end(tmp);
	}

	return ret;
}

SpinelNCPInstance::SpinelNCPInstance(const Settings& settings) :
	NCPInstanceBase(settings), mControlInterface(this)
{
	mOutboundBufferLen = 0;
	mInboundHeader = 0;
	mDefaultChannelMask = 0x07FFF800;
	mIsPcapInProgress = false;
	mSettings.clear();

	if (!settings.empty()) {
		int status;
		Settings::const_iterator iter;

		for(iter = settings.begin(); iter != settings.end(); iter++) {
			if (!NCPInstanceBase::setup_property_supported_by_class(iter->first)) {
				status = static_cast<NCPControlInterface&>(get_control_interface())
					.set_property(iter->first, iter->second);

				if (status != 0) {
					syslog(LOG_WARNING, "Attempt to set property \"%s\" failed with err %d", iter->first.c_str(), status);
				}
			}
		}
	}
}

SpinelNCPInstance::~SpinelNCPInstance()
{
}


bool
SpinelNCPInstance::setup_property_supported_by_class(const std::string& prop_name)
{
	return NCPInstanceBase::setup_property_supported_by_class(prop_name);
}

SpinelNCPControlInterface&
SpinelNCPInstance::get_control_interface()
{
	return mControlInterface;
}

std::set<std::string>
SpinelNCPInstance::get_supported_property_keys()const
{
	std::set<std::string> properties (NCPInstanceBase::get_supported_property_keys());

	properties.insert(kWPANTUNDProperty_ConfigNCPDriverName);
	properties.insert(kWPANTUNDProperty_NCPChannel);
	properties.insert(kWPANTUNDProperty_NCPFrequency);
	properties.insert(kWPANTUNDProperty_NCPRSSI);
	properties.insert(kWPANTUNDProperty_NCPExtendedAddress);

	if (mCapabilities.count(SPINEL_CAP_NET_THREAD_1_0)) {
		properties.insert(kWPANTUNDProperty_ThreadRLOC16);
		properties.insert(kWPANTUNDProperty_ThreadRouterID);
		properties.insert(kWPANTUNDProperty_ThreadLeaderAddress);
		properties.insert(kWPANTUNDProperty_ThreadLeaderRouterID);
		properties.insert(kWPANTUNDProperty_ThreadLeaderWeight);
		properties.insert(kWPANTUNDProperty_ThreadLeaderLocalWeight);
		properties.insert(kWPANTUNDProperty_ThreadNetworkData);
		properties.insert(kWPANTUNDProperty_ThreadNetworkDataVersion);
		properties.insert(kWPANTUNDProperty_ThreadStableNetworkDataVersion);
		properties.insert(kWPANTUNDProperty_ThreadChildTable);
		properties.insert(kWPANTUNDProperty_ThreadNeighborTable);
	}

	if (mCapabilities.count(SPINEL_CAP_COUNTERS)) {
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_UNICAST");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_BROADCAST");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_ACK_REQ");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_ACKED");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_NO_ACK_REQ");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_DATA");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_DATA_POLL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_BEACON");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_BEACON_REQ");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_OTHER");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_RETRY");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_ERR_CCA");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_UNICAST");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_BROADCAST");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_DATA");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_DATA_POLL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_BEACON");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_BEACON_REQ");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_OTHER");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_FILT_WL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_FILT_DA");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_ERR_EMPTY");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_ERR_UKWN_NBR");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_ERR_NVLD_SADDR");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_ERR_SECURITY");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_ERR_BAD_FCS");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_ERR_OTHER");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_IP_SEC_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_IP_INSEC_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_IP_DROPPED");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_IP_SEC_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_IP_INSEC_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_IP_DROPPED");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_SPINEL_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_SPINEL_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_SPINEL_ERR");
	}

	if (mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
		properties.insert(kWPANTUNDProperty_JamDetectionStatus);
		properties.insert(kWPANTUNDProperty_JamDetectionEnable);
		properties.insert(kWPANTUNDProperty_JamDetectionRssiThreshold);
		properties.insert(kWPANTUNDProperty_JamDetectionWindow);
		properties.insert(kWPANTUNDProperty_JamDetectionBusyPeriod);
		properties.insert(kWPANTUNDProperty_JamDetectionDebugHistoryBitmap);
	}

	return properties;
}

cms_t
SpinelNCPInstance::get_ms_to_next_event(void)
{
	cms_t cms = NCPInstanceBase::get_ms_to_next_event();

	if (ncp_state_is_detached_from_ncp(get_ncp_state())) {
		return CMS_DISTANT_FUTURE;
	}

	// If the control protothread hasn't even started, set cms to zero.
	if (0 == mControlPT.lc) {
		cms = 0;
	}

	if (!mTaskQueue.empty()) {
		int tmp_cms = mTaskQueue.front()->get_ms_to_next_event();
		if (tmp_cms < cms) {
			cms = tmp_cms;
		}
	}

	if (cms < 0) {
		cms = 0;
	}

	return cms;
}

static void convert_rloc16_to_router_id(CallbackWithStatusArg1 cb, int status, const boost::any& value)
{
	uint8_t router_id = 0;

	if (status == kWPANTUNDStatus_Ok) {
		uint16_t rloc16 = any_to_int(value);
		router_id = rloc16 >> 10;
	}
	cb(status, router_id);
}

static int unpack_jam_detect_history_bitmap(const uint8_t *data_in, spinel_size_t data_len, boost::any& value)
{
	spinel_ssize_t len;
	uint32_t lower, higher;
	uint64_t val;
	int ret = kWPANTUNDStatus_Failure;

	len = spinel_datatype_unpack(
		data_in,
		data_len,
		SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_UINT32_S,
		&lower,
		&higher
	);

	if (len > 0)
	{
		ret = kWPANTUNDStatus_Ok;
		value = (static_cast<uint64_t>(higher) << 32) + static_cast<uint64_t>(lower);
	}

	return ret;
}

void
SpinelNCPInstance::get_property(
	const std::string& key,
	CallbackWithStatusArg1 cb
) {

#define SIMPLE_SPINEL_GET(prop__, type__)                                \
	start_new_task(SpinelNCPTaskSendCommand::Factory(this)               \
		.set_callback(cb)                                                \
		.add_command(                                                    \
			SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, prop__) \
		)                                                                \
		.set_reply_format(type__)                                        \
		.finish()                                                        \
	)

	if (strcaseequal(key.c_str(), kWPANTUNDProperty_ConfigNCPDriverName)) {
		cb(0, boost::any(std::string("spinel")));

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPCCAThreshold)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPFrequency)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_PHY_FREQ, SPINEL_DATATYPE_INT32_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkKey)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_NET_MASTER_KEY, SPINEL_DATATYPE_DATA_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPExtendedAddress)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_MAC_EXTENDED_ADDR, SPINEL_DATATYPE_EUI64_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkKeyIndex)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER, SPINEL_DATATYPE_UINT32_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPRSSI)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_PHY_RSSI, SPINEL_DATATYPE_INT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadRLOC16)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_RLOC16, SPINEL_DATATYPE_UINT16_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadRouterID)) {
		cb = boost::bind(convert_rloc16_to_router_id, cb, _1, _2);
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_RLOC16, SPINEL_DATATYPE_UINT16_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadLeaderAddress)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_LEADER_ADDR, SPINEL_DATATYPE_IPv6ADDR_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadLeaderRouterID)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_LEADER_RID, SPINEL_DATATYPE_UINT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadLeaderWeight)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_LEADER_WEIGHT, SPINEL_DATATYPE_UINT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadLeaderLocalWeight)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT, SPINEL_DATATYPE_UINT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadNetworkData)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_NETWORK_DATA, SPINEL_DATATYPE_DATA_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadNetworkDataVersion)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_NETWORK_DATA_VERSION, SPINEL_DATATYPE_UINT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadStableNetworkData)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_STABLE_NETWORK_DATA, SPINEL_DATATYPE_DATA_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadStableNetworkDataVersion)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION, SPINEL_DATATYPE_UINT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6MeshLocalPrefix) && !buffer_is_nonzero(mNCPV6Prefix, sizeof(mNCPV6Prefix))) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_IPV6_ML_PREFIX, SPINEL_DATATYPE_IPv6ADDR_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6MeshLocalAddress) && !buffer_is_nonzero(mNCPV6Prefix, sizeof(mNCPV6Prefix))) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_IPV6_ML_ADDR, SPINEL_DATATYPE_IPv6ADDR_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6LinkLocalAddress) && !IN6_IS_ADDR_LINKLOCAL(&mNCPLinkLocalAddress)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_IPV6_LL_ADDR, SPINEL_DATATYPE_IPv6ADDR_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_JamDetectionStatus)) {
		if (!mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
			cb(kWPANTUNDStatus_FeatureNotSupported, boost::any(std::string("Jam Detection Feature Not Supported")));
		} else {
			SIMPLE_SPINEL_GET(SPINEL_PROP_JAM_DETECTED, SPINEL_DATATYPE_BOOL_S);
		}

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_JamDetectionEnable)) {
		if (!mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
			cb(kWPANTUNDStatus_FeatureNotSupported, boost::any(std::string("Jam Detection Feature Not Supported")));
		} else {
			SIMPLE_SPINEL_GET(SPINEL_PROP_JAM_DETECT_ENABLE, SPINEL_DATATYPE_BOOL_S);
		}

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_JamDetectionRssiThreshold)) {
		if (!mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
			cb(kWPANTUNDStatus_FeatureNotSupported, boost::any(std::string("Jam Detection Feature Not Supported")));
		} else {
			SIMPLE_SPINEL_GET(SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD, SPINEL_DATATYPE_INT8_S);
		}

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_JamDetectionWindow)) {
		if (!mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
			cb(kWPANTUNDStatus_FeatureNotSupported, boost::any(std::string("Jam Detection Feature Not Supported")));
		} else {
			SIMPLE_SPINEL_GET(SPINEL_PROP_JAM_DETECT_WINDOW, SPINEL_DATATYPE_UINT8_S);
		}

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_JamDetectionBusyPeriod)) {
		if (!mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
			cb(kWPANTUNDStatus_FeatureNotSupported, boost::any(std::string("Jam Detection Feature Not Supported")));
		} else {
			SIMPLE_SPINEL_GET(SPINEL_PROP_JAM_DETECT_BUSY, SPINEL_DATATYPE_UINT8_S);
		}

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_JamDetectionDebugHistoryBitmap)) {
		if (!mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
			cb(kWPANTUNDStatus_FeatureNotSupported, boost::any(std::string("Jam Detection Feature Not Supported")));
		} else {
			start_new_task(SpinelNCPTaskSendCommand::Factory(this)
				.set_callback(cb)
				.add_command(
					SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_JAM_DETECT_HISTORY_BITMAP)
				)
				.set_reply_unpacker(unpack_jam_detect_history_bitmap)
				.finish()
			);
		}

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadChildTable)) {
		start_new_task(boost::shared_ptr<SpinelNCPTask>(
			new SpinelNCPTaskGetNetworkTopology(
				this,
				cb,
				SpinelNCPTaskGetNetworkTopology::kChildTable,
				SpinelNCPTaskGetNetworkTopology::kResultFormat_StringArray
			)
		));
	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadChildTableAsValMap)) {
		start_new_task(boost::shared_ptr<SpinelNCPTask>(
			new SpinelNCPTaskGetNetworkTopology(
				this,
				cb,
				SpinelNCPTaskGetNetworkTopology::kChildTable,
				SpinelNCPTaskGetNetworkTopology::kResultFormat_ValueMapArray
			)
		));
		
	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadNeighborTable)) {
		start_new_task(boost::shared_ptr<SpinelNCPTask>(
			new SpinelNCPTaskGetNetworkTopology(
				this,
				cb,
				SpinelNCPTaskGetNetworkTopology::kNeighborTable,
				SpinelNCPTaskGetNetworkTopology::kResultFormat_StringArray
			)
		));

		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadNeighborTableAsValMap)) {
		start_new_task(boost::shared_ptr<SpinelNCPTask>(
			new SpinelNCPTaskGetNetworkTopology(
				this,
				cb,
				SpinelNCPTaskGetNetworkTopology::kNeighborTable,
				SpinelNCPTaskGetNetworkTopology::kResultFormat_StringArray
			)
		));
		
	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_OpenThreadMsgBufferCounters)) {
		start_new_task(boost::shared_ptr<SpinelNCPTask>(
			new SpinelNCPTaskGetMsgBufferCounters(
				this,
				cb,
				SpinelNCPTaskGetMsgBufferCounters::kResultFormat_StringArray
			)
		));

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_OpenThreadMsgBufferCountersAsString)) {<commit_msg>Corrected the return type for kWPANTUNDProperty_ThreadNeighborTableAsValMap<commit_after>/*
 *
 * Copyright (c) 2016 <NAME>, Inc.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include "SpinelNCPInstance.h"
#include "time-utils.h"
#include "assert-macros.h"
#include <syslog.h>
#include <errno.h>
#include "socket-utils.h"
#include <stdexcept>
#include <sys/file.h>
#include "SuperSocket.h"
#include "SpinelNCPTask.h"
#include "SpinelNCPTaskWake.h"
#include "SpinelNCPTaskSendCommand.h"
#include "SpinelNCPTaskJoin.h"
#include "SpinelNCPTaskGetNetworkTopology.h"
#include "SpinelNCPTaskGetMsgBufferCounters.h"
#include "any-to.h"
#include "spinel-extra.h"

#define kWPANTUNDProperty_Spinel_CounterPrefix		"NCP:Counter:"

using namespace nl;
using namespace wpantund;

WPANTUND_DEFINE_NCPINSTANCE_PLUGIN(spinel, SpinelNCPInstance);

void
SpinelNCPInstance::handle_ncp_log(const uint8_t* data_ptr, int data_len)
{
	static char linebuffer[NCP_DEBUG_LINE_LENGTH_MAX + 1];
	static int linepos = 0;
	while (data_len--) {
		char nextchar = *data_ptr++;

		if ((nextchar == '\t') || (nextchar >= 32)) {
			linebuffer[linepos++] = nextchar;
		}

		if ( (linepos != 0)
		  && ( (nextchar == '\n')
			|| (nextchar == '\r')
			|| (linepos >= (sizeof(linebuffer) - 1))
		  )
		)
		{
			// flush.
			linebuffer[linepos] = 0;
			syslog(LOG_WARNING, "NCP => %s\n", linebuffer);
			linepos = 0;
		}
	}
}

void
SpinelNCPInstance::start_new_task(const boost::shared_ptr<SpinelNCPTask> &task)
{
	if (ncp_state_is_detached_from_ncp(get_ncp_state())) {
		task->finish(kWPANTUNDStatus_InvalidWhenDisabled);
	} else if (PT_SCHEDULE(task->process_event(EVENT_STARTING_TASK))) {

		if (ncp_state_is_sleeping(get_ncp_state())
			&& (dynamic_cast<const SpinelNCPTaskWake*>(task.get()) == NULL)
		) {
			start_new_task(boost::shared_ptr<SpinelNCPTask>(new SpinelNCPTaskWake(this, NilReturn())));
		}
		mTaskQueue.push_back(task);
	}
}

int
nl::wpantund::spinel_status_to_wpantund_status(int spinel_status)
{
	wpantund_status_t ret;
	switch (spinel_status) {
	case SPINEL_STATUS_ALREADY:
		ret = kWPANTUNDStatus_Already;
		break;
	case SPINEL_STATUS_BUSY:
		ret = kWPANTUNDStatus_Busy;
		break;
	case SPINEL_STATUS_IN_PROGRESS:
		ret = kWPANTUNDStatus_InProgress;
		break;
	case SPINEL_STATUS_JOIN_FAILURE:
		ret = kWPANTUNDStatus_JoinFailedUnknown;
		break;
	case SPINEL_STATUS_JOIN_INCOMPATIBLE:
		ret = kWPANTUNDStatus_JoinFailedAtScan;
		break;
	case SPINEL_STATUS_JOIN_SECURITY:
		ret = kWPANTUNDStatus_JoinFailedAtAuthenticate;
		break;
	case SPINEL_STATUS_OK:
		ret = kWPANTUNDStatus_Ok;
		break;
	case SPINEL_STATUS_PROP_NOT_FOUND:
		ret = kWPANTUNDStatus_PropertyNotFound;
		break;
	case SPINEL_STATUS_INVALID_ARGUMENT:
		ret = kWPANTUNDStatus_NCP_InvalidArgument;
		break;
	case SPINEL_STATUS_INVALID_STATE:
		ret = kWPANTUNDStatus_InvalidForCurrentState;
		break;
	default:
		ret = WPANTUND_NCPERROR_TO_STATUS(spinel_status);
		break;
	}

	return ret;
}

int
nl::wpantund::peek_ncp_callback_status(int event, va_list args)
{
	int ret = 0;

	if (EVENT_NCP_PROP_VALUE_IS == event) {
		va_list tmp;
		va_copy(tmp, args);
		unsigned int key = va_arg(tmp, unsigned int);
		if (SPINEL_PROP_LAST_STATUS == key) {
			const uint8_t* spinel_data_ptr = va_arg(tmp, const uint8_t*);
			spinel_size_t spinel_data_len = va_arg(tmp, spinel_size_t);

			if (spinel_datatype_unpack(spinel_data_ptr, spinel_data_len, "i", &ret) <= 0) {
				ret = SPINEL_STATUS_PARSE_ERROR;
			}
		}
		va_end(tmp);
	} else if (EVENT_NCP_RESET == event) {
		va_list tmp;
		va_copy(tmp, args);
		ret = va_arg(tmp, int);
		va_end(tmp);
	}

	return ret;
}

SpinelNCPInstance::SpinelNCPInstance(const Settings& settings) :
	NCPInstanceBase(settings), mControlInterface(this)
{
	mOutboundBufferLen = 0;
	mInboundHeader = 0;
	mDefaultChannelMask = 0x07FFF800;
	mIsPcapInProgress = false;
	mSettings.clear();

	if (!settings.empty()) {
		int status;
		Settings::const_iterator iter;

		for(iter = settings.begin(); iter != settings.end(); iter++) {
			if (!NCPInstanceBase::setup_property_supported_by_class(iter->first)) {
				status = static_cast<NCPControlInterface&>(get_control_interface())
					.set_property(iter->first, iter->second);

				if (status != 0) {
					syslog(LOG_WARNING, "Attempt to set property \"%s\" failed with err %d", iter->first.c_str(), status);
				}
			}
		}
	}
}

SpinelNCPInstance::~SpinelNCPInstance()
{
}


bool
SpinelNCPInstance::setup_property_supported_by_class(const std::string& prop_name)
{
	return NCPInstanceBase::setup_property_supported_by_class(prop_name);
}

SpinelNCPControlInterface&
SpinelNCPInstance::get_control_interface()
{
	return mControlInterface;
}

std::set<std::string>
SpinelNCPInstance::get_supported_property_keys()const
{
	std::set<std::string> properties (NCPInstanceBase::get_supported_property_keys());

	properties.insert(kWPANTUNDProperty_ConfigNCPDriverName);
	properties.insert(kWPANTUNDProperty_NCPChannel);
	properties.insert(kWPANTUNDProperty_NCPFrequency);
	properties.insert(kWPANTUNDProperty_NCPRSSI);
	properties.insert(kWPANTUNDProperty_NCPExtendedAddress);

	if (mCapabilities.count(SPINEL_CAP_NET_THREAD_1_0)) {
		properties.insert(kWPANTUNDProperty_ThreadRLOC16);
		properties.insert(kWPANTUNDProperty_ThreadRouterID);
		properties.insert(kWPANTUNDProperty_ThreadLeaderAddress);
		properties.insert(kWPANTUNDProperty_ThreadLeaderRouterID);
		properties.insert(kWPANTUNDProperty_ThreadLeaderWeight);
		properties.insert(kWPANTUNDProperty_ThreadLeaderLocalWeight);
		properties.insert(kWPANTUNDProperty_ThreadNetworkData);
		properties.insert(kWPANTUNDProperty_ThreadNetworkDataVersion);
		properties.insert(kWPANTUNDProperty_ThreadStableNetworkDataVersion);
		properties.insert(kWPANTUNDProperty_ThreadChildTable);
		properties.insert(kWPANTUNDProperty_ThreadNeighborTable);
	}

	if (mCapabilities.count(SPINEL_CAP_COUNTERS)) {
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_UNICAST");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_BROADCAST");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_ACK_REQ");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_ACKED");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_NO_ACK_REQ");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_DATA");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_DATA_POLL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_BEACON");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_BEACON_REQ");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_OTHER");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_PKT_RETRY");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_ERR_CCA");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_UNICAST");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_BROADCAST");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_DATA");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_DATA_POLL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_BEACON");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_BEACON_REQ");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_OTHER");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_FILT_WL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_PKT_FILT_DA");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_ERR_EMPTY");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_ERR_UKWN_NBR");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_ERR_NVLD_SADDR");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_ERR_SECURITY");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_ERR_BAD_FCS");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_ERR_OTHER");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_IP_SEC_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_IP_INSEC_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_IP_DROPPED");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_IP_SEC_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_IP_INSEC_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_IP_DROPPED");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "TX_SPINEL_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_SPINEL_TOTAL");
		properties.insert(kWPANTUNDProperty_Spinel_CounterPrefix "RX_SPINEL_ERR");
	}

	if (mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
		properties.insert(kWPANTUNDProperty_JamDetectionStatus);
		properties.insert(kWPANTUNDProperty_JamDetectionEnable);
		properties.insert(kWPANTUNDProperty_JamDetectionRssiThreshold);
		properties.insert(kWPANTUNDProperty_JamDetectionWindow);
		properties.insert(kWPANTUNDProperty_JamDetectionBusyPeriod);
		properties.insert(kWPANTUNDProperty_JamDetectionDebugHistoryBitmap);
	}

	return properties;
}

cms_t
SpinelNCPInstance::get_ms_to_next_event(void)
{
	cms_t cms = NCPInstanceBase::get_ms_to_next_event();

	if (ncp_state_is_detached_from_ncp(get_ncp_state())) {
		return CMS_DISTANT_FUTURE;
	}

	// If the control protothread hasn't even started, set cms to zero.
	if (0 == mControlPT.lc) {
		cms = 0;
	}

	if (!mTaskQueue.empty()) {
		int tmp_cms = mTaskQueue.front()->get_ms_to_next_event();
		if (tmp_cms < cms) {
			cms = tmp_cms;
		}
	}

	if (cms < 0) {
		cms = 0;
	}

	return cms;
}

static void convert_rloc16_to_router_id(CallbackWithStatusArg1 cb, int status, const boost::any& value)
{
	uint8_t router_id = 0;

	if (status == kWPANTUNDStatus_Ok) {
		uint16_t rloc16 = any_to_int(value);
		router_id = rloc16 >> 10;
	}
	cb(status, router_id);
}

static int unpack_jam_detect_history_bitmap(const uint8_t *data_in, spinel_size_t data_len, boost::any& value)
{
	spinel_ssize_t len;
	uint32_t lower, higher;
	uint64_t val;
	int ret = kWPANTUNDStatus_Failure;

	len = spinel_datatype_unpack(
		data_in,
		data_len,
		SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_UINT32_S,
		&lower,
		&higher
	);

	if (len > 0)
	{
		ret = kWPANTUNDStatus_Ok;
		value = (static_cast<uint64_t>(higher) << 32) + static_cast<uint64_t>(lower);
	}

	return ret;
}

void
SpinelNCPInstance::get_property(
	const std::string& key,
	CallbackWithStatusArg1 cb
) {

#define SIMPLE_SPINEL_GET(prop__, type__)                                \
	start_new_task(SpinelNCPTaskSendCommand::Factory(this)               \
		.set_callback(cb)                                                \
		.add_command(                                                    \
			SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, prop__) \
		)                                                                \
		.set_reply_format(type__)                                        \
		.finish()                                                        \
	)

	if (strcaseequal(key.c_str(), kWPANTUNDProperty_ConfigNCPDriverName)) {
		cb(0, boost::any(std::string("spinel")));

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPCCAThreshold)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPFrequency)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_PHY_FREQ, SPINEL_DATATYPE_INT32_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkKey)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_NET_MASTER_KEY, SPINEL_DATATYPE_DATA_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPExtendedAddress)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_MAC_EXTENDED_ADDR, SPINEL_DATATYPE_EUI64_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NetworkKeyIndex)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER, SPINEL_DATATYPE_UINT32_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_NCPRSSI)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_PHY_RSSI, SPINEL_DATATYPE_INT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadRLOC16)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_RLOC16, SPINEL_DATATYPE_UINT16_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadRouterID)) {
		cb = boost::bind(convert_rloc16_to_router_id, cb, _1, _2);
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_RLOC16, SPINEL_DATATYPE_UINT16_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadLeaderAddress)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_LEADER_ADDR, SPINEL_DATATYPE_IPv6ADDR_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadLeaderRouterID)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_LEADER_RID, SPINEL_DATATYPE_UINT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadLeaderWeight)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_LEADER_WEIGHT, SPINEL_DATATYPE_UINT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadLeaderLocalWeight)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT, SPINEL_DATATYPE_UINT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadNetworkData)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_NETWORK_DATA, SPINEL_DATATYPE_DATA_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadNetworkDataVersion)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_NETWORK_DATA_VERSION, SPINEL_DATATYPE_UINT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadStableNetworkData)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_STABLE_NETWORK_DATA, SPINEL_DATATYPE_DATA_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadStableNetworkDataVersion)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION, SPINEL_DATATYPE_UINT8_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6MeshLocalPrefix) && !buffer_is_nonzero(mNCPV6Prefix, sizeof(mNCPV6Prefix))) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_IPV6_ML_PREFIX, SPINEL_DATATYPE_IPv6ADDR_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6MeshLocalAddress) && !buffer_is_nonzero(mNCPV6Prefix, sizeof(mNCPV6Prefix))) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_IPV6_ML_ADDR, SPINEL_DATATYPE_IPv6ADDR_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_IPv6LinkLocalAddress) && !IN6_IS_ADDR_LINKLOCAL(&mNCPLinkLocalAddress)) {
		SIMPLE_SPINEL_GET(SPINEL_PROP_IPV6_LL_ADDR, SPINEL_DATATYPE_IPv6ADDR_S);

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_JamDetectionStatus)) {
		if (!mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
			cb(kWPANTUNDStatus_FeatureNotSupported, boost::any(std::string("Jam Detection Feature Not Supported")));
		} else {
			SIMPLE_SPINEL_GET(SPINEL_PROP_JAM_DETECTED, SPINEL_DATATYPE_BOOL_S);
		}

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_JamDetectionEnable)) {
		if (!mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
			cb(kWPANTUNDStatus_FeatureNotSupported, boost::any(std::string("Jam Detection Feature Not Supported")));
		} else {
			SIMPLE_SPINEL_GET(SPINEL_PROP_JAM_DETECT_ENABLE, SPINEL_DATATYPE_BOOL_S);
		}

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_JamDetectionRssiThreshold)) {
		if (!mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
			cb(kWPANTUNDStatus_FeatureNotSupported, boost::any(std::string("Jam Detection Feature Not Supported")));
		} else {
			SIMPLE_SPINEL_GET(SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD, SPINEL_DATATYPE_INT8_S);
		}

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_JamDetectionWindow)) {
		if (!mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
			cb(kWPANTUNDStatus_FeatureNotSupported, boost::any(std::string("Jam Detection Feature Not Supported")));
		} else {
			SIMPLE_SPINEL_GET(SPINEL_PROP_JAM_DETECT_WINDOW, SPINEL_DATATYPE_UINT8_S);
		}

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_JamDetectionBusyPeriod)) {
		if (!mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
			cb(kWPANTUNDStatus_FeatureNotSupported, boost::any(std::string("Jam Detection Feature Not Supported")));
		} else {
			SIMPLE_SPINEL_GET(SPINEL_PROP_JAM_DETECT_BUSY, SPINEL_DATATYPE_UINT8_S);
		}

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_JamDetectionDebugHistoryBitmap)) {
		if (!mCapabilities.count(SPINEL_CAP_JAM_DETECT)) {
			cb(kWPANTUNDStatus_FeatureNotSupported, boost::any(std::string("Jam Detection Feature Not Supported")));
		} else {
			start_new_task(SpinelNCPTaskSendCommand::Factory(this)
				.set_callback(cb)
				.add_command(
					SpinelPackData(SPINEL_FRAME_PACK_CMD_PROP_VALUE_GET, SPINEL_PROP_JAM_DETECT_HISTORY_BITMAP)
				)
				.set_reply_unpacker(unpack_jam_detect_history_bitmap)
				.finish()
			);
		}

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadChildTable)) {
		start_new_task(boost::shared_ptr<SpinelNCPTask>(
			new SpinelNCPTaskGetNetworkTopology(
				this,
				cb,
				SpinelNCPTaskGetNetworkTopology::kChildTable,
				SpinelNCPTaskGetNetworkTopology::kResultFormat_StringArray
			)
		));
	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadChildTableAsValMap)) {
		start_new_task(boost::shared_ptr<SpinelNCPTask>(
			new SpinelNCPTaskGetNetworkTopology(
				this,
				cb,
				SpinelNCPTaskGetNetworkTopology::kChildTable,
				SpinelNCPTaskGetNetworkTopology::kResultFormat_ValueMapArray
			)
		));
		
	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadNeighborTable)) {
		start_new_task(boost::shared_ptr<SpinelNCPTask>(
			new SpinelNCPTaskGetNetworkTopology(
				this,
				cb,
				SpinelNCPTaskGetNetworkTopology::kNeighborTable,
				SpinelNCPTaskGetNetworkTopology::kResultFormat_StringArray
			)
		));

		} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_ThreadNeighborTableAsValMap)) {
		start_new_task(boost::shared_ptr<SpinelNCPTask>(
			new SpinelNCPTaskGetNetworkTopology(
				this,
				cb,
				SpinelNCPTaskGetNetworkTopology::kNeighborTable,
				SpinelNCPTaskGetNetworkTopology::kResultFormat_ValueMapArray
			)
		));
		
	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_OpenThreadMsgBufferCounters)) {
		start_new_task(boost::shared_ptr<SpinelNCPTask>(
			new SpinelNCPTaskGetMsgBufferCounters(
				this,
				cb,
				SpinelNCPTaskGetMsgBufferCounters::kResultFormat_StringArray
			)
		));

	} else if (strcaseequal(key.c_str(), kWPANTUNDProperty_OpenThreadMsgBufferCountersAsString)) {
{'fraction_non_alphanumeric': 0.09854919546293854, 'fraction_numerical': 0.006014244262727513, 'mean_word_length': 6.649515738498789, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3730276', 'n_tokens_mistral': 15265, 'n_tokens_neox': 14222, 'n_words': 1787}