/*
 * dump2er.c
 * WiFi Data Elements json objects dump functions
 *
 * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved.
 *
 * See AUTHORS file for author and contributors.
 *
 * See LICENSE file for license related information.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <easy/easy.h>
#include <easymesh.h>

#include <libubox/blobmsg.h>
#include <libubox/utils.h>

#include "debug.h"
#include "config.h"
#include "decollector.h"
#include "timer.h"
#include "utils.h"
#include "wifi_dataelements.h"


/**
 *  Returns: new output lenght
 */
static int remove_new_lines(const char *src, int src_len, char *out, int out_len)
{
	int i, new_len;

	if (out_len < src_len)
		return 0;

	for (i = 0, new_len = 0; i < src_len; ++i) {
		if (src[i] != '\n') {
			*out = src[i];
			++out;
			++new_len;
		}
	}

	return new_len;
}

const char *to_base64_str(const uint8_t *src, size_t src_len, char *out,
				 size_t out_len)
{
	size_t tmp_len = out_len;
	char *tmp_str;
	int new_out_len;

	if (!out || !src)
		return NULL;

	tmp_str = malloc(tmp_len);
	if (!tmp_str)
		return NULL;

	if (base64_encode(src, src_len, (unsigned char *)tmp_str, &tmp_len)) {
		dbg("%s: base64_encode failed.", __func__);
		free(tmp_str);
		return NULL;
	}

	new_out_len = remove_new_lines(tmp_str, tmp_len, out, out_len);
	free(tmp_str);

	if (new_out_len < 1)
		return NULL;

	if (new_out_len < out_len)
		out[new_out_len] = '\0';
	else
		out[new_out_len - 1] = '\0';

	return out;
}

bool is_network_device_agent(struct wifi_network_device *dev)
{
	return dev->multi_ap_device.agent_opmode != NOT_SUPPORTED;
}

int count_available_nodes(struct wifi_network *net)
{
	struct wifi_network_device *dev = NULL;
	int n = 0;

	list_for_each_entry(dev, &net->devicelist, list) {
		if (!is_network_device_agent(dev))
			continue;
#ifdef SKIP_UNRESPONSIVE_NODE
		if (!is_network_device_alive(dev))
			continue;
#endif
		n++;
	}

	return n;
}

const char *wifi_bsstype_to_str(enum wifi_bsstype type)
{
	switch (type) {
	case WIFI_BSSTYPE_FBSS:		return "Fronthaul";
	case WIFI_BSSTYPE_BBSS:		return "Backhaul";
	case WIFI_BSSTYPE_COMBINED:	return "Fronthaul+Backhaul";
	default:			return "";
	}
}

const char *wifi_band_to_str(enum wifi_band band)
{
	switch (band) {
	case BAND_2:	return "2";
	case BAND_5:	return "5";
	case BAND_6:	return "6";
	default:	return "";
	}
}

static char *wifi_bands_to_str(enum wifi_band band, char *out)
{
	uint32_t bands[] = { BAND_2, BAND_5, BAND_6 };
	const char *bandstr[] = { "2", "5", "6" };
	char buf[32] = {0};
	int i;

	for (i = 0; i < ARRAY_SIZE(bands); i++) {
		if (!!(band & bands[i])) {
			snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf) - 1,
				"%s%s", i == 0 ? "" : ",", bandstr[i]);
		}
	}

	strncpy(out, buf, strlen(buf));
	return out;
}


static const char *link_type_to_str(enum network_link_type link)
{
	switch (link) {
	case LINK_TYPE_NONE:	return "None";
	case LINK_TYPE_WIFI:	return "Wi-Fi";
	case LINK_TYPE_ETH:	return "Ethernet";
	case LINK_TYPE_MOCA:	return "MoCA";
	case LINK_TYPE_GHN:	return "G.hn";
	case LINK_TYPE_HPNA:	return "HPNA";
	case LINK_TYPE_HOME:	return "HomePlug";
	case LINK_TYPE_UPA:	return "UPA";

	default:		return "Unknown";
	}
}

const char *operation_mode_to_str(enum operation_mode op_mode)
{
	switch (op_mode) {
	case NOT_SUPPORTED:	return "NotSupported";
	case SUPPORTED:		return "SupportedNotEnabled";
	case RUNNING:		return "Running";

	default:		return "Unknown";
	}
}

static const char *oui_to_str(const uint8_t *oui, char *oui_str)
{
	/* Always 6 hex-digit value using all upper-case letters */
	snprintf(oui_str, 7, "%02X%02X%02X", oui[0], oui[1], oui[2]);

	return oui_str;
}

static const char *ch_usage_reason_to_str(uint8_t ch_usage_reason)
{
	enum {
		TWT_SCHEDULE = 0x00,
		TSPEC = 0x01,
		SCHEDULER_POLICY = 0x02,
		IEEE_802_11 = 0x03,
		NON_IEEE_802_11 = 0x04,

		BSS_NON_USAGE = 0xFF
	};

	switch (ch_usage_reason) {
	case TWT_SCHEDULE:	return "TWT_schedule";
	case TSPEC:		return "TSPEC";
	case SCHEDULER_POLICY:	return "Scheduler_policy";
	case NON_IEEE_802_11:	return "Non_IEEE_802_11";

	case BSS_NON_USAGE:	return "BSS_non_usage";

	default:		return "Unknown";
	}
}

const char *scan_status_to_str(uint8_t scan_status)
{
	switch (scan_status) {
	case CH_SCAN_STATUS_SUCCESS:		return "0";
	case CH_SCAN_STATUS_SCAN_NOT_SUPPORTED: return "1";
	case CH_SCAN_STATUS_TOO_SOON:		return "2";
	case CH_SCAN_STATUS_TOO_BUSY:		return "3";
	case CH_SCAN_STATUS_SCAN_NOT_COMPLETED: return "4";
	case CH_SCAN_STATUS_SCAN_ABORTED:	return "5";
	case CH_SCAN_STATUS_BOOT_SCAN_ONLY:	return "6";
	default:				return "7";
	}
}

static void dump2_network_device_default8021q(struct decollector_private *p,
					      struct wifi_network *net,
					      struct wifi_network_device *dev,
					      struct blob_buf *bb)
{
	void *arr, *t;
	int i;

	/* TODO this is for device or for network ? */

	/* Device.WiFi.DataElements.Network.Device.{i}.Default8021Q.{i}. */
	arr = blobmsg_open_array(bb, "Default8021Q");
	for (i = 0; i < 1; i++) {
		t = blobmsg_open_table(bb, "");
		blobmsg_add_u8(bb, "Enable", net->primary_vid ? true : false);
		blobmsg_add_u32(bb, "PrimaryVID", net->primary_vid);
		blobmsg_add_u32(bb, "DefaultPCP", net->default_pcp);
		blobmsg_close_table(bb, t);
	}

	blobmsg_close_array(bb, arr); // Default8021Q
}


static void dump2_network_device_ssidtovidmapping(struct decollector_private *p,
					      struct wifi_network *net,
					      struct wifi_network_device *dev,
					      struct blob_buf *bb)
{
	struct wifi_network_ssid *ssid;
	void *arr, *t;

	/* TODO this is for device or for network ? */

	/* Device.WiFi.DataElements.Network.Device.{i}.SSIDtoVIDMapping.{i}. */
	arr = blobmsg_open_array(bb, "SSIDtoVIDMapping");
	list_for_each_entry(ssid, &net->ssidlist, list) {
		t = blobmsg_open_table(bb, "");
		blobmsg_add_string(bb, "SSID", ssid->ssid);
		blobmsg_add_u32(bb, "VID", ssid->vid);
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr); // SSIDtoVIDMapping

}


static void dump2_network_device_cacstatus(struct decollector_private *p,
					   struct wifi_network *net,
					   struct wifi_network_device *dev,
					   struct blob_buf *bb)
{
	const struct wifi_cac_status *cac_status_entry;
	void *cac_status_array;

	/* Device.WiFi.DataElements.Network.Device.{i}.CACStatus.{i}. */
	cac_status_array = blobmsg_open_array(bb, "CACStatus");
	list_for_each_entry(cac_status_entry, &dev->cac_statuslist, list) {

		void *cac_status_table = blobmsg_open_table(bb, "");
		void *tmp_array;
		const struct wifi_cac_available_channel *cac_avail_entry;
		const struct wifi_cac_nop_channel *cac_nop_entry;
		const struct wifi_cac_active_channel *cac_active_entry;

		blobmsg_add_string(bb, "TimeStamp", cac_status_entry->tsp);

		blobmsg_add_u32(bb, "CACAvailableChannelNumberOfEntries",
				list_count(&cac_status_entry->available_chlist));
		blobmsg_add_u32(bb, "CACNonOccupancyChannelNumberOfEntries",
				list_count(&cac_status_entry->nop_chlist));
		blobmsg_add_u32(bb, "CACActiveChannelNumberOfEntries",
				list_count(&cac_status_entry->cac_chlist));

		/* Device.WiFi.DataElements.Network.Device.{i}.CACStatus.{i}.CACAvailableChannel.{i}. */
		tmp_array = blobmsg_open_array(bb, "CACAvailableChannel");
		list_for_each_entry(cac_avail_entry, &cac_status_entry->available_chlist, list) {
			void *table = blobmsg_open_table(bb, "");

			blobmsg_add_u32(bb, "OpClass", cac_avail_entry->opclass);
			blobmsg_add_u32(bb, "Channel", cac_avail_entry->channel);
			blobmsg_add_u32(bb, "Minutes", cac_avail_entry->cleared);

			blobmsg_close_table(bb, table);
		}
		blobmsg_close_array(bb, tmp_array); // CACAvailableChannel

		/* Device.WiFi.DataElements.Network.Device.{i}.CACStatus.{i}.CACNonOccupancyChannel.{i}. */
		tmp_array = blobmsg_open_array(bb, "CACNonOccupancyChannel");
		list_for_each_entry(cac_nop_entry, &cac_status_entry->nop_chlist, list) {
			void *table = blobmsg_open_table(bb, "");

			blobmsg_add_u32(bb, "OpClass", cac_nop_entry->opclass);
			blobmsg_add_u32(bb, "Channel", cac_nop_entry->channel);
			blobmsg_add_u32(bb, "Seconds", cac_nop_entry->remaining);

			blobmsg_close_table(bb, table);

		}
		blobmsg_close_array(bb, tmp_array); // CACNonOccupancyChannel

		/* Device.WiFi.DataElements.Network.Device.{i}.CACStatus.{i}.CACActiveChannel.{i}. */
		tmp_array = blobmsg_open_array(bb, "CACActiveChannel");
		list_for_each_entry(cac_active_entry, &cac_status_entry->cac_chlist, list) {
			void *table = blobmsg_open_table(bb, "");

			blobmsg_add_u32(bb, "OpClass", cac_active_entry->opclass);
			blobmsg_add_u32(bb, "Channel", cac_active_entry->channel);
			blobmsg_add_u32(bb, "Countdown", cac_active_entry->remaining);

			blobmsg_close_table(bb, table);

		}
		blobmsg_close_array(bb, tmp_array); // CACActiveChannel

		blobmsg_close_table(bb, cac_status_table); //CACStatus.{i}
	}

	blobmsg_close_array(bb, cac_status_array); // CACStatus

}

static void dump2_network_device_sprule(struct decollector_private *p,
					struct wifi_network *net,
					struct wifi_network_device *dev,
					struct blob_buf *bb)
{
	const struct wifi_sp_rule *sp_rule_entry;
	void *arr, *t;

	/* Device.WiFi.DataElements.Network.Device.{i}.SPRule.{i}. */
	arr = blobmsg_open_array(bb, "SPRule");
	list_for_each_entry(sp_rule_entry, &dev->sp_rulelist, list) {
		t = blobmsg_open_table(bb, "");

		blobmsg_add_u32(bb, "ID", sp_rule_entry->id);
		blobmsg_add_u32(bb, "Precedence", sp_rule_entry->priority);
		blobmsg_add_u32(bb, "Output", sp_rule_entry->output);
		blobmsg_add_u8(bb, "AlwaysMatch", sp_rule_entry->always_match);

		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);
}

static void dump2_network_device_ieee1905security(struct decollector_private *p,
						  struct wifi_network *net,
						  struct wifi_network_device *dev,
						  struct blob_buf *bb)
{
	void *arr;
	void *t;
	int i;
	const int num_of_entries = dev->i1905_seccap.caps_valid ? 1 : 0;

	/* Device.WiFi.DataElements.Network.Device.{i}.IEEE1905Security.{i}. */
	arr = blobmsg_open_array(bb, "IEEE1905Security");
	for (i = 0; i < num_of_entries; i++) {
		t = blobmsg_open_table(bb, "");

		blobmsg_add_u32(bb, "OnboardingProtocol", dev->i1905_seccap.onboarding_protocol);
		blobmsg_add_u32(bb, "IntegrityAlgorithm", dev->i1905_seccap.integrity);
		blobmsg_add_u32(bb, "EncryptionAlgorithm", dev->i1905_seccap.encryption);

		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr); // IEEE1905Security

}

static void dump2_network_device_anticipatedchannels(struct decollector_private *p,
						     struct wifi_network *net,
						     struct wifi_network_device *dev,
						     struct blob_buf *bb)
{
	void *arr, *t;
	int i;

	/* Device.WiFi.DataElements.Network.Device.{i}.AnticipatedChannels.{i}. */
	arr = blobmsg_open_array(bb, "AnticipatedChannels");

	for (i = 0; i < 0 /* TODO */; i++) {
		t = blobmsg_open_table(bb, "");

		blobmsg_add_u32(bb, "OpClass", 0); // TODO unsignedInt(:255)

		arr = blobmsg_open_array(bb, "ChannelList");
		for (i = 0; i < 0 /* TODO */; i++) {
			 ; // list of unsignedInt(:255)
		}
		blobmsg_close_array(bb, arr);

		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr); // AnticipatedChannels
}

static void dump2_network_device_anticipatedchannelsusage(struct decollector_private *p,
						     struct wifi_network *net,
						     struct wifi_network_device *dev,
						     struct blob_buf *bb)
{
	const struct anticipated_ch_usage *ch_usage = NULL;
	/* Device.WiFi.DataElements.Network.Device.{i}.AnticipatedChannelUsage.{i}. */
	void *usage_arr = blobmsg_open_array(bb, "AnticipatedChannelUsage");

	list_for_each_entry(ch_usage, &dev->anticipated_channel_usagelist, list) {
		char macaddr_str[18] = { 0 };
		const struct anticipated_ch_usage_entry *entry = NULL;
		void *entry_arr = NULL;

		void *usage_table = blobmsg_open_table(bb, "");

		blobmsg_add_u32(bb, "OpClass", ch_usage->op_class);
		blobmsg_add_u32(bb, "Channel", ch_usage->channel);
		hwaddr_ntoa(ch_usage->ref_bssid, macaddr_str);
		blobmsg_add_string(bb, "ReferenceBSSID", macaddr_str);
		blobmsg_add_u32(bb, "EntryNumberOfEntries", ch_usage->num_of_usage_entries);

		/* Device.WiFi.DataElements.Network.Device.{i}.AnticipatedChannelUsage.{i}.Entry.{i}. */
		entry_arr = blobmsg_open_array(bb, "Entry");
		list_for_each_entry(entry, &ch_usage->entry_list, list) {
			char hexbin_str[2 * MAX_RU_BITMASK_LENGTH + 1] = { 0 };
			void *entry_table = blobmsg_open_table(bb, "");
			int i;
			const uint8_t *time = (const uint8_t *)&entry->burst_start_time;

			// todo: hex­Binary­(4), what byte order?
			snprintf(hexbin_str, 2 * 4 + 1, "%02x%02x%02x%02x", time[0], time[1], time[2], time[3]);
			blobmsg_add_string(bb, "BurstStartTime", hexbin_str);
			blobmsg_add_u32(bb, "BurstLength", entry->burst_length);
			blobmsg_add_u32(bb, "Repetitions", entry->repetitions);
			blobmsg_add_u32(bb, "BurstInterval", entry->burst_interval);

			// hexBinary(2:10)
			for (i = 0; i < entry->ru_bitmask_length; ++i)
				snprintf(hexbin_str + i * 2, 2 + 1, "%02x", entry->ru_bitmask[i]);

			blobmsg_add_string(bb, "RUBitmask", hexbin_str);
			hwaddr_ntoa(entry->transmitter_id, macaddr_str);
			blobmsg_add_string(bb, "TransmitterIdentifier", macaddr_str);
			blobmsg_add_u32(bb, "PowerLevel", entry->power_level);

			blobmsg_add_string(bb, "ChannelUsageReason",
					   ch_usage_reason_to_str(entry->ch_usage_reason));

			blobmsg_close_table(bb, entry_table); // Entry.{i}.
		}
		blobmsg_close_array(bb, entry_arr); // Entry

		blobmsg_close_table(bb, usage_table); // .AnticipatedChannelUsage.{i}
	}

	blobmsg_close_array(bb, usage_arr); // AnticipatedChannelUsage
}

static void dump2_network_device_backhauldown(struct decollector_private *p,
	       struct wifi_network *net,
	       struct wifi_network_device *dev,
	       struct blob_buf *bb)
{
	const struct wifi_bh_down *bh_down = NULL;
	/* Device.WiFi.DataElements.Network.Device.{i}.BackhaulDown.{i}. */
	void *bhdown_arr = blobmsg_open_array(bb, "BackhaulDown");

	list_for_each_entry(bh_down, &dev->bh_downlist, list) {
		char macaddr_str[18] = { 0 };

		void *bhdown_table = blobmsg_open_table(bb, "");

		hwaddr_ntoa(bh_down->al_macaddr, macaddr_str);
		blobmsg_add_string(bb, "BackhaulDownALID", macaddr_str);
		hwaddr_ntoa(bh_down->bss_macaddr, macaddr_str);
		blobmsg_add_string(bb, "BackhaulDownMACAddress", macaddr_str);

		blobmsg_close_table(bb, bhdown_table); // .BackhaulDown.{i}
	}

	blobmsg_close_array(bb, bhdown_arr); // BackhaulDown
}

static void dump2_network_device_multiapdevice(struct decollector_private *p,
					       struct wifi_network *net,
					       struct wifi_network_device *dev,
					       struct blob_buf *bb)
{
	void *map_device, *backhaul, *class_profile, *stats;
	void *class_profile_arr;
	const struct wifi_network_device_backhaul *map_dev_backhaul =
		&dev->multi_ap_device.backhaul;
	char macaddr_str[18] = { 0 };
	int i;

	/* Device.WiFi.DataElements.Network.Device.{i}.MultiAPDevice. */
	map_device = blobmsg_open_table(bb, "MultiAPDevice");
	blobmsg_add_string(bb, "ManufacturerOUI", oui_to_str(dev->multi_ap_device.oui, macaddr_str));
	blobmsg_add_string(bb, "LastContactTime", dev->multi_ap_device.last_contacttime);
	/* Currently not needed, empty value means referenced IEEE1905.1 object is deleted. */
	blobmsg_add_string(bb, "AssocIEEE1905DeviceRef", "");
	blobmsg_add_string(bb, "EasyMeshControllerOperationMode",
		operation_mode_to_str(dev->multi_ap_device.controller_opmode));
	blobmsg_add_string(bb, "EasyMeshAgentOperationMode",
		operation_mode_to_str(dev->multi_ap_device.agent_opmode));

	/* Device.WiFi.DataElements.Network.Device.{i}.MultiAPDevice.Backhaul. */
	backhaul = blobmsg_open_table(bb, "Backhaul");
	blobmsg_add_string(bb, "LinkType", link_type_to_str(map_dev_backhaul->linktype));

	hwaddr_ntoa(map_dev_backhaul->upstream_bbss_macaddr, macaddr_str);
	blobmsg_add_string(bb, "BackhaulMACAddress", macaddr_str);

	hwaddr_ntoa(map_dev_backhaul->upstream_device_macaddr, macaddr_str);
	blobmsg_add_string(bb, "BackhaulDeviceID", macaddr_str);

	hwaddr_ntoa(map_dev_backhaul->bsta_macaddr, macaddr_str);
	blobmsg_add_string(bb, "MACAddress", macaddr_str);

	blobmsg_add_u32(bb, "CurrentOperatingClassProfileNumberOfEntries", map_dev_backhaul->cur_opclass.num_opclass);

	/* Device.WiFi.DataElements.Network.Device.{i}.MultiAPDevice.Backhaul.CurrentOperatingClassProfile.{i}. */
	class_profile_arr = blobmsg_open_array(bb, "CurrentOperatingClassProfile");
	for (i = 0; i < map_dev_backhaul->cur_opclass.num_opclass; i++) {
		char tsp[32];

		class_profile = blobmsg_open_table(bb, "");

		blobmsg_add_u32(bb, "Class", map_dev_backhaul->cur_opclass.opclass[i].id);
		blobmsg_add_u32(bb, "Channel", map_dev_backhaul->cur_opclass.opclass[i].channel[0].channel);
		blobmsg_add_u32(bb, "TxPower", map_dev_backhaul->cur_opclass.opclass[i].max_txpower);
		blobmsg_add_string(bb, "TimeStamp",
			get_timestamp(&map_dev_backhaul->cur_opclass.entry_time.tv_sec, tsp, sizeof(tsp)));

		blobmsg_close_table(bb, class_profile);
	}
	blobmsg_close_array(bb, class_profile_arr); // CurrentOperatingClassProfile

	/* Device.WiFi.DataElements.Network.Device.{i}.MultiAPDevice.Backhaul.Stats. */
	stats = blobmsg_open_table(bb, "Stats");
	blobmsg_add_u64(bb, "BytesSent", map_dev_backhaul->stats.tx_bytes);
	blobmsg_add_u64(bb, "BytesReceived", map_dev_backhaul->stats.rx_bytes);
	blobmsg_add_u64(bb, "PacketsSent", map_dev_backhaul->stats.tx_pkts);
	blobmsg_add_u64(bb, "PacketsReceived", map_dev_backhaul->stats.rx_pkts);
	blobmsg_add_u64(bb, "ErrorsSent", map_dev_backhaul->stats.tx_errors);
	blobmsg_add_u64(bb, "ErrorsReceived", map_dev_backhaul->stats.rx_errors);
	blobmsg_add_u32(bb, "LinkUtilization", 0); // TODO unsignedInt(:100)
	blobmsg_add_u32(bb, "SignalStrength", map_dev_backhaul->stats.rcpi);
	blobmsg_add_u32(bb, "LastDataDownlinkRate", map_dev_backhaul->stats.dl_rate);
	blobmsg_add_u32(bb, "LastDataUplinkRate", map_dev_backhaul->stats.ul_rate);
	blobmsg_add_string(bb, "TimeStamp", map_dev_backhaul->stats.tsp);
	blobmsg_close_table(bb, stats);

	blobmsg_close_table(bb, backhaul);
	blobmsg_close_table(bb, map_device);

}

static void dump2_network_device_radio_scanresult(struct decollector_private *p,
						  struct wifi_network *net,
						  struct wifi_network_device *dev,
						  struct wifi_radio_element *radio,
						  struct blob_buf *bb)
{
	struct wifi_scanres_element *sres = NULL;
	void *arr, *arr2, *arr3, *arr4;
	void *t, *t2, *t3, *t4;


	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.ScanResult.{i}. */
	arr = blobmsg_open_array(bb, "ScanResult");
	list_for_each_entry(sres, &radio->scanlist, list) {
		struct wifi_scanres_opclass_element *op = NULL;

		t = blobmsg_open_table(bb, "");
		blobmsg_add_string(bb, "TimeStamp", sres->tsp);
		blobmsg_add_u32(bb, "OpClassScanNumberOfEntries", sres->num_opclass_scanned);

		/* ... Radio.{i}.ScanResult.{i}.OpClassScan.{i}. */
		arr2 = blobmsg_open_array(bb, "OpClassScan");
		list_for_each_entry_reverse(op, &sres->opclass_scanlist, list) {
			struct wifi_scanres_channel_element *ch = NULL;

			t2 = blobmsg_open_table(bb, "");
			blobmsg_add_u32(bb, "OperatingClass", op->opclass);
			blobmsg_add_u32(bb, "ChannelScanNumberOfEntries", op->num_channels_scanned);

			/* ... ScanResult.{i}.OpClassScan.{i}.ChannelScan.{i}. */
			arr3 = blobmsg_open_array(bb, "ChannelScan");
			list_for_each_entry(ch, &op->channel_scanlist, list) {
				struct wifi_scanres_neighbor_element *nbr = NULL;

				t3 = blobmsg_open_table(bb, "");
				blobmsg_add_u32(bb, "Channel", ch->channel);
				blobmsg_add_string(bb, "TimeStamp", ch->tsp);
				blobmsg_add_u32(bb, "Utilization", ch->utilization);
				blobmsg_add_u32(bb, "Noise", ch->anpi);
				blobmsg_add_string(bb, "ScanStatus", scan_status_to_str(ch->scan_status));
				blobmsg_add_u32(bb, "NeighborBSSNumberOfEntries", ch->num_neighbors);

				/* ... OpClassScan.{i}.ChannelScan.{i}.NeighborBSS.{i}. */
				arr4 = blobmsg_open_array(bb, "NeighborBSS");
				list_for_each_entry(nbr, &ch->nbrlist, list) {
					t4 = blobmsg_open_table(bb, "");
					blobmsg_add_macaddr(bb, "BSSID", nbr->bssid);
					blobmsg_add_string(bb, "SSID", nbr->ssid);
					blobmsg_add_u32(bb, "SignalStrength", nbr->rssi);
					blobmsg_add_u32(bb, "ChannelBandwidth", nbr->bw);
					blobmsg_add_u32(bb, "ChannelUtilization", nbr->utilization);
					blobmsg_add_u32(bb, "StationCount", nbr->num_stations);
					blobmsg_add_macaddr(bb, "MLDMACAddress", nbr->mld_macaddr);
					blobmsg_add_macaddr(bb, "ReportingBSSID", nbr->rpt_bssid);
					blobmsg_add_u8(bb, "MultiBSSID", nbr->multibss_type == 0 ? false : true);
					blobmsg_add_u8(bb, "BSSLoadElementPresent", nbr->bssload_present ? true : false);
					blobmsg_add_u32(bb, "BSSColor", nbr->bss_color);
					blobmsg_close_table(bb, t4);
				}
				blobmsg_close_array(bb, arr4); // NeighborBSS
				blobmsg_close_table(bb, t3);
			}
			blobmsg_close_array(bb, arr3); // ChannelScan
			blobmsg_close_table(bb, t2);
		}
		blobmsg_close_array(bb, arr2); // OpClassScan
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr); // ScanResult
}

static void dump2_network_device_radio_bss_sta_multiapsta(struct decollector_private *p,
							  struct wifi_network *net,
							  struct wifi_network_device *dev,
							  struct wifi_radio_element *radio,
							  struct wifi_bss_element *bss,
							  struct wifi_sta_element *sta,
							  struct blob_buf *bb)
{
	void *t, *t2;
	void *arr;
	int i;


	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.BSS.{i}.STA.{i}.MultiAPSTA. */
	t = blobmsg_open_table(bb, "MultiAPSTA");
	blobmsg_add_u32(bb, "SteeringHistoryNumberOfEntries", 0); // TODO unsignedInt

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.BSS.{i}.STA.{i}.MultiAPSTA.SteeringSummaryStats. */
	t2 = blobmsg_open_table(bb, "SteeringSummaryStats");
	blobmsg_add_u64(bb, "NoCandidateAPFailures", sta->mapsta.stats.no_candidate_cnt);
	blobmsg_add_u64(bb, "BlacklistAttempts", sta->mapsta.stats.blacklist_attempt_cnt);
	blobmsg_add_u64(bb, "BlacklistSuccesses", sta->mapsta.stats.blacklist_success_cnt);
	blobmsg_add_u64(bb, "BlacklistFailures", sta->mapsta.stats.blacklist_failure_cnt);
	blobmsg_add_u64(bb, "BTMAttempts", sta->mapsta.stats.btm_attempt_cnt);
	blobmsg_add_u64(bb, "BTMSuccesses", sta->mapsta.stats.btm_success_cnt);
	blobmsg_add_u64(bb, "BTMFailures", sta->mapsta.stats.btm_failure_cnt);
	blobmsg_add_u64(bb, "BTMQueryResponses", sta->mapsta.stats.btm_query_resp_cnt);
	blobmsg_add_u32(bb, "LastSteerTime", sta->mapsta.stats.last_tsp.tv_sec);
	blobmsg_close_table(bb, t2); // SteeringSummaryStats

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.BSS.{i}.STA.{i}.MultiAPSTA.SteeringHistory.{i}. */
	arr = blobmsg_open_array(bb, "SteeringHistory");
	for (i = 0; i < 0 /* TODO */; i++) {
		blobmsg_add_string(bb, "Time", "TODO dateTime");
		blobmsg_add_string(bb, "APOrigin", "TODO");
		blobmsg_add_string(bb, "TriggerEvent", "TODO");
		blobmsg_add_string(bb, "SteeringApproach", "TODO");
		blobmsg_add_string(bb, "APDestination", "TODO");
		blobmsg_add_u32(bb, "SteeringDuration", 0); // TODO unsignedInt
	}
	blobmsg_close_array(bb, arr); // SteeringHistory

	blobmsg_close_table(bb, t); // MultiAPSTA
}

static void dump2_capabilities_wifi6(struct wifi_wifi6_capabilities *wifi6_caps,
				     struct blob_buf *bb)
{
	char caps_b64[32] = {0};
	const char *caps_b64_ret = NULL;

	blobmsg_add_u8(bb, "HE160",  wifi6_caps->he160);
	blobmsg_add_u8(bb, "HE8080", wifi6_caps->he8080);

	/* fallback to '0000', when valid MCSNSS is absent */
	if (!wifi6_caps->mcs_nss_len)
		wifi6_caps->mcs_nss_len = 4;

	/* Encode 4, 8 or 12 bytes of MCS NSS */
	caps_b64_ret = to_base64_str(wifi6_caps->mcs_nss_12,
				     wifi6_caps->mcs_nss_len, caps_b64,
				     sizeof(caps_b64));
	blobmsg_add_string(bb, "MCSNSS",  caps_b64_ret ? caps_b64_ret : "");

	blobmsg_add_u8(bb, "SUBeamformer", wifi6_caps->su_beamformer);
	blobmsg_add_u8(bb, "SUBeamformee", wifi6_caps->su_beamformee);
	blobmsg_add_u8(bb, "MUBeamformer", wifi6_caps->mu_beamformer);
	blobmsg_add_u8(bb, "Beamformee80orLess", wifi6_caps->beamformee_le80);
	blobmsg_add_u8(bb, "BeamformeeAbove80", wifi6_caps->beamformee_gt80);
	blobmsg_add_u8(bb, "ULMUMIMO", wifi6_caps->ul_mumimo);
	blobmsg_add_u8(bb, "ULOFDMA",  wifi6_caps->ul_ofdma);
	blobmsg_add_u8(bb, "DLOFDMA",  wifi6_caps->dl_ofdma);

	blobmsg_add_u32(bb, "MaxDLMUMIMO", wifi6_caps->max_dl_mumimo);
	blobmsg_add_u32(bb, "MaxULMUMIMO", wifi6_caps->max_ul_mumimo);
	blobmsg_add_u32(bb, "MaxDLOFDMA", wifi6_caps->max_dl_ofdma);
	blobmsg_add_u32(bb, "MaxULOFDMA", wifi6_caps->max_ul_ofdma);

	blobmsg_add_u8(bb, "RTS", wifi6_caps->rts);
	blobmsg_add_u8(bb, "MURTS", wifi6_caps->mu_rts);
	blobmsg_add_u8(bb, "MultiBSSID", wifi6_caps->multi_bssid);
	blobmsg_add_u8(bb, "MUEDCA", wifi6_caps->mu_edca);
	blobmsg_add_u8(bb, "TWTRequestor", wifi6_caps->twt_requester);
	blobmsg_add_u8(bb, "TWTResponder", wifi6_caps->twt_responder);
	blobmsg_add_u8(bb, "SpatialReuse", wifi6_caps->spatial_reuse);
	blobmsg_add_u8(bb, "AnticipatedChannelUsage", wifi6_caps->anticipated_ch_usage);
}

static void dump2_capabilities_wifi7(struct wifi_wifi7_capabilities *caps,
				     struct blob_buf *bb)
{
	void *arr;

	blobmsg_add_u8(bb, "EMLMRSupport",  caps->emlmr);
	blobmsg_add_u8(bb, "EMLSRSupport",  caps->emlsr);
	blobmsg_add_u8(bb, "STRSupport",  caps->str);
	blobmsg_add_u8(bb, "NSTRSupport",  caps->nstr);
	blobmsg_add_u8(bb, "TIDLinkMapNegotiation",  caps->tidlinkmap);
	blobmsg_add_u32(bb, "EMLMRFreqSeparationNumberOfEntries", caps->num_freqsep_emlmr);
	blobmsg_add_u32(bb, "EMLSRFreqSeparationNumberOfEntries", caps->num_freqsep_emlsr);
	blobmsg_add_u32(bb, "STRFreqSeparationNumberOfEntries", caps->num_freqsep_str);
	blobmsg_add_u32(bb, "NSTRFreqSeparationNumberOfEntries", 0);

	arr = blobmsg_open_array(bb, "EMLMRFreqSeparation");
	for (int i = 0; i < MAX_NUM_MLO_FREQ_SEP && i < caps->num_freqsep_emlmr; i++) {
		char ruid[32] = {0};
		void *t;

		t = blobmsg_open_table(bb, "");
		blobmsg_add_string(bb, "RUID",
			to_base64_str(caps->emlmr_freqsep[i].ruid, 6, ruid, sizeof(ruid)));
		blobmsg_add_u32(bb, "FreqSeparation", caps->emlmr_freqsep[i].sep);
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	arr = blobmsg_open_array(bb, "EMLSRFreqSeparation");
	for (int i = 0; i < MAX_NUM_MLO_FREQ_SEP && i < caps->num_freqsep_emlsr; i++) {
		char ruid[32] = {0};
		void *t;

		t = blobmsg_open_table(bb, "");
		blobmsg_add_string(bb, "RUID",
			to_base64_str(caps->emlsr_freqsep[i].ruid, 6, ruid, sizeof(ruid)));
		blobmsg_add_u32(bb, "FreqSeparation", caps->emlsr_freqsep[i].sep);
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	arr = blobmsg_open_array(bb, "STRFreqSeparation");
	for (int i = 0; i < MAX_NUM_MLO_FREQ_SEP && i < caps->num_freqsep_str; i++) {
		char ruid[32] = {0};
		void *t;

		t = blobmsg_open_table(bb, "");
		blobmsg_add_string(bb, "RUID",
			to_base64_str(caps->str_freqsep[i].ruid, 6, ruid, sizeof(ruid)));
		blobmsg_add_u32(bb, "FreqSeparation", caps->str_freqsep[i].sep);
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	arr = blobmsg_open_array(bb, "NSTRFreqSeparation");
	/* placeholder only */
	blobmsg_close_array(bb, arr);
}

static void dump2_network_device_radio_bss_sta_wifi6cap(struct wifi_sta_element *sta,
							struct blob_buf *bb)
{
	void *t;

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.BSS.{i}.STA.{i}.WiFi6Capabilities. */
	t = blobmsg_open_table(bb, "WiFi6Capabilities");
	dump2_capabilities_wifi6(&sta->wifi6caps, bb);
	blobmsg_close_table(bb, t);
}

static void dump2_network_device_radio_bss_sta(struct decollector_private *p,
					       struct wifi_network *net,
					       struct wifi_network_device *dev,
					       struct wifi_radio_element *radio,
					       struct wifi_bss_element *bss,
					       struct wifi_sta_element *sta,
					       struct blob_buf *bb)
{
	struct wifi_sta_meas_report *sta_meas_report = NULL;
	char ip_addr[INET6_ADDRSTRLEN] = { 0 };
	const char *client_caps_b64_ret = NULL;
	const char *caps_b64_ret = NULL;
	char *client_caps_b64 = NULL;
	char caps_b64[32] = { 0 };
	char macaddr[18] = { 0 };
	int he_caps_len;
	void *arr;
	int i;


	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.BSS.{i}.STA.{i}. */
	hwaddr_ntoa(sta->macaddr, macaddr);
	blobmsg_add_string(bb, "MACAddress", macaddr);
	blobmsg_add_string(bb, "TimeStamp", sta->tsp);
	blobmsg_add_double(bb, "Rating", sta->rating);

	caps_b64_ret = to_base64_str(&sta->caps.ht,
				     sizeof(sta->caps.ht), caps_b64,
				     sizeof(caps_b64));
	blobmsg_add_string(bb, "HTCapabilities", caps_b64_ret ? caps_b64_ret : "");

	caps_b64_ret = to_base64_str(sta->caps.vht,
				     sizeof(sta->caps.vht), caps_b64,
				     sizeof(caps_b64));
	blobmsg_add_string(bb, "VHTCapabilities", caps_b64_ret ? caps_b64_ret : "");

	if (sta->caps.valid & HE_CAP_VALID)
		he_caps_len = 2 + sta->caps.he[0];
	else
		he_caps_len = 4; /* pass the usp-validator range test */

	caps_b64_ret = to_base64_str(&sta->caps.he[1],
				     he_caps_len, caps_b64,
				     sizeof(caps_b64));
	blobmsg_add_string(bb, "HECapabilities", caps_b64_ret ? caps_b64_ret : "");


	if (sta->reassoc_framelen) {
		/* Calculate min. size of output buffer as required by base64_encode */
		uint32_t client_cap_b64_len =
			((sta->reassoc_framelen * 4) / 3) + 4;
		client_cap_b64_len += (client_cap_b64_len / 72) + 1;

		client_caps_b64 = malloc(client_cap_b64_len);
		if (client_caps_b64) {
			client_caps_b64_ret = to_base64_str(sta->reassoc_frame,
							    sta->reassoc_framelen,
							    client_caps_b64, client_cap_b64_len);
		}
	}

	blobmsg_add_string(bb, "ClientCapabilities", client_caps_b64_ret ? client_caps_b64_ret : "");
	free(client_caps_b64);

	blobmsg_add_u32(bb, "LastDataDownlinkRate", sta->dl_rate);
	blobmsg_add_u32(bb, "LastDataUplinkRate", sta->ul_rate);
	blobmsg_add_u32(bb, "UtilizationReceive", sta->ul_utilization);
	blobmsg_add_u32(bb, "UtilizationTransmit", sta->dl_utilization);
	blobmsg_add_u32(bb, "EstMACDataRateDownlink", sta->dl_est_thput);
	blobmsg_add_u32(bb, "EstMACDataRateUplink", sta->ul_est_thput);
	blobmsg_add_u32(bb, "SignalStrength", sta->rcpi);
	blobmsg_add_u32(bb, "LastConnectTime", sta->conn_time);
	blobmsg_add_u64(bb, "BytesSent", sta->tx_bytes);
	blobmsg_add_u64(bb, "BytesReceived", sta->rx_bytes);
	blobmsg_add_u64(bb, "PacketsSent", sta->tx_pkts);
	blobmsg_add_u64(bb, "PacketsReceived", sta->rx_pkts);
	blobmsg_add_u64(bb, "ErrorsSent", sta->tx_errors);
	blobmsg_add_u64(bb, "ErrorsReceived", sta->rx_errors);
	blobmsg_add_u64(bb, "RetransCount", sta->rtx_pkts);

	blobmsg_add_u32(bb, "NumberOfMeasureReports", sta->num_meas_reports);
	arr = blobmsg_open_array(bb, "MeasurementReport");

	list_for_each_entry(sta_meas_report, &sta->meas_reportlist, list) {
		/* Calculate min. size of output buffer as required by base64_encode */
		uint32_t b64_len = ((sta_meas_report->element_len * 4) / 3) + 4;
		char *b64_buf = NULL;
		const char *b64_ret = NULL;

		b64_len += (b64_len / 72) + 1;

		b64_buf = malloc(b64_len);

		if (b64_buf) {
			b64_ret = to_base64_str(sta_meas_report->element_data,
						sta_meas_report->element_len,
						b64_buf, b64_len);
		}

		blobmsg_add_string(bb, "", b64_ret ? b64_ret : "");
		free(b64_buf);
	}

	blobmsg_close_array(bb, arr);

	blobmsg_add_string(bb, "IPV4Address",
			   inet_ntop(AF_INET, &sta->ipv4_addr.addr.ip4, ip_addr, INET_ADDRSTRLEN));
	blobmsg_add_string(bb, "IPV6Address",
			   inet_ntop(AF_INET6, &sta->ipv6_addr.addr.ip6, ip_addr, INET6_ADDRSTRLEN));

	blobmsg_add_string(bb, "Hostname", sta->hostname);
	blobmsg_add_string(bb, "CellularDataPreference", "TODO");
	blobmsg_add_u32(bb, "ReAssociationDelay", 0); // TODO unsignedInt(:65535)
	blobmsg_add_u32(bb, "TIDQueueSizesNumberOfEntries", 0); // TODO unsignedInt

	dump2_network_device_radio_bss_sta_multiapsta(p, net, dev, radio, bss, sta, bb);
	dump2_network_device_radio_bss_sta_wifi6cap(sta, bb);

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.BSS.{i}.STA.{i}.TIDQueueSizes.{i}. */
	arr = blobmsg_open_array(bb, "TIDQueueSizes");
	for (i = 0; i < 0 /* TODO */; i++) {
		blobmsg_add_u32(bb, "TID", 0); // TODO unsignedInt(:255)
		blobmsg_add_u32(bb, "Size", 0); // TODO unsignedInt(:255)
	}
	blobmsg_close_array(bb, arr); // TIDQueueSizes
}


static void dump2_network_device_radio_bss(struct decollector_private *p,
					   struct wifi_network *net,
					   struct wifi_network_device *dev,
					   struct wifi_radio_element *radio,
					   struct wifi_bss_element *bss,
					   struct blob_buf *bb)
{
	struct wifi_sta_element *sta;
	char est_bk[16] = {0};
	char est_be[16] = {0};
	char est_vi[16] = {0};
	char est_vo[16] = {0};
	char bssid[18] = {0};
	void *bss_table;
	void *arr;
	void *t;
	int i;

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.BSS.{i}. */

	bss_table = blobmsg_open_table(bb, "");

	hwaddr_ntoa(bss->bssid, bssid);
	blobmsg_add_string(bb, "BSSID", bssid);
	blobmsg_add_string(bb, "SSID", (char *)bss->ssid);
	blobmsg_add_string(bb, "Enabled", bss->enabled ? "true" : "false");
	blobmsg_add_u32(bb, "LastChange", bss->uptime);
	blobmsg_add_string(bb, "TimeStamp", bss->tsp);
	blobmsg_add_u64(bb, "UnicastBytesSent", bss->tx_ucast_bytes);
	blobmsg_add_u64(bb, "UnicastBytesReceived", bss->rx_ucast_bytes);
	blobmsg_add_u64(bb, "MulticastBytesSent", bss->tx_mcast_bytes);
	blobmsg_add_u64(bb, "MulticastBytesReceived", bss->rx_mcast_bytes);
	blobmsg_add_u64(bb, "BroadcastBytesSent", bss->tx_bcast_bytes);
	blobmsg_add_u64(bb, "BroadcastBytesReceived", bss->rx_bcast_bytes);
	blobmsg_add_u32(bb, "ByteCounterUnits", bss->unit);
	blobmsg_add_string(bb, "Profile1bSTAsDisallowed", "TODO bool");
	blobmsg_add_string(bb, "Profile2bSTAsDisallowed", "TODO bool");
	blobmsg_add_u32(bb, "AssociationAllowanceStatus", 0); // TODO unsignedInt(0:1)

	to_base64_str(bss->est_wmm_be, 3, est_be, sizeof(est_be));
	blobmsg_add_string(bb, "EstServiceParametersBE", est_be);

	to_base64_str(bss->est_wmm_bk, 3, est_bk, sizeof(est_bk));
	blobmsg_add_string(bb, "EstServiceParametersBK", est_bk);

	to_base64_str(bss->est_wmm_vo, 3, est_vo, sizeof(est_vo));
	blobmsg_add_string(bb, "EstServiceParametersVO", est_vo);

	to_base64_str(bss->est_wmm_vi, 3, est_vi, sizeof(est_vi));
	blobmsg_add_string(bb, "EstServiceParametersVI", est_vi);

	blobmsg_add_u8(bb, "BackhaulUse", bss->is_bbss);
	blobmsg_add_u8(bb, "FronthaulUse", bss->is_fbss);
	blobmsg_add_u8(bb, "R1disallowed", bss->r1_disallowed);
	blobmsg_add_u8(bb, "R2disallowed", bss->r2_disallowed);
	blobmsg_add_u8(bb, "MultiBSSID", bss->multi_bssid);
	blobmsg_add_u8(bb, "TransmittedBSSID", bss->transmitted_bssid);

	arr = blobmsg_open_array(bb, "FronthaulAKMsAllowed");
	for (i = 0; i < 0 /* TODO */; i++) {
		t = blobmsg_open_table(bb, "");
		 ; // list of string
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	arr = blobmsg_open_array(bb, "BackhaulAKMsAllowed");
	for (i = 0; i < 0 /* TODO */; i++) {
		t = blobmsg_open_table(bb, "");
		 ; // list of string
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	blobmsg_add_u32(bb, "STANumberOfEntries", bss->num_stations);
	blobmsg_add_u32(bb, "QMDescriptorNumberOfEntries", 0); // TODO unsignedInt

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.BSS.{i}.QMDescriptor.{i}. */
	arr = blobmsg_open_array(bb, "QMDescriptor");
	for (i = 0; i < 0 /* TODO */; i++) {
		//hwaddr_ntoa(usta->macaddr, macaddr);)
		blobmsg_add_string(bb, "ClientMAC", "TODO MACAddress");
		blobmsg_add_string(bb, "DescriptorElement", "TODO hexBinary");
	}
	blobmsg_close_array(bb, arr); // QMDescriptor

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.BSS.{i}.MultiAPSteering. */
	t = blobmsg_open_table(bb, "MultiAPSteering");
	blobmsg_add_u64(bb, "BlacklistAttempts", 0); //TODO StatsCounter64
	blobmsg_add_u64(bb, "BTMAttempts", 0); //TODO StatsCounter64
	blobmsg_add_u64(bb, "BTMQueryResponses", 0); //TODO StatsCounter64
	blobmsg_close_table(bb, t); // MultiAPSteering

	arr = blobmsg_open_array(bb, "STAList");
	list_for_each_entry(sta, &bss->stalist, list) {
		t = blobmsg_open_table(bb, "");
		dump2_network_device_radio_bss_sta(p, net, dev, radio, bss, sta, bb);  /* cppcheck-suppress uninitvar */
		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr);

	blobmsg_close_table(bb, bss_table);
}

static void dump2_network_device_radio_backhaulsta(struct decollector_private *p,
						   struct wifi_network *net,
						   struct wifi_network_device *dev,
						   struct wifi_radio_element *radio,
						   struct blob_buf *bb)
{
	struct wifi_backhaul_element *bsta = &radio->bsta;
	char macaddr[18] = { 0 };
	void *t;

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.BackhaulSta. */
	t = blobmsg_open_table(bb, "BackhaulSta");
	hwaddr_ntoa(bsta->macaddr, macaddr);
	blobmsg_add_string(bb, "MACAddress", macaddr);
	blobmsg_close_table(bb, t); // BackhaulSta
}

static void dump2_network_device_radio_scancapability(struct decollector_private *p,
						      struct wifi_network *net,
						      struct wifi_network_device *dev,
						      struct wifi_radio_element *radio,
						      struct blob_buf *bb)
{
	void *scan_cap, *op_class;
	void *op_class_array, *channel_array;
	int i, j;

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.ScanCapability. */
	scan_cap = blobmsg_open_table(bb, "ScanCapability");

	blobmsg_add_u8(bb, "OnBootOnly", radio->scan_caps.boot_only);
	blobmsg_add_u32(bb, "Impact", radio->scan_caps.impact);
	blobmsg_add_u32(bb, "MinimumInterval", radio->scan_caps.interval);
	blobmsg_add_u32(bb, "OpClassChannelsNumberOfEntries", radio->scan_caps.opclass.num_opclass);

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.ScanCapability.OpClassChannels.{i}. */
	op_class_array = blobmsg_open_array(bb, "OpClassChannels");

	for (i = 0; i < radio->scan_caps.opclass.num_opclass; i++) {
		op_class = blobmsg_open_table(bb, "");
		blobmsg_add_u32(bb, "OpClass", radio->scan_caps.opclass.opclass[i].id);

		channel_array = blobmsg_open_array(bb, "ChannelList");

		for (j = 0; j < radio->scan_caps.opclass.opclass[i].num_channel; j++)
			blobmsg_add_u32(bb, NULL, radio->scan_caps.opclass.opclass[i].channel[j].channel);

		blobmsg_close_array(bb, channel_array);

		blobmsg_close_table(bb, op_class);
	}
	blobmsg_close_array(bb, op_class_array); // OpClassChannels

	blobmsg_close_table(bb, scan_cap); // ScanCapability
}

static void dump2_network_device_radio_caccapability(struct decollector_private *p,
						     struct wifi_network *net,
						     struct wifi_network_device *dev,
						     struct wifi_radio_element *radio,
						     struct blob_buf *bb)
{
	void *cac_method_arr, *op_class_channels_arr, *channel_arr;
	void *cac_capability, *cac_method, *opclass_channel;
	int i, j;
	struct wifi_radio_cac_capabilities *cac_caps;

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.CACCapability. */
	cac_capability = blobmsg_open_table(bb, "CACCapability");
	blobmsg_add_u32(bb, "CACMethodNumberOfEntries", list_count(&radio->cac_capslist));

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.CACCapability.CACMethod.{i}. */
	cac_method_arr = blobmsg_open_array(bb, "CACMethod");
	list_for_each_entry(cac_caps, &radio->cac_capslist, list) {
		cac_method = blobmsg_open_table(bb, "");

		blobmsg_add_u32(bb, "Method", cac_caps->method);
		blobmsg_add_u32(bb, "NumberOfSeconds", cac_caps->num_seconds);
		blobmsg_add_u32(bb, "OpClassChannelsNumberOfEntries", cac_caps->opclasses.num_opclass);

		/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.CACCapability.CACMethod.{i}.OpClassChannels.{i}. */
		op_class_channels_arr = blobmsg_open_array(bb, "OpClassChannels");
		for (i = 0; i < cac_caps->opclasses.num_opclass; ++i) {
			opclass_channel = blobmsg_open_table(bb, "");

			blobmsg_add_u32(bb, "OpClass", cac_caps->opclasses.opclass[i].id);

			channel_arr = blobmsg_open_array(bb, "ChannelList");
			for (j = 0; j < cac_caps->opclasses.opclass[i].num_channel; ++j)
				blobmsg_add_u32(bb, "", cac_caps->opclasses.opclass[i].channel[j].channel);

			blobmsg_close_array(bb, channel_arr);

			blobmsg_close_table(bb, opclass_channel);
		}
		blobmsg_close_array(bb, op_class_channels_arr);

		blobmsg_close_table(bb, cac_method);
	}
	blobmsg_close_array(bb, cac_method_arr);

	blobmsg_close_table(bb, cac_capability);
}

static void dump2_network_device_radio_capabilities(struct decollector_private *p,
						    struct wifi_network *net,
						    struct wifi_network_device *dev,
						    struct wifi_radio_element *radio,
						    struct blob_buf *bb)
{
	const char *caps_b64_ret = NULL;
	struct wifi_akm_suite *bss_akm;
	void *capabilities, *w6, *w7;
	char caps_b64[32] = {0};
	void *arr;
	int i;


	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.Capabilities. */
	capabilities = blobmsg_open_table(bb, "Capabilities");

	caps_b64_ret = to_base64_str(&radio->caps.ht,
				     sizeof(radio->caps.ht), caps_b64,
				     sizeof(caps_b64));
	blobmsg_add_string(bb, "HTCapabilities", caps_b64_ret ? caps_b64_ret : "");

	caps_b64_ret = to_base64_str(radio->caps.vht, sizeof(radio->caps.vht),
				     caps_b64, sizeof(caps_b64));
	blobmsg_add_string(bb, "VHTCapabilities",  caps_b64_ret ? caps_b64_ret : "");

	/* "HECapabilities" is deprecated in tr-181 2.15 and replaced by WiFi6AP/STARole */

	blobmsg_add_u32(bb, "CapableOperatingClassProfileNumberOfEntries", radio->supp_opclass.num_opclass);
	blobmsg_add_u32(bb, "AKMFrontHaulNumberOfEntries", radio->num_bbss_akm);
	blobmsg_add_u32(bb, "AKMBackhaulNumberOfEntries", radio->num_bbss_akm);

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.Capabilities.WiFi6APRole. */
	w6 = blobmsg_open_table(bb, "WiFi6APRole");
	dump2_capabilities_wifi6(&radio->wifi6caps_ap, bb);
	blobmsg_close_table(bb, w6);

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.Capabilities.WiFi6bSTARole. */
	w6 = blobmsg_open_table(bb, "WiFi6bSTARole");
	dump2_capabilities_wifi6(&radio->wifi6caps_bsta, bb);
	blobmsg_close_table(bb, w6);

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.Capabilities.WiFi7APRole. */
	w7 = blobmsg_open_table(bb, "WiFi7APRole");
	dump2_capabilities_wifi7(&radio->wifi7caps_ap, bb);
	blobmsg_close_table(bb, w7);

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.Capabilities.WiFi7bSTARole. */
	w7 = blobmsg_open_table(bb, "WiFi7bSTARole");
	dump2_capabilities_wifi7(&radio->wifi7caps_bsta, bb);
	blobmsg_close_table(bb, w7);

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.Capabilities.AKMFrontHaul.{i}. */
	arr = blobmsg_open_array(bb, "AKMFrontHaul");
	list_for_each_entry(bss_akm, &radio->fbss_akmlist, list) {
		char oui[16] = {0};
		void *tab;

		to_base64_str(bss_akm->oui, 3, oui, sizeof(oui));

		tab = blobmsg_open_table(bb, "");
		blobmsg_add_string(bb, "OUI", oui);
		blobmsg_add_u32(bb, "Type", bss_akm->type);
		blobmsg_close_table(bb, tab);
	}
	blobmsg_close_array(bb, arr);

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.Capabilities.AKMBackhaul.{i}. */
	arr = blobmsg_open_array(bb, "AKMBackhaul");
	list_for_each_entry(bss_akm, &radio->bbss_akmlist, list) {
		char oui[16] = {0};
		void *tab;

		to_base64_str(bss_akm->oui, 3, oui, sizeof(oui));

		tab = blobmsg_open_table(bb, "");
		blobmsg_add_string(bb, "OUI", oui);
		blobmsg_add_u32(bb, "Type", bss_akm->type);
		blobmsg_close_table(bb, tab);
	}
	blobmsg_close_array(bb, arr);

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.Capabilities.CapableOperatingClassProfile.{i}. */
	arr = blobmsg_open_array(bb, "CapableOperatingClassProfile");
	for (i = 0; i < radio->supp_opclass.num_opclass; ++i) {
		const struct wifi_radio_opclass_entry *opclass =
			&radio->supp_opclass.opclass[i];
		void *non_op_arr, *op_class_table;
		int j;
		uint32_t num_excluded_channels = 0;

		op_class_table = blobmsg_open_table(bb, "");

		blobmsg_add_u32(bb, "Class", opclass->id);
		blobmsg_add_u32(bb, "MaxTxPower", opclass->max_txpower);
		non_op_arr = blobmsg_open_array(bb, "NonOperable");
		for (j = 0; j < opclass->num_channel; ++j) {
			if (opclass->channel[j].preference == WIFI_RADIO_OPCLASS_NON_OPERABLE) {
				++num_excluded_channels;
				blobmsg_add_u32(bb, "", opclass->channel[j].channel);
			}
		}
		blobmsg_close_array(bb, non_op_arr);
		blobmsg_add_u32(bb, "NumberOfNonOperChan", num_excluded_channels);

		blobmsg_close_table(bb, op_class_table);
	}
	blobmsg_close_array(bb, arr);

	blobmsg_close_table(bb, capabilities);
}

static void dump2_network_device_radio_opclass(struct decollector_private *p,
					       struct wifi_network *net,
					       struct wifi_network_device *dev,
					       struct wifi_radio_element *radio,
					       struct blob_buf *bb)
{
	void *arr, *arr2, *t;
	int i, j;
	char out_str[17];
	const uint8_t *bit_map;
	uint32_t num_dis_opclass = 0;

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.CurrentOperatingClassProfile.{i}. */
	arr = blobmsg_open_array(bb, "CurrentOperatingClassProfile");
	for (i = 0; i < radio->cur_opclass.num_opclass; i++) {
		char tsp[32];
		void *op_class_table = blobmsg_open_table(bb, "");

		blobmsg_add_u32(bb, "Class", radio->cur_opclass.opclass[i].id);
		blobmsg_add_u32(bb, "Channel", radio->cur_opclass.opclass[i].channel[0].channel);
		/* todo: max_txpower here is current tx power  */
		blobmsg_add_u32(bb, "TxPower", radio->cur_opclass.opclass[i].max_txpower);
		blobmsg_add_u32(bb, "TransmitPowerLimit", 0); // TODO int(-128:127)
		blobmsg_add_string(bb, "TimeStamp",
			get_timestamp(&radio->cur_opclass.entry_time.tv_sec, tsp, sizeof(tsp)));

		blobmsg_close_table(bb, op_class_table);
	}
	blobmsg_close_array(bb, arr); // CurrentOperatingClassProfile

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.DisAllowedOpClassChannels.{i}. */
	arr = blobmsg_open_array(bb, "DisAllowedOpClassChannels");
	for (i = 0; i < radio->pref_opclass.num_opclass &&
			i < (int)ARRAY_SIZE(radio->pref_opclass.opclass); i++) {
		struct wifi_radio_opclass_entry *dis_opclass = &radio->pref_opclass.opclass[i];

		if (((dis_opclass->channel[0].preference & CHANNEL_PREF_MASK) >> 4) != 0 ||
				(dis_opclass->channel[0].preference & CHANNEL_PREF_REASON) == 0)
			continue;

		num_dis_opclass++;
		t = blobmsg_open_table(bb, "");
		blobmsg_add_string(bb, "Enable", "true");
		blobmsg_add_u32(bb, "OpClass", dis_opclass->id);
		arr2 = blobmsg_open_array(bb, "ChannelList");
		for (j = 0; j < dis_opclass->num_channel &&
				j < (int)ARRAY_SIZE(dis_opclass->channel); j++)
			blobmsg_add_u32(bb, "", dis_opclass->channel[j].channel);
		blobmsg_close_array(bb, arr2);

		blobmsg_close_table(bb, t);
	}
	blobmsg_close_array(bb, arr); // DisAllowedOpClassChannels

	blobmsg_add_u32(bb, "DisAllowedOpClassChannelsNumberOfEntries", num_dis_opclass);

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.OpClassPreference.{i}. */
	arr = blobmsg_open_array(bb, "OpClassPreference");
	for (i = 0; i < radio->pref_opclass.num_opclass &&
			i < ARRAY_SIZE(radio->pref_opclass.opclass); i++) {
		void *op_class_pref_table = blobmsg_open_table(bb, "");

		blobmsg_add_u32(bb, "OpClass", radio->pref_opclass.opclass[i].id);

		arr2 = blobmsg_open_array(bb, "ChannelList");
		for (j = 0; j < radio->pref_opclass.opclass[i].num_channel &&
				j < ARRAY_SIZE(radio->pref_opclass.opclass[i].channel); j++)
			blobmsg_add_u32(bb, "", radio->pref_opclass.opclass[i].channel[j].channel);
		blobmsg_close_array(bb, arr2);

		const uint8_t op_pref = radio->pref_opclass.opclass[i].channel[0].preference;

		blobmsg_add_u32(bb, "Preference", (op_pref & CHANNEL_PREF_MASK) >> 4);
		blobmsg_add_u32(bb, "ReasonCode", op_pref & CHANNEL_PREF_REASON);

		blobmsg_close_table(bb, op_class_pref_table);
	}
	blobmsg_close_array(bb, arr); // OpClassPreference

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.SpatialReuse. */
	t = blobmsg_open_table(bb, "SpatialReuse");
	blobmsg_add_u32(bb, "PartialBSSColor", radio->spatial_reuse_report.partial_bss_color);
	blobmsg_add_u32(bb, "BSSColor", radio->spatial_reuse_report.bss_color);
	blobmsg_add_u8(bb, "HESIGASpatialReuseValue15Allowed", radio->spatial_reuse_report.hesiga_val15_allowed);
	blobmsg_add_u8(bb, "SRGInformationValid", radio->spatial_reuse_report.srg_info_valid);
	blobmsg_add_u8(bb, "NonSRGOffsetValid", radio->spatial_reuse_report.non_srg_offset_valid);
	blobmsg_add_u8(bb, "PSRDisallowed", radio->spatial_reuse_report.psr_disallowed);
	blobmsg_add_u32(bb, "NonSRGOBSSPDMaxOffset", radio->spatial_reuse_report.non_srg_obsspd_max_offset);
	blobmsg_add_u32(bb, "SRGOBSSPDMinOffset", radio->spatial_reuse_report.srg_obsspd_min_offset);
	blobmsg_add_u32(bb, "SRGOBSSPDMaxOffset", radio->spatial_reuse_report.srg_obsspd_max_offset);

	bit_map = radio->spatial_reuse_report.srg_bss_color_bitmap;
	snprintf(out_str, sizeof(out_str), "%02X%02X%02X%02X%02X%02X%02X%02X",
		 bit_map[0], bit_map[1], bit_map[2], bit_map[3],
		 bit_map[4], bit_map[5], bit_map[6], bit_map[7]);
	blobmsg_add_string(bb, "SRGBSSColorBitmap", out_str);
	bit_map = radio->spatial_reuse_report.srg_partial_bssid_bitmap;
	snprintf(out_str, sizeof(out_str), "%02X%02X%02X%02X%02X%02X%02X%02X",
		 bit_map[0], bit_map[1], bit_map[2], bit_map[3],
		 bit_map[4], bit_map[5], bit_map[6], bit_map[7]);
	blobmsg_add_string(bb, "SRGPartialBSSIDBitmap", out_str);
	bit_map = radio->spatial_reuse_report.neighbor_bss_color_in_use_bitmap;
	snprintf(out_str, sizeof(out_str), "%02X%02X%02X%02X%02X%02X%02X%02X",
		 bit_map[0], bit_map[1], bit_map[2], bit_map[3],
		 bit_map[4], bit_map[5], bit_map[6], bit_map[7]);
	blobmsg_add_string(bb, "NeighborBSSColorInUseBitmap", out_str);
	blobmsg_close_table(bb, t); // SpatialReuse
}

static void dump2_network_device_radio(struct decollector_private *p,
				       struct wifi_network *net,
				       struct wifi_network_device *dev,
				       struct wifi_radio_element *radio,
				       struct blob_buf *bb)
{
	struct wifi_unassoc_sta_element *usta = NULL;
	struct wifi_bss_element *bss = NULL;
	char id[32];
	void *arr, *t, *radio_table;

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}. */

	radio_table = blobmsg_open_table(bb, "");

	blobmsg_add_string(bb, "ID", to_base64_str(radio->macaddr, sizeof(radio->macaddr), id, sizeof(id)));
	blobmsg_add_string(bb, "Enabled", radio->enabled ? "true" : "false");
	blobmsg_add_u32(bb, "Noise", radio->anpi);
	blobmsg_add_u32(bb, "Utilization", radio->total_utilization);
	blobmsg_add_u32(bb, "Transmit", radio->tx_utilization);
	blobmsg_add_u32(bb, "ReceiveSelf", radio->rx_utilization);
	blobmsg_add_u32(bb, "ReceiveOther", radio->other_utilization);
	blobmsg_add_u8(bb, "TrafficSeparationCombinedFronthaul", radio->ts_combined_fronthaul);
	blobmsg_add_u8(bb, "TrafficSeparationCombinedBackhaul", radio->ts_combined_backhaul);

	/* Multi-AP Policy Config Request / Steering Policy TLV */
	blobmsg_add_u32(bb, "SteeringPolicy", radio->steer_policy);
	blobmsg_add_u32(bb, "ChannelUtilizationThreshold", radio->channel_util_threshold);
	blobmsg_add_u32(bb, "RCPISteeringThreshold", radio->rcpi_steer_threshold);

	/* Multi-AP Policy Config Request / Metric Reporting Policy TLV */
	blobmsg_add_u32(bb, "STAReportingRCPIThreshold", radio->report.sta_rcpi_threshold);
	blobmsg_add_u32(bb, "STAReportingRCPIHysteresisMarginOverride",
			radio->report.sta_rcpi_margin_override);
	blobmsg_add_u32(bb, "ChannelUtilizationReportingThreshold",
			radio->report.channel_util_threshold);
	blobmsg_add_string(bb, "AssociatedSTATrafficStatsInclusionPolicy",
			radio->report.include_sta_stats ? "true" : "false");
	blobmsg_add_string(bb, "AssociatedSTALinkMetricsInclusionPolicy",
			radio->report.include_sta_metrics ? "true" : "false");

	blobmsg_add_string(bb, "ChipsetVendor", radio->vendor);

	/* Multi-AP Policy Config Request / Metric Reporting Policy TLV */
	blobmsg_add_string(bb, "APMetricsWiFi6",
			radio->report.include_wifi6_metrics ? "true" : "false");

	blobmsg_add_u32(bb, "MaxBSS", radio->max_bssnum);
	blobmsg_add_u32(bb, "CurrentOperatingClassProfileNumberOfEntries", radio->cur_opclass.num_opclass);
	blobmsg_add_u32(bb, "UnassociatedSTANumberOfEntries", radio->num_unassoc_sta);
	blobmsg_add_u32(bb, "BSSNumberOfEntries", radio->num_bss);
	blobmsg_add_u32(bb, "ScanResultNumberOfEntries", radio->num_scanresult);
	blobmsg_add_u32(bb, "OpClassPreferenceNumberOfEntries", radio->pref_opclass.num_opclass);

	dump2_network_device_radio_scanresult(p, net, dev, radio, bb);
	dump2_network_device_radio_backhaulsta(p, net, dev, radio, bb);
	dump2_network_device_radio_scancapability(p, net, dev, radio, bb);
	dump2_network_device_radio_caccapability(p, net, dev, radio, bb);
	dump2_network_device_radio_capabilities(p, net, dev, radio, bb);
	dump2_network_device_radio_opclass(p, net, dev, radio, bb);

	arr = blobmsg_open_array(bb, "BSSList");
	list_for_each_entry(bss, &radio->bsslist, list) {
		dump2_network_device_radio_bss(p, net, dev, radio, bss, bb);
	}
	blobmsg_close_array(bb, arr);

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.UnassociatedSTA.{i}. */
	arr = blobmsg_open_array(bb, "UnassociatedSTA");
	list_for_each_entry(usta, &radio->unassoc_stalist, list) {
		char usta_macaddr[18] = { 0 };

		hwaddr_ntoa(usta->macaddr, usta_macaddr);
		blobmsg_add_string(bb, "MACAddress", usta_macaddr);
		blobmsg_add_u32(bb, "SignalStrength", usta->rcpi);
	}
	blobmsg_close_array(bb, arr); // UnassociatedSTA

	/* Device.WiFi.DataElements.Network.Device.{i}.Radio.{i}.MultiAPRadio. */
	t = blobmsg_open_table(bb, "MultiAPRadio");
	//Unknown type  list(1024) of unsignedInt TOKEN RadarDetections
	blobmsg_close_table(bb, t); // MultiAPRadio

	blobmsg_close_table(bb, radio_table);
}

static void dump2_network_device_apmld(struct decollector_private *p,
				       struct wifi_apmld_element *apmld,
				       struct blob_buf *bb)
{
	struct wifi_affiliated_ap_element *ap = NULL;
	struct wifi_stamld_element *stamld = NULL;
	struct wifi_ttlm_element *ttlm = NULL;
	struct wifi_linkopcmap_element *linkopcmap = NULL;
	void *apmld_cfg_table;
	void *apmld_table;
	void *arr;

	/* Device.WiFi.DataElements.Network.Device.{i}.APMLD.{i}. */
	apmld_table = blobmsg_open_table(bb, "");
	blobmsg_add_macaddr(bb, "MLDMACAddress", apmld->mld_macaddr);
	blobmsg_add_u32(bb, "TIDLinkMapNumberOfEntries", apmld->num_ttlm);
	blobmsg_add_u32(bb, "AffiliatedAPNumberOfEntries", apmld->num_ap);
	blobmsg_add_u32(bb, "STAMLDNumberOfEntries", apmld->num_sta);
	blobmsg_add_u32(bb, "LinkToOpClassMapNumberOfEntries", apmld->num_linkopcmap);

	/* Device.WiFi.DataElements.Network.Device.{i}.APMLD.{i}.APMLDConfig. */
	apmld_cfg_table = blobmsg_open_table(bb, "APMLDConfig");
	blobmsg_add_u8(bb, "EMLMREnabled", apmld->emlmr_enabled);
	blobmsg_add_u8(bb, "EMLSREnabled", apmld->emlsr_enabled);
	blobmsg_add_u8(bb, "STREnabled", apmld->str_enabled);
	blobmsg_add_u8(bb, "NSTREnabled", apmld->nstr_enabled);
	blobmsg_add_u8(bb, "TIDLinkMapNegotiation", apmld->ttlm_enabled);
	blobmsg_close_table(bb, apmld_cfg_table);

	/* Device.WiFi.DataElements.Network.Device.{i}.APMLD.{i}.TIDLinkMap.{i}. */
	arr = blobmsg_open_array(bb, "TIDLinkMapList");
	list_for_each_entry(ttlm, &apmld->ttlmlist, list) {
		void *tab;

		tab = blobmsg_open_table(bb, "TIDLinkMap");
		blobmsg_add_string(bb, "Direction", ttlm->dir == 0 ? "Up" : "Down");	//FIXME
		blobmsg_add_u32(bb, "TID", ttlm->tid);
		blobmsg_add_u32(bb, "LinkID", ttlm->linkid);
		blobmsg_close_table(bb, tab);
	}
	blobmsg_close_array(bb, arr);

	/* Device.WiFi.DataElements.Network.Device.{i}.APMLD.{i}.LinkToOpClassMap.{i}. */
	arr = blobmsg_open_array(bb, "LinkToOpClassMapList");
	list_for_each_entry(linkopcmap, &apmld->linkopcmaplist, list) {
		void *tab;

		tab = blobmsg_open_table(bb, "");
		blobmsg_add_u32(bb, "LinkID", linkopcmap->linkid);
		blobmsg_add_u32(bb, "OpClass", linkopcmap->opclass);
		blobmsg_close_table(bb, tab);
	}
	blobmsg_close_array(bb, arr);

	/* Device.WiFi.DataElements.Network.Device.{i}.APMLD.{i}.AffiliatedAP.{i}. */
	arr = blobmsg_open_array(bb, "AffiliatedAPList");
	list_for_each_entry(ap, &apmld->aplist, list) {
		char est_bk[16] = {0};
		char est_be[16] = {0};
		char est_vi[16] = {0};
		char est_vo[16] = {0};
		char ruid[32] = {0};
		void *tab;

		tab = blobmsg_open_table(bb, "");
		blobmsg_add_macaddr(bb, "BSSID", ap->bssid);
		blobmsg_add_u32(bb, "LinkID", ap->linkid);
		blobmsg_add_string(bb, "RUID",
			to_base64_str(ap->ruid, 6, ruid, sizeof(ruid)));

		blobmsg_add_u64(bb, "PacketsSent", ap->tx_packets);
		blobmsg_add_u64(bb, "PacketsReceived", ap->rx_packets);
		blobmsg_add_u64(bb, "UnicastBytesSent", ap->tx_ucast_bytes);
		blobmsg_add_u64(bb, "UnicastBytesReceived", ap->rx_ucast_bytes);
		blobmsg_add_u64(bb, "ErrorsSent", ap->tx_errors);
		blobmsg_add_u64(bb, "MulticastBytesSent", ap->tx_mcast_bytes);
		blobmsg_add_u64(bb, "MulticastBytesReceived", ap->rx_mcast_bytes);
		blobmsg_add_u64(bb, "BroadcastBytesSent", ap->tx_bcast_bytes);
		blobmsg_add_u64(bb, "BroadcastBytesReceived", ap->rx_bcast_bytes);

		to_base64_str(ap->est_wmm_be, 3, est_be, sizeof(est_be));
		blobmsg_add_string(bb, "EstServiceParametersBE", est_be);
		to_base64_str(ap->est_wmm_bk, 3, est_bk, sizeof(est_bk));
		blobmsg_add_string(bb, "EstServiceParametersBK", est_bk);
		to_base64_str(ap->est_wmm_vo, 3, est_vo, sizeof(est_vo));
		blobmsg_add_string(bb, "EstServiceParametersVO", est_vo);
		to_base64_str(ap->est_wmm_vi, 3, est_vi, sizeof(est_vi));
		blobmsg_add_string(bb, "EstServiceParametersVI", est_vi);
		blobmsg_close_table(bb, tab);
	}
	blobmsg_close_array(bb, arr);

	/* Device.WiFi.DataElements.Network.Device.{i}.APMLD.{i}.STAMLD.{i}. */
	arr = blobmsg_open_array(bb, "STAMLDList");
	list_for_each_entry(stamld, &apmld->stamldlist, list) {
		struct wifi_affiliated_sta_element *sta;
		char ip_addr[INET6_ADDRSTRLEN] = {0};
		void *t1, *t2;
		void *arr2;

		t1 = blobmsg_open_table(bb, "");
		blobmsg_add_macaddr(bb, "MLDMACAddress", stamld->mld_macaddr);
		blobmsg_add_double(bb, "Rating", stamld->rating);
		blobmsg_add_string(bb, "Hostname", stamld->hostname);
		blobmsg_add_string(bb, "IPV4Address",
				   inet_ntop(AF_INET, &stamld->ipv4_addr.addr.ip4, ip_addr, INET_ADDRSTRLEN));
		blobmsg_add_string(bb, "IPV6Address",
				   inet_ntop(AF_INET6, &stamld->ipv6_addr.addr.ip6, ip_addr, INET6_ADDRSTRLEN));

		blobmsg_add_u8(bb, "IsbSTA", stamld->is_bsta);
		blobmsg_add_u32(bb, "LastConnectTime", stamld->conn_time);
		blobmsg_add_u64(bb, "BytesSent", stamld->tx_bytes);
		blobmsg_add_u64(bb, "BytesReceived", stamld->rx_bytes);
		blobmsg_add_u64(bb, "PacketsSent", stamld->tx_packets);
		blobmsg_add_u64(bb, "PacketsReceived", stamld->rx_packets);
		blobmsg_add_u64(bb, "ErrorsSent", stamld->tx_errors);
		blobmsg_add_u64(bb, "ErrorsReceived", stamld->rx_errors);
		blobmsg_add_u64(bb, "RetransCount", stamld->rtx_packets);
		blobmsg_add_u32(bb, "STATIDLinkMapNumberOfEntries", stamld->num_ttlm);
		blobmsg_add_u32(bb, "AffiliatedSTANumberOfEntries", stamld->num_sta);

		t2 = blobmsg_open_table(bb, "WiFi7Capabilities");
		blobmsg_add_u8(bb, "EMLMRSupport", stamld->emlmr_supported);
		blobmsg_add_u8(bb, "EMLSRSupport", stamld->emlsr_supported);
		blobmsg_add_u8(bb, "STRSupport", stamld->str_supported);
		blobmsg_add_u8(bb, "NSTRSupport", stamld->nstr_supported);
		blobmsg_add_u8(bb, "TIDLinkMapNegotiation", stamld->ttlm_supported);
		blobmsg_close_table(bb, t2);

		t2 = blobmsg_open_table(bb, "STAMLDConfig");
		blobmsg_add_u8(bb, "EMLMREnabled", stamld->emlmr_enabled);
		blobmsg_add_u8(bb, "EMLSREnabled", stamld->emlsr_enabled);
		blobmsg_add_u8(bb, "STREnabled", stamld->str_enabled);
		blobmsg_add_u8(bb, "NSTREnabled", stamld->nstr_enabled);
		blobmsg_add_u8(bb, "TIDLinkMapNegotiation", stamld->ttlm_enabled);
		blobmsg_close_table(bb, t2);

		arr2 = blobmsg_open_array(bb, "STATIDLinkMapList");
		list_for_each_entry(ttlm, &stamld->ttlmlist, list) {
			void *t3;

			t3 = blobmsg_open_table(bb, "TIDLinkMap");
			blobmsg_add_string(bb, "Direction", ttlm->dir == 0 ? "Up" : "Down");	//FIXME
			blobmsg_add_u32(bb, "TID", ttlm->tid);
			blobmsg_add_u32(bb, "LinkID", ttlm->linkid);
			blobmsg_close_table(bb, t3);
		}
		blobmsg_close_array(bb, arr2);

		/* Device.WiFi.DataElements.Network.Device.{i}.APMLD.{i}.STAMLD.{i}.AffiliatedSTA.{i}. */
		arr2 = blobmsg_open_array(bb, "AffiliatedSTAList");
		list_for_each_entry(sta, &stamld->stalist, list) {
			void *tab;

			tab = blobmsg_open_table(bb, "");
			blobmsg_add_macaddr(bb, "MACAddress", sta->macaddr);
			blobmsg_add_macaddr(bb, "BSSID", sta->bssid);
			blobmsg_add_u64(bb, "BytesSent", sta->tx_bytes);
			blobmsg_add_u64(bb, "BytesReceived", sta->rx_bytes);
			blobmsg_add_u64(bb, "PacketsSent", sta->tx_packets);
			blobmsg_add_u64(bb, "PacketsReceived", sta->rx_packets);
			blobmsg_add_u64(bb, "ErrorsSent", sta->tx_errors);
			blobmsg_add_u32(bb, "SignalStrength", sta->rcpi);
			blobmsg_add_u32(bb, "EstMACDataRateDownlink", sta->dl_est_thput);
			blobmsg_add_u32(bb, "EstMACDataRateUplink", sta->ul_est_thput);
			blobmsg_add_u32(bb, "LastDataDownlinkRate", sta->dl_rate);
			blobmsg_add_u32(bb, "LastDataUplinkRate", sta->ul_rate);
			blobmsg_add_u32(bb, "UtilizationReceive", sta->ul_utilization);
			blobmsg_add_u32(bb, "UtilizationTransmit", sta->dl_utilization);
			blobmsg_close_table(bb, tab);
		}
		blobmsg_close_array(bb, arr2);
		blobmsg_close_table(bb, t1);
	}
	blobmsg_close_array(bb, arr);

	blobmsg_close_table(bb, apmld_table);
}

static void dump2_network_device_bstamld(struct decollector_private *p,
				       struct wifi_network_device *dev,
				       struct blob_buf *bb)
{
	struct wifi_affiliated_sta_element *sta = NULL;

	void *bstamld_cfg_table;
	void *bstamld_table;
	void *arr;

	if (hwaddr_is_zero(dev->bstamld.mld_macaddr))
		return;

	/* Device.WiFi.DataElements.Network.Device.{i}.bSTAMLD. */
	bstamld_table = blobmsg_open_table(bb, "bSTAMLD");
	blobmsg_add_macaddr(bb, "MLDMACAddress", dev->bstamld.mld_macaddr);
	blobmsg_add_macaddr(bb, "BSSID", dev->bstamld.bssid);
	//blobmsg_add_u32(bb, "bSTAMLDNumberOfEntries", dev->bstamld.num_aff_bsta);
	arr = blobmsg_open_array(bb, "AffiliatedbSTAList");
	if (dev->bstamld.num_aff_bsta) {
		list_for_each_entry(sta, &dev->bstamld.affbstalist, list) {
			blobmsg_add_macaddr(bb, "", sta->macaddr);
		}
	}
	blobmsg_close_array(bb, arr);

	/* Device.WiFi.DataElements.Network.Device.{i}.bSTAMLD.bSTAMLDConfig. */
	bstamld_cfg_table = blobmsg_open_table(bb, "bSTAMLDConfig");
	blobmsg_add_u8(bb, "EMLMREnabled", dev->bstamld.emlmr_enabled);
	blobmsg_add_u8(bb, "EMLSREnabled", dev->bstamld.emlsr_enabled);
	blobmsg_add_u8(bb, "STREnabled", dev->bstamld.str_enabled);
	blobmsg_add_u8(bb, "NSTREnabled", dev->bstamld.nstr_enabled);
	blobmsg_add_u8(bb, "TIDLinkMapNegotiation", 0 /* TODO: dev->bstamld.ttlm_enabled */);
	blobmsg_close_table(bb, bstamld_cfg_table);

	blobmsg_close_table(bb, bstamld_table);
}

static void dump2_network_device(struct decollector_private *p, struct wifi_network *net,
				 struct wifi_network_device *dev, struct blob_buf *bb)
{
	struct mac_address_element *macaddr = NULL;
	struct wifi_radio_element *radio = NULL;
	struct wifi_apmld_element *apmld = NULL;
	struct wifi_bh_down *bh_down = NULL;
	char ap_caps_encoded[8] = { 0 };
	char hexbin_str[2 * 64 + 1] = { 0 };
	void *arr, *device_table;
	int i;
	const struct wifi_network_device_backhaul *map_dev_backhaul =
		&dev->multi_ap_device.backhaul;

	/* Device.WiFi.DataElements.Network.Device.{i}. */
	device_table = blobmsg_open_table(bb, "");

	blobmsg_add_macaddr(bb, "ID", dev->macaddr);

	blobmsg_add_string(bb, "MultiAPCapabilities",
			   to_base64_str(&dev->multiap_caps, sizeof(dev->multiap_caps),
					 ap_caps_encoded, sizeof(ap_caps_encoded)));

	blobmsg_add_u32(bb, "CollectionInterval", p->opts.refresh_int);

	blobmsg_add_u8(bb, "ReportUnsuccessfulAssociations", dev->report.sta_assocfails);
	blobmsg_add_u32(bb, "MaxReportingRate", dev->report.sta_assocfails_rate);
	blobmsg_add_u32(bb, "APMetricsReportingInterval", dev->report.ap_metrics_int);

	blobmsg_add_string(bb, "Manufacturer", dev->manufacturer);
	blobmsg_add_string(bb, "SerialNumber", dev->serial);
	blobmsg_add_string(bb, "ManufacturerModel", dev->model);
	blobmsg_add_string(bb, "SoftwareVersion", dev->swversion);
	blobmsg_add_string(bb, "ExecutionEnv", dev->execenv);

	// hexBinary(64)
	for (i = 0; i < ARRAY_SIZE(dev->dscp_mapping); i++)
		snprintf(hexbin_str + i * 2, 2 + 1, "%02X", dev->dscp_mapping[i]);

	blobmsg_add_string(bb, "DSCPMap", hexbin_str);

	blobmsg_add_u32(bb, "MaxPrioritizationRules", dev->max_prules);
	blobmsg_add_u8(bb, "PrioritizationSupport", dev->support_sp);
	blobmsg_add_u32(bb, "MaxVIDs", dev->max_vids);

	blobmsg_add_string(bb, "CountryCode", dev->country_code[0] == 0 ?
			   "XX" : dev->country_code);

	arr = blobmsg_open_array(bb, "LocalSteeringDisallowedSTAList");
	list_for_each_entry(macaddr, &dev->sta_steer_disallowlist, list) {
		blobmsg_add_macaddr(bb, "", macaddr->macaddr);
	}
	blobmsg_close_array(bb, arr);

	arr = blobmsg_open_array(bb, "BTMSteeringDisallowedSTAList");
	list_for_each_entry(macaddr, &dev->sta_btmsteer_disallowlist, list) {
		blobmsg_add_macaddr(bb, "", macaddr->macaddr);
	}
	blobmsg_close_array(bb, arr);

	blobmsg_add_u8(bb, "DFSEnable", dev->dfs_enabled);

	blobmsg_add_u8(bb, "ReportIndependentScans",  dev->report.independent_scans);

	blobmsg_add_u8(bb, "STASteeringState", dev->sta_steering_state);
	blobmsg_add_u8(bb, "CoordinatedCACAllowed", dev->coordinated_cac_allowed);
	blobmsg_add_u8(bb, "TrafficSeparationAllowed", dev->ts_allowed);
	blobmsg_add_u8(bb, "ServicePrioritizationAllowed", dev->sp_allowed);

	blobmsg_add_u32(bb, "BackhaulPHYRate", map_dev_backhaul->stats.phyrate);
	blobmsg_add_macaddr(bb, "BackhaulMACAddress", map_dev_backhaul->upstream_bbss_macaddr);
	blobmsg_add_macaddr(bb, "BackhaulALID", map_dev_backhaul->upstream_device_macaddr);

	arr = blobmsg_open_array(bb, "BackhaulDownMACAddress");
	list_for_each_entry(bh_down, &dev->bh_downlist, list) {
		blobmsg_add_macaddr(bb, "", bh_down->bss_macaddr);
	}
	blobmsg_close_array(bb, arr);

	blobmsg_add_string(bb, "BackhaulMediaType", i1905_media_type_to_str(map_dev_backhaul->media_type));
	blobmsg_add_u8(bb, "TrafficSeparationCapability", dev->support_ts);
	blobmsg_add_u8(bb, "EasyConnectCapability", dev->support_dpp);

	blobmsg_add_u32(bb, "RadioNumberOfEntries", dev->num_radios);

	// TODO: see dump2_network_device_default8021q
	blobmsg_add_u32(bb, "Default8021QNumberOfEntries",  net->primary_vid ? 1 : 0); // TODO unsignedInt

	// TODO: see dump2_network_device_ssidtovidmapping
	blobmsg_add_u32(bb, "SSIDtoVIDMappingNumberOfEntries", list_count(&net->ssidlist));

	blobmsg_add_u32(bb, "CACStatusNumberOfEntries", list_count(&dev->cac_statuslist));

	blobmsg_add_u32(bb, "IEEE1905SecurityNumberOfEntries", dev->i1905_seccap.caps_valid ? 1 : 0);
	blobmsg_add_u32(bb, "SPRuleNumberOfEntries", dev->num_sprules);
	blobmsg_add_u32(bb, "AnticipatedChannelsNumberOfEntries", 0); // TODO unsignedInt
	blobmsg_add_u32(bb, "AnticipatedChannelUsageNumberOfEntries", dev->num_anticipated_channel_usage);
	blobmsg_add_u32(bb, "MaxNumMLDs", dev->max_mlds);
	blobmsg_add_u32(bb, "APMLDMaxLinks", dev->max_apmld_links);
	blobmsg_add_u32(bb, "bSTAMLDMaxLinks", dev->max_bstamld_links);
	blobmsg_add_u32(bb, "TIDLinkMapCapability", dev->ttlm_cap);
	blobmsg_add_u32(bb, "APMLDNumberOfEntries", dev->num_apmld);
	blobmsg_add_u32(bb, "BackhaulDownNumberOfEntries", dev->num_bh_down);

	dump2_network_device_default8021q(p, net, dev, bb);
	dump2_network_device_ssidtovidmapping(p, net, dev, bb);
	dump2_network_device_cacstatus(p, net, dev, bb);
	dump2_network_device_sprule(p, net, dev, bb);
	dump2_network_device_ieee1905security(p, net, dev, bb);
	dump2_network_device_anticipatedchannels(p, net, dev, bb);
	dump2_network_device_anticipatedchannelsusage(p, net, dev, bb);
	dump2_network_device_backhauldown(p, net, dev, bb);
	dump2_network_device_multiapdevice(p, net, dev, bb);

	arr = blobmsg_open_array(bb, "RadioList");
	list_for_each_entry(radio, &dev->radiolist, list) {
		dump2_network_device_radio(p, net, dev, radio, bb);
	}
	blobmsg_close_array(bb, arr);

	arr = blobmsg_open_array(bb, "APMLDList");
	list_for_each_entry(apmld, &dev->apmldlist, list) {
		dump2_network_device_apmld(p, apmld, bb);
	}
	blobmsg_close_array(bb, arr);

	if (!hwaddr_is_zero(dev->bstamld.mld_macaddr))
		dump2_network_device_bstamld(p, dev, bb);

	blobmsg_close_table(bb, device_table);
}

static void dump2_network_ssid(struct blob_buf *bb, struct wifi_network_ssid *ssid)
{
	void *t;

	/* Only show non-affiliated SSIDs in this list */
	if (ssid->mld_id != 255)
		return;

	/* Device.WiFi.DataElements.Network.SSID.{i}. */
	t = blobmsg_open_table(bb, "");

	blobmsg_add_u8(bb, "Enabled", ssid->enabled ? true : false);
	blobmsg_add_string(bb, "SSID", ssid->ssid);
	blobmsg_add_string(bb, "Band", wifi_band_to_str(ssid->band));
	blobmsg_add_string(bb, "HaulType", wifi_bsstype_to_str(ssid->type));
	blobmsg_add_u32(bb, "MLDUnit", (int8_t)ssid->mld_id);

	blobmsg_close_table(bb, t);
}

static void dump2_network_apmld(struct blob_buf *bb, struct wifi_network_mld *mld)
{
	char bands[32] = {0};
	void *t;

	t = blobmsg_open_table(bb, "");
	blobmsg_add_u8(bb, "Enabled", mld->enabled ? true : false);
	blobmsg_add_string(bb, "SSID", mld->ssid);
	blobmsg_add_string(bb, "Band", wifi_bands_to_str(mld->band, bands));
	blobmsg_add_string(bb, "HaulType", wifi_bsstype_to_str(mld->type));
	blobmsg_add_u32(bb, "MLDUnit", (int8_t)mld->id);
	blobmsg_close_table(bb, t);
}

static void dump2_network_multi_ap_steering_summary_stats(
	struct blob_buf *bb, const struct wifi_steer_summary *steer_stats)
{
	void *t;
	/* Device.WiFi.DataElements.Network.MultiAPSteeringSummaryStats. */

	t = blobmsg_open_table(bb, "MultiAPSteeringSummaryStats");

	blobmsg_add_u64(bb, "NoCandidateAPFailures", steer_stats->no_candidate_cnt);
	blobmsg_add_u64(bb, "BlacklistAttempts", steer_stats->blacklist_attempt_cnt);
	blobmsg_add_u64(bb, "BlacklistSuccesses", steer_stats->blacklist_success_cnt);
	blobmsg_add_u64(bb, "BlacklistFailures", steer_stats->blacklist_failure_cnt);
	blobmsg_add_u64(bb, "BTMAttempts", steer_stats->btm_attempt_cnt);
	blobmsg_add_u64(bb, "BTMSuccesses", steer_stats->btm_success_cnt);
	blobmsg_add_u64(bb, "BTMFailures", steer_stats->btm_failure_cnt);
	blobmsg_add_u64(bb, "BTMQueryResponses", steer_stats->btm_query_resp_cnt);

	blobmsg_close_table(bb, t); // MultiAPSteeringSummaryStats
}

static void dump2_network_stablock(struct blob_buf *bb,
				   const struct wifi_sta_block *blk)
{
	char macstr[18] = { 0 };
	void *t, *arr;
	int i;

	t = blobmsg_open_table(bb, "");

	hwaddr_ntoa(blk->macaddr, macstr);
	blobmsg_add_string(bb, "BlockedSTA", macstr);

	arr = blobmsg_open_array(bb, "BSSID");
	for (i = 0; i < blk->num_bssid; i++) {
		char bssid_str[18] = { 0 };

		hwaddr_ntoa(&blk->bssid[i * 6], bssid_str);
		blobmsg_add_string(bb, NULL, bssid_str);
	}
	blobmsg_close_array(bb, arr);

	blobmsg_add_u32(bb, "ScheduleNumberOfEntries", blk->num_schedule);

	arr = blobmsg_open_array(bb, "Schedule");
	for (i = 0; i < blk->num_schedule; i++) {
		void *st = blobmsg_open_table(bb, "");

		blobmsg_add_string(bb, "StartTime", blk->schedule[i].start_time);
		blobmsg_add_u32(bb, "Duration", blk->schedule[i].duration);

		blobmsg_close_table(bb, st);
	}
	blobmsg_close_array(bb, arr);

	blobmsg_close_table(bb, t);
}

static void dump2_network_provisioned_dpp(struct blob_buf *bb,
					  const struct wifi_provisioned_dpp *dpp)
{
	void *t = blobmsg_open_table(bb, "");

	blobmsg_add_string(bb, "Alias", dpp->alias);
	blobmsg_add_string(bb, "DPPURI", dpp->uri);

	blobmsg_close_table(bb, t);
}

static void dump2_network(struct decollector_private *p, struct wifi_network *net,
			  struct blob_buf *bb)
{
	void *ssid_arr, *device_arr, *blk_arr, *dpp_arr;
	struct wifi_provisioned_dpp *dpp = NULL;
	struct wifi_network_device *dev = NULL;
	struct wifi_network_ssid *ssid = NULL;
	struct wifi_network_mld *mld = NULL;
	struct wifi_sta_block *blk = NULL;
	char coagent_id[18] = { 0 };
	char network_id[37] = { 0 };
	char timestamp[32] = { 0 };
	char cntlr_id[18] = { 0 };

	/* Device.WiFi.DataElements.Network. */

	uuid_btostr((uint8_t *)net->id, network_id);
	blobmsg_add_string(bb, "ID", network_id);
	blobmsg_add_string(bb, "TimeStamp", get_timestamp(NULL, timestamp, sizeof(timestamp)));
	hwaddr_ntoa(net->cntlr_id, cntlr_id);
	blobmsg_add_string(bb, "ControllerID", cntlr_id);

	// TODO:   Multi-AP Policy Config Request / QoS Management Policy TLV
	blobmsg_add_string(bb, "MSCSDisallowedStaList", "" /* TODO: comma_separated_mac_list */);
	blobmsg_add_string(bb, "SCSDisallowedStaList", "" /* TODO: comma_separated_mac_list */);

	hwaddr_ntoa(net->coagent_id, coagent_id);
	blobmsg_add_string(bb, "ColocatedAgentID", coagent_id);

	blobmsg_add_u32(bb, "DeviceNumberOfEntries", count_available_nodes(net));
	blobmsg_add_u32(bb, "SSIDNumberOfEntries", net->num_ssid);
	blobmsg_add_u32(bb, "STABlockNumberOfEntries", net->num_sta_block);
	blobmsg_add_u32(bb, "ProvisionedDPPNumberOfEntries", net->num_dpp);
	//blobmsg_add_u32(bb, "PreferredBackhaulsNumberOfEntries", net->num_pref_bhs);

	ssid_arr = blobmsg_open_array(bb, "SSID");
	list_for_each_entry(ssid, &net->ssidlist, list) {
		dump2_network_ssid(bb, ssid);
	}
	list_for_each_entry(mld, &net->mldlist, list) {
		dump2_network_apmld(bb, mld);
	}
	blobmsg_close_array(bb, ssid_arr);

	dump2_network_multi_ap_steering_summary_stats(bb, &net->steer_summary);

	blk_arr = blobmsg_open_array(bb, "STABlock");
	list_for_each_entry(blk, &net->sta_blocklist, list) {
		dump2_network_stablock(bb, blk);
	}
	blobmsg_close_array(bb, blk_arr);

	dpp_arr = blobmsg_open_array(bb, "ProvisionedDPP");
	list_for_each_entry(dpp, &net->dpplist, list) {
		dump2_network_provisioned_dpp(bb, dpp);
	}
	blobmsg_close_array(bb, dpp_arr);

	device_arr = blobmsg_open_array(bb, "DeviceList");
	list_for_each_entry(dev, &net->devicelist, list) {
		if (!is_network_device_agent(dev))
			continue;
#ifdef SKIP_UNRESPONSIVE_NODE
		if (!is_network_device_alive(dev))
			continue;
#endif
		dump2_network_device(p, net, dev, bb);
	}
	blobmsg_close_array(bb, device_arr);
}

int decollector_dump2_all(struct decollector_private *p, struct blob_buf *bb)
{
	struct wifi_data_element *dm = p->dm;
	char dstr[32] = {0};
	char tsp[32] = {0};
	void *t, *t2;
	void *arr;

	blobmsg_add_string(bb, "date", get_date(NULL, dstr));
	blobmsg_add_string(bb, "version", WIFI_DATAELEMENTS_VER_4_0);
	blobmsg_add_string(bb, "description", "WFA Data Elements");
	blobmsg_add_string(bb, "TimeStamp", get_timestamp(NULL, tsp, sizeof(tsp)));
	blobmsg_add_string(bb, "name", "wifi");

	arr = blobmsg_open_array(bb, "data");
	t = blobmsg_open_table(bb, "");
	t2 = blobmsg_open_table(bb, "wfa-dataelements:Network");

	dump2_network(p, &dm->network, bb);

	blobmsg_close_table(bb, t2);
	blobmsg_close_table(bb, t);
	blobmsg_close_array(bb, arr);

	return 0;
}
