/*
 * wifi_ubus.c - wifimngr ubus interface
 *
 * Copyright (C) 2022 IOPSYS Software Solutions AB. All rights reserved.
 *
 */

#include "wifi_ubus.h"

#include <easy/utils.h>
#include <easymesh.h>
#include <libubox/blob.h>
#include <libubox/blobmsg.h>
#include <libubus.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ubusmsg.h>

#include "agent.h"
#include "config.h"
#include "utils/debug.h"
#include "utils/utils.h"
#include "wifi.h"
#include "wifi_opclass.h"

int wifi_ubus_scan(struct ubus_context *ubus_ctx, const char *radio,
		   struct scan_param_ex *param,
		   int num_opclass, uint8_t *opclass,
		   int num_channel, uint8_t *channel)
{
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s num_ssid %d num_freq %d num_opclass %d num_channel %d\n",
		  radio, __func__,
		  param->num_ssid, param->num_freq,
		  num_opclass, num_channel);

	for (id = 0; id < num_opclass; id++)
		trace("\topclass: %d\n", opclass[id]);

	/* Get id from radio name */
	snprintf(name, sizeof(name), "wifi.radio.%s", radio);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);

	/* TODO add/check param->bssid */

	/* array of ssids */
	if (param->num_ssid) {
		void *a;
		int i;

		a = blobmsg_open_array(&bb, "ssid");
		for (i = 0; i < param->num_ssid; i++) {
			if (strlen(param->ssid[i])) {
				trace("[%s] %s add ssid %s\n", radio, __func__,
					  param->ssid[i]);
				blobmsg_add_string(&bb, "", param->ssid[i]);
			}
		}
		blobmsg_close_array(&bb, a);
	}

	/* array of opclasses */
	if (num_opclass) {
		void *a;
		int i;

		a = blobmsg_open_array(&bb, "opclass");
		for (i = 0; i < num_opclass; i++)
			blobmsg_add_u32(&bb, NULL, opclass[i]);
		blobmsg_close_array(&bb, a);
	}

	/* array of channels */
	if (num_channel) {
		void *a;
		int i;

		a = blobmsg_open_array(&bb, "channel");
		for (i = 0; i < num_channel; i++)
			blobmsg_add_u32(&bb, NULL, channel[i]);
		blobmsg_close_array(&bb, a);
	}

	ret = ubus_invoke(ubus_ctx, id, "scan_ex", bb.head, NULL, NULL, 3 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", radio, __func__, ret);
	return ret;
}

int wifi_ubus_ap_set_state(struct ubus_context *ubus_ctx, const char *ifname, bool up)
{
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s %s\n", ifname, __func__, up ? "up" : "down");

	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
        ret = ubus_lookup_id(ubus_ctx, name, &id);
        if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	ret = ubus_invoke(ubus_ctx, id, up ? "up" : "down",
			  bb.head, NULL, NULL, 2 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

int wifi_ubus_start_cac(struct ubus_context *ubus_ctx, const char *radio, int channel,
			enum wifi_bw bw, enum wifi_cac_method method)
{
	struct blob_buf bb = {};
	int bandwidth;
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s %d/%d method %d\n", radio, __func__,
	      channel, bw, method);

	switch (bw) {
	case BW20:
		bandwidth = 20;
		break;
	case BW40:
		bandwidth = 40;
		break;
	case BW80:
		bandwidth = 80;
		break;
	case BW160:
		bandwidth = 160;
		break;
	default:
		bandwidth = 20;
		break;
	}

	/* Get id from radio name */
	snprintf(name, sizeof(name), "wifi.radio.%s", radio);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	/* Setup required params */
	blob_buf_init(&bb, 0);
	blobmsg_add_u32(&bb, "channel", channel);
	blobmsg_add_u32(&bb, "bandwidth", bandwidth);
	blobmsg_add_u32(&bb, "method", method);

	ret = ubus_invoke(ubus_ctx, id, "start_cac", bb.head, NULL, NULL, 3 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", radio, __func__, ret);
	return ret;
}

int wifi_ubus_stop_cac(struct ubus_context *ubus_ctx, const char *radio,
		       int channel, enum wifi_bw bw)
{
	struct blob_buf bb = {};
	int bandwidth;
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s\n", radio, __func__);

	/* Get id from radio name */
	snprintf(name, sizeof(name), "wifi.radio.%s", radio);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);

	if (channel) {
		blobmsg_add_u32(&bb, "channel", channel);

		switch (bw) {
		case BW20:
			bandwidth = 20;
			break;
		case BW40:
			bandwidth = 40;
			break;
		case BW80:
			bandwidth = 80;
			break;
		case BW160:
			bandwidth = 160;
			break;
		default:
			bandwidth = 20;
			break;
		}

		blobmsg_add_u32(&bb, "bandwidth", bandwidth);
	}

	ret = ubus_invoke(ubus_ctx, id, "stop_cac", bb.head, NULL, NULL, 3 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", radio, __func__, ret);
	return ret;
}

int wifi_ubus_del_neighbor(struct ubus_context *ubus_ctx, const char *ifname, uint8_t *bssid)
{
	struct blob_buf bb = {};
	char bssidstr[18] = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	hwaddr_ntoa(bssid, bssidstr);
	trace("[%s] %s %s\n", ifname, __func__, bssidstr);

	/* Get id from ap name */
	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	blobmsg_add_string(&bb, "bssid", bssidstr);
	ret = ubus_invoke(ubus_ctx, id, "del_neighbor", bb.head,
			  NULL, NULL, 2 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

int wifi_ubus_add_neighbor(struct ubus_context *ubus_ctx, const char *ifname,
			   struct nbr *nbr)
{
	struct blob_buf bb = {};
	char bssidstr[18] = {};
	char bssid_infostr[12] = {};
	char name[256] = {};
	uint32_t id;
	int ret = 0;

	hwaddr_ntoa(nbr->bssid, bssidstr);
	snprintf(bssid_infostr, sizeof(bssid_infostr), "%u", nbr->bssid_info);

	trace("[%s]: %s %s channel %d bssidinfo %s\n",
	      ifname, __func__, bssidstr, nbr->channel, bssid_infostr);

	/* Get id from ap name */
	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	blobmsg_add_string(&bb, "bssid", bssidstr);
	blobmsg_add_u32(&bb, "channel", nbr->channel);
	blobmsg_add_string(&bb, "bssid_info", bssid_infostr);
	blobmsg_add_u32(&bb, "reg", nbr->reg);
	blobmsg_add_u32(&bb, "phy", nbr->phy);
	ret = ubus_invoke(ubus_ctx, id, "add_neighbor", bb.head,
			  NULL, NULL, 2 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

struct list_neighbor_ctx {
	struct nbr *nbr;
	int num;
	int max;
	int status;
};

static void wifi_ubus_list_neighbor_cb(struct ubus_request *req,
				       int type,
				       struct blob_attr *msg)
{
	struct list_neighbor_ctx *ctx = req->priv;
        static const struct blobmsg_policy list_neighbor_policy[] = {
                [0] = { .name = "neighbors", .type = BLOBMSG_TYPE_ARRAY },
	};
	struct blob_attr *tb[ARRAY_SIZE(list_neighbor_policy)];
	struct blob_attr *cur;
	struct nbr *nbr;
	int num, rem;

	ctx->num = 0;
	num = 0;

	blobmsg_parse(list_neighbor_policy, ARRAY_SIZE(list_neighbor_policy),
		      tb, blob_data(msg), blob_len(msg));

	if (!tb[0]) {
		ctx->status = -1;
		return;
	}

	blobmsg_for_each_attr(cur, tb[0], rem) {
		static const struct blobmsg_policy neighbor_policy[] = {
			[0] = { .name = "bssid", .type = BLOBMSG_TYPE_STRING },
			[1] = { .name = "bss_info", .type = BLOBMSG_TYPE_INT32 },
			[2] = { .name = "regulatory", .type = BLOBMSG_TYPE_INT32 },
			[3] = { .name = "channel", .type = BLOBMSG_TYPE_INT32 },
			[4] = { .name = "phy", .type = BLOBMSG_TYPE_INT32 },
		};
		struct blob_attr *neighbor_tb[ARRAY_SIZE(neighbor_policy)];

		if (WARN_ON(num >= ctx->max))
			break;

		blobmsg_parse(neighbor_policy, ARRAY_SIZE(neighbor_policy), neighbor_tb,
			      blobmsg_data(cur), blobmsg_data_len(cur));

		if (!neighbor_tb[0] || !neighbor_tb[1] || !neighbor_tb[2] || !neighbor_tb[3] || !neighbor_tb[4])
			continue;

		nbr = &ctx->nbr[num];

		hwaddr_aton(blobmsg_get_string(neighbor_tb[0]), nbr->bssid);
		nbr->bssid_info = blobmsg_get_u32(neighbor_tb[1]);
		nbr->reg = blobmsg_get_u32(neighbor_tb[2]);
		nbr->channel = blobmsg_get_u32(neighbor_tb[3]);
		nbr->phy = blobmsg_get_u32(neighbor_tb[4]);

		num++;
	}

	ctx->status = 0;
	ctx->num = num;
}

int wifi_ubus_list_neighbor(struct ubus_context *ubus_ctx, const char *ifname,
			    struct nbr *nbr, int *nbr_num)
{
	struct blob_buf bb = {};
	struct list_neighbor_ctx ctx = {
		.nbr = nbr,
		.max = *nbr_num,
		.num = 0,
		.status = -1,
	};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s]: %s\n", ifname, __func__);

	/* Get id from ap name */
	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	ret = ubus_invoke(ubus_ctx, id, "list_neighbor", bb.head,
			  wifi_ubus_list_neighbor_cb, &ctx, 2 * 1000);
	blob_buf_free(&bb);

	if (ctx.status)
		ret = ctx.status;

	*nbr_num = ctx.num;
out:
	trace("[%s]: %s ret %d num %d\n", ifname, __func__, ret, *nbr_num);
	return ret;
}

int wifi_ubus_monitor_add_del(struct ubus_context *ubus_ctx, const char *ifname,
			      uint8_t *macaddr, bool add)
{
	struct blob_buf bb = {};
	char macaddrstr[18] = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	hwaddr_ntoa(macaddr, macaddrstr);
	trace("[%s] %s %s %s\n", ifname, __func__, macaddrstr, add ? "add" : "del");

	/* Get id from ap name */
	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	blobmsg_add_string(&bb, "sta", macaddrstr);
	ret = ubus_invoke(ubus_ctx, id, add ? "monitor_add" : "monitor_del",
			  bb.head, NULL, NULL, 2 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

int wifi_ubus_monitor_add(struct ubus_context *ubus_ctx, const char *ifname, uint8_t *macaddr)
{
	return wifi_ubus_monitor_add_del(ubus_ctx, ifname, macaddr, true);
}

int wifi_ubus_monitor_del(struct ubus_context *ubus_ctx, const char *ifname, uint8_t *macaddr)
{
	return wifi_ubus_monitor_add_del(ubus_ctx, ifname, macaddr, false);
}

struct monitor_get_ctx {
	struct wifi_monsta *monsta;
	int status;
};

static void wifi_ubus_monitor_get_cb(struct ubus_request *req,
				     int type,
				     struct blob_attr *msg)
{
	struct monitor_get_ctx *ctx = req->priv;
        static const struct blobmsg_policy monitor_get_policy[] = {
                [0] = { .name = "sta", .type = BLOBMSG_TYPE_TABLE },
	};
	struct blob_attr *tb[ARRAY_SIZE(monitor_get_policy)];
	static const struct blobmsg_policy attr_policy[] = {
		[0] = { .name = "macaddr", .type = BLOBMSG_TYPE_STRING },
		[1] = { .name = "seen", .type = BLOBMSG_TYPE_INT32 },
		[2] = { .name = "rssi_avg", .type = BLOBMSG_TYPE_INT32 },
		[3] = { .name = "rssi", .type = BLOBMSG_TYPE_ARRAY },
	};
	struct blob_attr *attr_data[ARRAY_SIZE(attr_policy)];
	static const struct blobmsg_policy rssi_policy[] = {
		{ .type = BLOBMSG_TYPE_INT32 },
		{ .type = BLOBMSG_TYPE_INT32 },
		{ .type = BLOBMSG_TYPE_INT32 },
		{ .type = BLOBMSG_TYPE_INT32 },
	};
	struct blob_attr *rssi[ARRAY_SIZE(rssi_policy)];
	struct wifi_monsta *monsta;

	blobmsg_parse(monitor_get_policy, ARRAY_SIZE(monitor_get_policy),
		      tb, blob_data(msg), blob_len(msg));

	if (!tb[0]) {
		ctx->status = -1;
		return;
	}

	blobmsg_parse(attr_policy, ARRAY_SIZE(attr_policy),
		      attr_data, blobmsg_data(tb[0]), blobmsg_data_len(tb[0]));

	if (!attr_data[0] || !attr_data[1] || !attr_data[2] || !attr_data[3]) {
		ctx->status = -1;
		return;
	}

	monsta = ctx->monsta;

	hwaddr_aton(blobmsg_get_string(attr_data[0]), monsta->macaddr);
	monsta->last_seen = blobmsg_get_u32(attr_data[1]);
	monsta->rssi_avg = blobmsg_get_u32(attr_data[2]);

	blobmsg_parse_array(rssi_policy, ARRAY_SIZE(rssi_policy), rssi,
			    blobmsg_data(attr_data[3]), blobmsg_data_len(attr_data[3]));

	if (!rssi[0] || !rssi[1] || !rssi[2] || !rssi[3]) {
		ctx->status = -1;
		return;
	}

	monsta->rssi[0] = blobmsg_get_u32(rssi[0]);
	monsta->rssi[1] = blobmsg_get_u32(rssi[1]);
	monsta->rssi[2] = blobmsg_get_u32(rssi[2]);
	monsta->rssi[3] = blobmsg_get_u32(rssi[3]);

	ctx->status = 0;
}

int wifi_ubus_monitor_get(struct ubus_context *ubus_ctx, const char *ifname,
			  uint8_t *macaddr, struct wifi_monsta *monsta)
{
	struct blob_buf bb = {};
	struct monitor_get_ctx ctx = {
		.monsta = monsta,
		.status = -1,
	};
	char macaddrstr[18] = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	hwaddr_ntoa(macaddr, macaddrstr);
	trace("[%s] %s " MACFMT "\n", ifname, __func__, MAC2STR(macaddr));

	/* Get id from ap name */
	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	blobmsg_add_string(&bb, "sta", macaddrstr);
	ret = ubus_invoke(ubus_ctx, id, "monitor_get", bb.head,
			  wifi_ubus_monitor_get_cb, &ctx, 2 * 1000);
	blob_buf_free(&bb);

	if (ctx.status)
		ret = ctx.status;

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;

}

/* opclass preferences context */
struct opclass_preferences_ctx
{
	const char *radio;
	struct wifi_radio_opclass *opclass;
};

static uint8_t opclass_preferences_recalc_score(uint32_t score)
{
	uint8_t pref;

	/* Unknown/not supported */
	if (score == 255)
		pref = 14;
	else if (score > 100)
		pref = 14;
	else
		pref = (score * 14) / 100;

	/* preference = 0 - only when not supported by HW */
	if (!pref && score)
		pref = 1;

	return pref;
}

static enum wifi_radio_opclass_dfs opclass_preferences_get_dfs(const char *dfs_state)
{
	if (!dfs_state)
		return WIFI_RADIO_OPCLASS_CHANNEL_DFS_NONE;

	if (!strcmp(dfs_state, "usable"))
		return WIFI_RADIO_OPCLASS_CHANNEL_DFS_USABLE;
	else if (!strcmp(dfs_state, "available"))
		return WIFI_RADIO_OPCLASS_CHANNEL_DFS_AVAILABLE;
	else if (!strcmp(dfs_state, "unavailable"))
		return WIFI_RADIO_OPCLASS_CHANNEL_DFS_NOP;
	else if (!strcmp(dfs_state, "cac"))
		return WIFI_RADIO_OPCLASS_CHANNEL_DFS_CAC;

	return WIFI_RADIO_OPCLASS_CHANNEL_DFS_NONE;
}

static uint8_t opclass_preferences_get_reason(enum wifi_radio_opclass_dfs dfs)
{
	switch (dfs) {
		case (WIFI_RADIO_OPCLASS_CHANNEL_DFS_NONE):
			return CHANNEL_PREF_REASON_UNSPEC;
		case (WIFI_RADIO_OPCLASS_CHANNEL_DFS_USABLE):
			return CHANNEL_PREF_REASON_DFS_USABLE;
		case (WIFI_RADIO_OPCLASS_CHANNEL_DFS_AVAILABLE):
			return CHANNEL_PREF_REASON_DFS_AVAILABLE;
		case (WIFI_RADIO_OPCLASS_CHANNEL_DFS_NOP):
			return CHANNEL_PREF_REASON_DFS_NOP;
		case (WIFI_RADIO_OPCLASS_CHANNEL_DFS_CAC):
			return CHANNEL_PREF_REASON_DFS_USABLE;
		default:
			break;
	}

	return CHANNEL_PREF_REASON_UNSPEC;
}

static void wifi_ubus_opclass_preferences_cb(struct ubus_request *req,
					     int type,
					     struct blob_attr *msg)
{
	struct opclass_preferences_ctx *ctx = req->priv;
        static const struct blobmsg_policy pref_opclass_policy[] = {
                [0] = { .name = "pref_opclass", .type = BLOBMSG_TYPE_ARRAY },
	};
	struct blob_attr *tb[ARRAY_SIZE(pref_opclass_policy)];
	struct blob_attr *pref_opclass;
	struct blob_attr *cur;
	struct wifi_radio_opclass *opclass;
	int rem;
	int k;

	trace("[%s] %s\n", ctx->radio, __func__);

	/* Prepare result buffer */
	opclass = ctx->opclass;
	wifi_opclass_reset(opclass);

	blobmsg_parse(pref_opclass_policy, ARRAY_SIZE(pref_opclass_policy), tb, blob_data(msg), blob_len(msg));
	pref_opclass = tb[0];
	if (!pref_opclass)
		return;

	blobmsg_for_each_attr(cur, pref_opclass, rem) {
		static const struct blobmsg_policy attr_policy[] = {
			[0] = { .name = "opclass", .type = BLOBMSG_TYPE_INT32 },
			[1] = { .name = "bandwidth", .type = BLOBMSG_TYPE_INT32 },
			[2] = { .name = "txpower", .type = BLOBMSG_TYPE_INT32 },
			[3] = { .name = "channels", .type = BLOBMSG_TYPE_ARRAY },
		};
		struct blob_attr *attr_data[ARRAY_SIZE(attr_policy)];
		struct blob_attr *cur_chan;
		struct blob_attr *ctrl_chan;
		struct wifi_radio_opclass_entry entry = {};
		int rem_ctrl_chan;
		int rem_chan;

		if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE)
			continue;

		blobmsg_parse(attr_policy, ARRAY_SIZE(attr_policy),
			      attr_data, blobmsg_data(cur), blobmsg_data_len(cur));

		if (!attr_data[0] || !attr_data[1] || !attr_data[2] || !attr_data[3])
			continue;

		trace("[%s] %s %u txpower %u bandwidth %u\n", ctx->radio, __func__, blobmsg_get_u32(attr_data[0]),
		      blobmsg_get_u32(attr_data[2]), blobmsg_get_u32(attr_data[1]));

		entry.id = (uint8_t) blobmsg_get_u32(attr_data[0]);
		entry.bandwidth = blobmsg_get_u32(attr_data[1]);
		entry.max_txpower = (uint8_t) blobmsg_get_u32(attr_data[2]);

		blobmsg_for_each_attr(cur_chan, attr_data[3], rem_chan) {
			static const struct blobmsg_policy chan_policy[] = {
				[0] = { .name = "channel", .type = BLOBMSG_TYPE_INT32 },
				[1] = { .name = "score", .type = BLOBMSG_TYPE_INT32 },
				[2] = { .name = "dfs", .type = BLOBMSG_TYPE_INT32 },
				[3] = { .name = "dfs_state", .type = BLOBMSG_TYPE_STRING },
				[4] = { .name = "cac_time", .type = BLOBMSG_TYPE_INT32 },
				[5] = { .name = "nop_time", .type = BLOBMSG_TYPE_INT32 },
				[6] = { .name = "ctrl_channels", .type = BLOBMSG_TYPE_ARRAY },
			};
			struct blob_attr *chan_data[ARRAY_SIZE(chan_policy)];
			struct wifi_radio_opclass_channel channel = {};
			uint8_t score;

			if (blobmsg_type(cur_chan) != BLOBMSG_TYPE_TABLE)
				continue;

			blobmsg_parse(chan_policy, ARRAY_SIZE(chan_policy),
				      chan_data, blobmsg_data(cur_chan), blobmsg_data_len(cur_chan));

			if (!chan_data[0] ||  !chan_data[1] || ! chan_data[2])
				continue;

			score = opclass_preferences_recalc_score(blobmsg_get_u32(chan_data[1]));
			trace("[%s] %s chan %u score %u (%u) dfs %u dfs_state %s\n",
			      ctx->radio,
			      __func__,
			      chan_data[0] ? blobmsg_get_u32(chan_data[0]) : 0,
			      chan_data[1] ? blobmsg_get_u32(chan_data[1]) : 0,
			      score,
			      chan_data[2] ? blobmsg_get_u32(chan_data[2]) : 0,
			      chan_data[3] ? blobmsg_get_string(chan_data[3]) : "none");

			channel.channel = (uint8_t) blobmsg_get_u32(chan_data[0]);
			channel.preference = score << 4;
			channel.dfs = WIFI_RADIO_OPCLASS_CHANNEL_DFS_NONE;

			if (chan_data[3]) {
				channel.dfs = opclass_preferences_get_dfs(blobmsg_get_string(chan_data[3]));
				channel.preference |= opclass_preferences_get_reason(channel.dfs);
			}
			if (chan_data[4])
				channel.cac_time = blobmsg_get_u32(chan_data[4]);
			if (chan_data[5])
				channel.nop_time = blobmsg_get_u32(chan_data[5]);

			if (chan_data[6]) {
				k = 0;
				blobmsg_for_each_attr(ctrl_chan, chan_data[6], rem_ctrl_chan) {
					if (k >= ARRAY_SIZE(channel.ctrl_channels))
						break;
					trace("channel %d ctrl[%d]: %d\n", blobmsg_get_u32(chan_data[0]), k, blobmsg_get_u32(ctrl_chan));
					channel.ctrl_channels[k] = blobmsg_get_u32(ctrl_chan);
					k++;
				}
			}

			if (WARN_ON(wifi_opclass_add_channel(&entry, &channel)))
				break;
		}

		if (WARN_ON(wifi_opclass_add_entry(opclass, &entry)))
			break;
	}
}

int wifi_ubus_opclass_preferences(struct ubus_context *ubus_ctx, const char *radio,
				  struct wifi_radio_opclass *opclass)
{
	struct opclass_preferences_ctx ctx = {
			.radio = radio,
			.opclass = opclass,
	};
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s\n", radio, __func__);

	/* Get id from radio name */
	snprintf(name, sizeof(name), "wifi.radio.%s", radio);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	ret = ubus_invoke(ubus_ctx, id, "opclass_preferences", bb.head,
			  wifi_ubus_opclass_preferences_cb, &ctx,
			  2 * 1000);

	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", radio, __func__, ret);
	return ret;
}

struct radio_status_ctx {
	const char *radio;
	struct wifi_radio_status *radio_status;
	int status;
};

static void wifi_ubus_radio_status_cb(struct ubus_request *req,
				      int type,
				      struct blob_attr *msg)
{
	struct radio_status_ctx *ctx = req->priv;
        static const struct blobmsg_policy radio_status_policy[] = {
                [0] = { .name = "opclass", .type = BLOBMSG_TYPE_INT32 },
                [1] = { .name = "channel", .type = BLOBMSG_TYPE_INT32 },
                [2] = { .name = "bandwidth", .type = BLOBMSG_TYPE_INT32 },
	};
	struct blob_attr *tb[ARRAY_SIZE(radio_status_policy)];
	struct wifi_radio_status *status = ctx->radio_status;

	blobmsg_parse(radio_status_policy, ARRAY_SIZE(radio_status_policy),
		      tb, blob_data(msg), blob_len(msg));

	if (!tb[0] || !tb[1] || !tb[2]) {
		ctx->status = -1;
		return;
	}

	status->info.channel = blobmsg_get_u32(tb[1]);
	status->info.curr_bw = bw_to_wifi_bw(blobmsg_get_u32(tb[2]));

	status->channel = blobmsg_get_u32(tb[1]);
	status->bandwidth = blobmsg_get_u32(tb[2]);
	status->opclass = blobmsg_get_u32(tb[0]);

	ctx->status = 0;
}

int wifi_ubus_radio_status(struct ubus_context *ubus_ctx, const char *radio,
			   struct wifi_radio_status *status)
{
	struct radio_status_ctx ctx = {
		.radio = radio,
		.radio_status = status,
		.status = -1,
	};
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s\n", radio, __func__);

	/* Get id from radio name */
	snprintf(name, sizeof(name), "wifi.radio.%s", radio);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	ret = ubus_invoke(ubus_ctx, id, "status", bb.head,
			  wifi_ubus_radio_status_cb, &ctx,
			  2 * 1000);
	blob_buf_free(&bb);

	if (ctx.status)
		ret = ctx.status;

out:
	trace("[%s] %s ret %d ctx status %d\n", radio, __func__, ret, ctx.status);
	return ret;
}

int wifi_ubus_disconnect_sta(struct ubus_context *ubus_ctx, const char *ifname,
			     uint8_t *macaddr, uint16_t reason)
{
	struct blob_buf bb = {};
	char name[256] = {};
	char macstr[18] = {};
	uint32_t id;
	int ret;

	snprintf(macstr, sizeof(macstr), MACFMT, MAC2STR(macaddr)); /* Flawfinder: ignore */
	trace("[%s] %s " MACFMT "\n", ifname, __func__, MAC2STR(macaddr));

	/* Get id from ap name */
	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	/* Setup required params */
	blobmsg_add_string(&bb, "sta", macstr);
	blobmsg_add_u32(&bb, "reason", reason);

	ret = ubus_invoke(ubus_ctx, id, "disconnect", bb.head, NULL, NULL, 1 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

int wifi_ubus_restrict_sta(struct ubus_context *ubus_ctx, struct netif_ap *ap,
			   uint8_t *macaddr, bool block_sta)
{
	struct blob_buf bb = {};
	char name[256] = {};
	char macstr[18] = {};
	int bandnum = 0;
	uint32_t id;
	int ret;

	snprintf(macstr, sizeof(macstr), MACFMT, MAC2STR(macaddr)); /* Flawfinder: ignore */

	bandnum = band_to_int(ap->band);

	trace("[%s] sta " MACFMT " %s\n",
		  ap->ifname, MAC2STR(macaddr), block_sta ? "block" : "unblock");

	/* Get id from ap name */
	snprintf(name, sizeof(name), "wifi.ap.%s", ap->ifname);
#if (EASYMESH_VERSION >= 6)
	if (ap->is_affiliated_ap)
		snprintf(name, sizeof(name), "wifi.apmld.%s", ap->mld_ifname);
#endif
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	/* Setup required params */
	blob_buf_init(&bb, 0);
	blobmsg_add_string(&bb, "client", macstr);
	blobmsg_add_u32(&bb, "block", block_sta ? 1 : 0);
	if (bandnum)
		blobmsg_add_u32(&bb, "band", bandnum);

	ret = ubus_invoke(ubus_ctx, id, "block_sta", bb.head, NULL, NULL, 1 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ap->ifname, __func__, ret);
	return ret;
}

struct restricted_stas_ctx {
	uint8_t *stas; /* pointer to an array of 6byte mac addresses */
	int num;
	int max;
	int status;
};

static void wifi_ubus_restricted_stas_cb(struct ubus_request *req,
					 int type,
					 struct blob_attr *msg)
{
	struct restricted_stas_ctx *ctx = req->priv;
		static const struct blobmsg_policy restricted_stas_policy[] = {
				[0] = { .name = "blocked", .type = BLOBMSG_TYPE_ARRAY },
	};
	struct blob_attr *tb[ARRAY_SIZE(restricted_stas_policy)];
	struct blob_attr *cur;
	int num = 0;
	int rem;

	blobmsg_parse(restricted_stas_policy, ARRAY_SIZE(restricted_stas_policy),
		      tb, blob_data(msg), blob_len(msg));

	if (!tb[0]) {
		ctx->status = -1;
		return;
	}

	blobmsg_for_each_attr(cur, tb[0], rem) {
		if (WARN_ON(num >= ctx->max))
			break;

		hwaddr_aton(blobmsg_get_string(cur), &ctx->stas[num * 6]);
		num++;
	}

	ctx->num = num;
	ctx->status = 0;
}

int wifi_ubus_get_restricted_stas(struct ubus_context *ubus_ctx,
				  struct netif_ap *ap, uint8_t *stas, int *num)
{
	struct blob_buf bb = {};
	char name[256] = {};
	char bandnum = 0;
	struct restricted_stas_ctx ctx = {
		.stas = stas,
		.num = 0,
		.max = *num,
	};
	uint32_t id;
	int ret;

	bandnum = band_to_int(ap->band);

	trace("[%s] %s %d\n", ap->ifname, __func__, bandnum);

	/* Get id from ap name */
	snprintf(name, sizeof(name), "wifi.ap.%s", ap->ifname);
#if (EASYMESH_VERSION >= 6)
	if (ap->is_affiliated_ap)
		snprintf(name, sizeof(name), "wifi.apmld.%s", ap->mld_ifname);
#endif
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	if (bandnum)
		blobmsg_add_u32(&bb, "band", bandnum);

	ret = ubus_invoke(ubus_ctx, id, "blocked_stas", bb.head,
			  wifi_ubus_restricted_stas_cb, &ctx, 1 * 1000);

	blob_buf_free(&bb);

	if (ctx.status)
		ret = ctx.status;

	*num = ctx.num;
out:
	trace("[%s] %s ret %d\n", ap->ifname, __func__, ret);
	return ret;
}

int wifi_ubus_req_btm(struct ubus_context *ubus_ctx, const char *ifname, uint8_t *macaddr,
		    uint8_t pref_num, struct nbr *pref, struct wifi_btmreq *req)
{
	struct blob_buf bb = {};
	char name[256] = {};
	char macstr[18] = {};
	uint32_t id;
	int ret;
	void *t;
	int i;

	snprintf(macstr, sizeof(macstr), MACFMT, MAC2STR(macaddr)); /* Flawfinder: ignore */
	trace("[%s] %s %s\n", ifname, __func__, macstr);

	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	/* Client STA MAC addr */
	blobmsg_add_string(&bb, "sta", macstr);
	/* Target params: bssid, bssid_info, reg, channel & phy */
	t = blobmsg_open_array(&bb, "target_ap");

	for (i = 0; i < pref_num; i++) {
		void *tt;

		tt = blobmsg_open_table(&bb, "");

		/* skip if bssid is not provided */
		if (hwaddr_is_zero(pref[i].bssid))
			continue;

		hwaddr_ntoa(pref[i].bssid, macstr);
		blobmsg_add_string(&bb, "bssid", macstr);
		/* optional */
		if (pref[i].bssid_info)
			blobmsg_add_u32(&bb, "bssid_info", pref[i].bssid_info);
		if (pref[i].reg)
			blobmsg_add_u32(&bb, "reg", pref[i].reg);
		if (pref[i].channel)
			blobmsg_add_u32(&bb, "channel", pref[i].channel);
		if (pref[i].phy)
			blobmsg_add_u32(&bb, "phy", pref[i].phy);

		blobmsg_close_table(&bb, tt);
	}
	blobmsg_close_array(&bb, t);

	blobmsg_add_u32(&bb, "mode", req->mode);

	if (req->disassoc_tmo)
		blobmsg_add_u32(&bb, "disassoc_tmo", req->disassoc_tmo);

	if (req->validity_int)
		blobmsg_add_u32(&bb, "validity_int", req->validity_int);

	if (req->bssterm_dur)
		blobmsg_add_u32(&bb, "validity_int", req->bssterm_dur);

	if (req->dialog_token)
		blobmsg_add_u32(&bb, "dialog_token", req->dialog_token);

	ret = ubus_invoke(ubus_ctx, id, "request_btm", bb.head,
			  NULL, NULL, 2 * 1000);
	if (ret)
		warn("[%s] %s request_btm failed!\n", ifname, __func__);

	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

int wifi_ubus_add_vendor_ie(struct ubus_context *ubus_ctx, const char *ifname, int mgmt,
			    char *oui, char *data)
{
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s\n", ifname, __func__);

	/* Get id from ap name */
	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	/* Setup required params */
	blob_buf_init(&bb, 0);
	blobmsg_add_u32(&bb, "mgmt", mgmt);
	blobmsg_add_string(&bb, "oui", oui);
	blobmsg_add_string(&bb, "data", data);

	ret = ubus_invoke(ubus_ctx, id, "add_vendor_ie", bb.head, NULL, NULL, 1 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

int wifi_ubus_del_vendor_ie(struct ubus_context *ubus_ctx, const char *ifname, int mgmt,
			    char *oui, char *data)
{
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s\n", ifname, __func__);

	/* Get id from ap name */
	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	/* Setup required params */
	blob_buf_init(&bb, 0);
	blobmsg_add_u32(&bb, "mgmt", mgmt);
	blobmsg_add_string(&bb, "oui", oui);
	if (data)
		blobmsg_add_string(&bb, "data", data);

	ret = ubus_invoke(ubus_ctx, id, "del_vendor_ie", bb.head, NULL, NULL, 1 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

struct sta_4addr_ctx {
	bool enable;
	int status;
};

static void wifi_ubus_get_4addr_cb(struct ubus_request *req,
				   int type,
				   struct blob_attr *msg)
{
	struct sta_4addr_ctx *ctx = req->priv;
        static const struct blobmsg_policy sta_4addr_policy[] = {
                [0] = { .name = "enable", .type = BLOBMSG_TYPE_BOOL },
	};
	struct blob_attr *tb[ARRAY_SIZE(sta_4addr_policy)];

	blobmsg_parse(sta_4addr_policy, ARRAY_SIZE(sta_4addr_policy),
		      tb, blob_data(msg), blob_len(msg));

	if (!tb[0]) {
		ctx->status = -1;
		return;
	}

	ctx->enable = blobmsg_get_bool(tb[0]);
	ctx->status = 0;
}

int wifi_ubus_get_4addr(struct ubus_context *ubus_ctx, struct netif_bk *bk, bool *enable)
{
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	struct sta_4addr_ctx ctx = {};
	int ret;

	trace("[%s] %s\n", bk->ifname, __func__);

	snprintf(name, sizeof(name), "wifi.bsta.%s", bk->ifname);
#if (EASYMESH_VERSION >= 6)
	if (bk->cfg->is_mld_netdev)
		snprintf(name, sizeof(name), "wifi.bstamld.%s", bk->ifname);
	else if (bk->is_affiliated_sta)
		snprintf(name, sizeof(name), "wifi.bstamld.%s", bk->mld_ifname);
#endif
	/* Get id from backhaul name */
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	ret = ubus_invoke(ubus_ctx, id, "4addr", bb.head,
			  wifi_ubus_get_4addr_cb, &ctx, 1 * 1000);
	blob_buf_free(&bb);

	if (ret || ctx.status)
		goto out;

	*enable = ctx.enable;

out:
	trace("[%s] %s ret %d enable %d\n", bk->ifname, __func__, ret, *enable);
	return ret;
}

int wifi_ubus_set_4addr(struct ubus_context *ubus_ctx, const char *ifname, bool enable)
{
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s %d\n", ifname, __func__, enable);

	/* Get id from backhaul name */
	snprintf(name, sizeof(name), "wifi.bsta.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	blobmsg_add_u32(&bb, "enable", enable);

	ret = ubus_invoke(ubus_ctx, id, "4addr", bb.head, NULL, NULL, 1 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

struct ap_status_ctx {
	const char *ifname;
	struct wifi_ap_status *ap_status;
	int status;
};

static void wifi_ubus_ap_status_cb(struct ubus_request *req,
				   int type,
				   struct blob_attr *msg)
{
	struct ap_status_ctx *ctx = req->priv;
	static const struct blobmsg_policy ap_status_policy[] = {
			    [0] = { .name = "channel", .type = BLOBMSG_TYPE_INT32 },
			    [1] = { .name = "bssid", .type = BLOBMSG_TYPE_STRING },
			    [2] = { .name = "ssid", .type = BLOBMSG_TYPE_STRING },
			    [3] = { .name = "utilization", .type = BLOBMSG_TYPE_INT32 },
			    [4] = { .name = "capabilities", .type = BLOBMSG_TYPE_TABLE },
			    [5] = { .name = "standard", .type = BLOBMSG_TYPE_STRING },
			    [6] = { .name = "bandwidth", .type = BLOBMSG_TYPE_INT32 },
			    [7] = { .name = "status", .type = BLOBMSG_TYPE_STRING },
			    [8] = { .name = "num_stations", .type = BLOBMSG_TYPE_INT32 },
			    [9] = { .name = "enabled", .type = BLOBMSG_TYPE_BOOL },
			   [10] = { .name = "encryption", .type = BLOBMSG_TYPE_STRING },
			   [11] = { .name = "hidden", .type = BLOBMSG_TYPE_INT32 },
			   [12] = { .name = "max_stations", .type = BLOBMSG_TYPE_INT32 },
	};
	struct blob_attr *tb[ARRAY_SIZE(ap_status_policy)];
	struct wifi_ap *ap;

	blobmsg_parse(ap_status_policy, ARRAY_SIZE(ap_status_policy),
		      tb, blob_data(msg), blob_len(msg));

	if (!tb[0] || !tb[1] || !tb[2] || !tb[3] ||
	    !tb[8] || !tb[9] || !tb[12]) {
		ctx->status = -1;
		return;
	}

	ap = &ctx->ap_status->ap;

	ap->bss.channel = blobmsg_get_u32(tb[0]);
	if (hwaddr_aton(blobmsg_get_string(tb[1]), ap->bss.bssid) == NULL) {
		ctx->status = -1;
		return;
	}
	strncpy((char *) &ap->bss.ssid[0], blobmsg_get_string(tb[2]), sizeof(ap->bss.ssid) - 1);
	ap->bss.load.utilization = blobmsg_get_u32(tb[3]);

	ap->bss.load.sta_count = blobmsg_get_u32(tb[8]);
	ap->enabled = blobmsg_get_u32(tb[9]);

	ap->assoclist_max = blobmsg_get_u32(tb[12]);

	ctx->status = 0;
}

int wifi_ubus_ap_status(struct ubus_context *ubus_ctx, const char *ifname,
			struct wifi_ap_status *ap_status)
{
	struct ap_status_ctx ctx = {
		.ifname = ifname,
		.ap_status = ap_status,
		.status = -1,
	};
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s\n", ifname, __func__);

	/* Get id from radio name */
	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	ret = ubus_invoke(ubus_ctx, id, "status", bb.head,
			  wifi_ubus_ap_status_cb, &ctx,
			  2 * 1000);
	blob_buf_free(&bb);

	if (ctx.status)
		ret = ctx.status;

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

struct radio_scanresults_ctx {
	const char *radio;
	struct wifi_bss *bss;
	int num;
	int max;
	int status;
};

static int wifi_bss_rssi_cmp(const void *a, const void *b)
{
	const struct wifi_bss *aa = (const struct wifi_bss *)a;
	const struct wifi_bss *bb = (const struct wifi_bss *)b;

	/* strongest first (higher RSSI value = closer to zero) */
	return bb->rssi - aa->rssi;
}

static void wifi_ubus_radio_scanresults_cb(struct ubus_request *req,
					   int type,
					   struct blob_attr *msg)
{
	struct wifi_bss *bss;
	struct radio_scanresults_ctx *ctx = req->priv;
        static const struct blobmsg_policy radio_scanresults_policy[] = {
                [0] = { .name = "accesspoints", .type = BLOBMSG_TYPE_ARRAY },
	};
	struct blob_attr *tb[ARRAY_SIZE(radio_scanresults_policy)];
	struct blob_attr *cur;
	int rem, num;

	blobmsg_parse(radio_scanresults_policy, ARRAY_SIZE(radio_scanresults_policy),
		      tb, blob_data(msg), blob_len(msg));

	num = 0;
	ctx->num = 0;

	if (!tb[0]) {
		ctx->status = -1;
		return;
	}

	blobmsg_for_each_attr(cur, tb[0], rem) {
                static const struct blobmsg_policy bss_policy[] = {
                        [0] = { .name = "ssid", .type = BLOBMSG_TYPE_STRING },
                        [1] = { .name = "bssid", .type = BLOBMSG_TYPE_STRING },
                        [2] = { .name = "channel", .type = BLOBMSG_TYPE_INT32 },
                        [3] = { .name = "bandwidth", .type = BLOBMSG_TYPE_INT32 },
                        [4] = { .name = "rssi", .type = BLOBMSG_TYPE_INT32 },
                        [5] = { .name = "load_stas", .type = BLOBMSG_TYPE_INT32 },
                        [6] = { .name = "load_utilization", .type = BLOBMSG_TYPE_INT32 }
                };
                struct blob_attr *bss_tb[ARRAY_SIZE(bss_policy)];

		if (WARN_ON(num >= ctx->max))
			break;

		blobmsg_parse(bss_policy, ARRAY_SIZE(bss_policy), bss_tb,
			      blobmsg_data(cur), blobmsg_data_len(cur));

		if (!bss_tb[0] || !bss_tb[1] || !bss_tb[2] || !bss_tb[3] || !bss_tb[4] || !bss_tb[5] || !bss_tb[6])
			continue;

		bss = &ctx->bss[num];

		strncpy((char *) bss->ssid, blobmsg_get_string(bss_tb[0]), sizeof(bss->ssid) - 1);
		hwaddr_aton(blobmsg_get_string(bss_tb[1]), bss->bssid);
		bss->channel = blobmsg_get_u32(bss_tb[2]);

		switch (blobmsg_get_u32(bss_tb[3])) {
		case 20:
			bss->curr_bw = BW20;
			break;
		case 40:
			bss->curr_bw = BW40;
			break;
		case 80:
			bss->curr_bw = BW80;
			break;
		case 160:
			bss->curr_bw = BW160;
			break;
		case 8080:
			bss->curr_bw = BW8080;
			break;
		default:
			bss->curr_bw = BW20;
			break;
		}
		bss->rssi = (int32_t)blobmsg_get_u32(bss_tb[4]);
		bss->load.sta_count = blobmsg_get_u32(bss_tb[5]);
		bss->load.utilization = blobmsg_get_u32(bss_tb[6]);

		num++;
	}

	ctx->num = num;
	ctx->status = 0;

	/* sort by RSSI strongest first */
	if (ctx->num > 1)
		qsort(ctx->bss, ctx->num, sizeof(struct wifi_bss), wifi_bss_rssi_cmp);
}

int wifi_ubus_radio_scanresults(struct ubus_context *ubus_ctx, const char *radio,
				struct wifi_bss *bss, int *num, bool cache)
{
	struct radio_scanresults_ctx ctx = {
		.radio = radio,
		.bss = bss,
		.num = 0,
		.max = *num,
		.status = -1,
	};
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s\n", radio, __func__);

	/* Get id from radio name */
	snprintf(name, sizeof(name), "wifi.radio.%s", radio);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	blobmsg_add_string(&bb, "cache", cache ? "true" : "false");

	ret = ubus_invoke(ubus_ctx, id, "scanresults", bb.head,
			  wifi_ubus_radio_scanresults_cb, &ctx,
			  2 * 1000);
	blob_buf_free(&bb);

	if (ctx.status)
		ret = ctx.status;

	*num = ctx.num;

out:
	trace("[%s] %s ret %d num %d max %d\n", radio, __func__, ret, *num, ctx.max);
	return ret;
}


struct radio_chinfo_ctx {
	const char *radio;
	struct chan_entry *ch;
	int num;
	int max;
	int status;
};

static void wifi_ubus_channels_info_cb(struct ubus_request *req,
					   int type,
					   struct blob_attr *msg)
{
	struct radio_chinfo_ctx *ctx = req->priv;
        static const struct blobmsg_policy radio_chinfo_policy[] = {
                [0] = { .name = "channels", .type = BLOBMSG_TYPE_ARRAY },
	};
	struct blob_attr *tb[ARRAY_SIZE(radio_chinfo_policy)];
	struct chan_entry *ch;
	struct blob_attr *cur;
	int rem, num;

	blobmsg_parse(radio_chinfo_policy, ARRAY_SIZE(radio_chinfo_policy),
		      tb, blob_data(msg), blob_len(msg));

	num = 0;
	ctx->num = 0;

	if (!tb[0]) {
		ctx->status = -1;
		return;
	}

	blobmsg_for_each_attr(cur, tb[0], rem) {
                static const struct blobmsg_policy ch_policy[] = {
                        [0] = { .name = "channel", .type = BLOBMSG_TYPE_INT32 },
                        [1] = { .name = "freq", .type = BLOBMSG_TYPE_INT32 },
                        [2] = { .name = "noise", .type = BLOBMSG_TYPE_INT32 },
                        [3] = { .name = "diagnostics", .type = BLOBMSG_TYPE_TABLE }
                };
                struct blob_attr *ch_tb[ARRAY_SIZE(ch_policy)];

		if (WARN_ON(num >= ctx->max))
			break;

		blobmsg_parse(ch_policy, ARRAY_SIZE(ch_policy), ch_tb,
			blobmsg_data(cur), blobmsg_data_len(cur));

		if (!ch_tb[0] || !ch_tb[1] || !ch_tb[2] || !ch_tb[3])
			continue;

		ch = &ctx->ch[num];

		ch->channel = blobmsg_get_u32(ch_tb[0]);
		ch->freq = blobmsg_get_u32(ch_tb[1]);
		ch->noise = (int32_t)blobmsg_get_u32(ch_tb[2]);

		{
			static const struct blobmsg_policy diag_policy[] = {
				[0] = { .name = "channel_busy", .type = BLOBMSG_TYPE_INT64 },
				[1] = { .name = "tx_airtime", .type = BLOBMSG_TYPE_INT64 },
				[2] = { .name = "rx_airtime", .type = BLOBMSG_TYPE_INT64 },
				[3] = { .name = "obss_airtime", .type = BLOBMSG_TYPE_INT64 },
				[4] = { .name = "cca_time", .type = BLOBMSG_TYPE_INT64 },
				[5] = { .name = "false_cca_count", .type = BLOBMSG_TYPE_INT64 }
			};
			struct blob_attr *diag_tb[ARRAY_SIZE(diag_policy)];

			blobmsg_parse(diag_policy, ARRAY_SIZE(diag_policy), diag_tb,
				blobmsg_data(ch_tb[3]), blobmsg_data_len(ch_tb[3]));

			if (!diag_tb[0] || !diag_tb[1] || !diag_tb[2] || !diag_tb[3] || !diag_tb[4] || !diag_tb[5])
				continue;

			ch->survey.channel_busy = blobmsg_get_u64(diag_tb[0]);
			ch->survey.tx_airtime = blobmsg_get_u64(diag_tb[1]);
			ch->survey.rx_airtime = blobmsg_get_u64(diag_tb[2]);
			ch->survey.obss_airtime = blobmsg_get_u64(diag_tb[3]);
			ch->survey.cca_time = blobmsg_get_u64(diag_tb[4]);
			ch->survey.false_cca_count = blobmsg_get_u64(diag_tb[5]);
		}
		num++;
	}

	ctx->num = num;
	ctx->status = 0;
}



int wifi_ubus_channels_info(struct ubus_context *ubus_ctx, const char *radio,
				struct chan_entry *ch, int *num)
{
	struct radio_chinfo_ctx ctx = {
		.radio = radio,
		.ch = ch,
		.num = 0,
		.max = *num,
		.status = -1,
	};
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s\n", radio, __func__);

	/* Get id from radio name */
	snprintf(name, sizeof(name), "wifi.radio.%s", radio);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	ret = ubus_invoke(ubus_ctx, id, "channels_info", bb.head,
			  wifi_ubus_channels_info_cb, &ctx,
			  2 * 1000);
	blob_buf_free(&bb);

	if (ctx.status)
		ret = ctx.status;

	*num = ctx.num;

out:
	trace("[%s] %s ret %d num %d max %d\n", radio, __func__, ret, *num, ctx.max);
	return ret;
}

int wifi_ubus_chan_switch(struct ubus_context *ubus_ctx, const char *ifname,
			  struct chan_switch_param *param)
{
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s chan %d bw %d\n", ifname, __func__, param->freq, param->bandwidth);

	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);

	blobmsg_add_u32(&bb, "freq", param->freq);
	blobmsg_add_u32(&bb, "bw", param->bandwidth);
	blobmsg_add_u32(&bb, "count", param->count);

	if (param->cf1)
		blobmsg_add_u32(&bb, "cf1", param->cf1);
	if (param->cf2)
		blobmsg_add_u32(&bb, "cf2", param->cf2);
	if (param->sec_chan_offset)
		blobmsg_add_u32(&bb, "sec_chan_offset", param->sec_chan_offset);

	blobmsg_add_u8(&bb, "ht", param->ht);
	blobmsg_add_u8(&bb, "vht", param->vht);
	blobmsg_add_u8(&bb, "he", param->he);
	blobmsg_add_u8(&bb, "eht", param->eht);

	ret = ubus_invoke(ubus_ctx, id, "chan_switch", bb.head,
			  NULL, NULL, 2 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

struct ap_assoclist_ctx {
	struct netif_ap *ap;
	const char *ifname;
	uint8_t *sta;
	int num;
	int max;
	int status;
};

static void wifi_ubus_ap_assoclist_cb(struct ubus_request *req,
				      int type,
				      struct blob_attr *msg)
{
	struct ap_assoclist_ctx *ctx = req->priv;
	static const struct blobmsg_policy assoclist_policy[] = {
		[0] = { .name = "assoclist", .type = BLOBMSG_TYPE_ARRAY },
	};
	struct blob_attr *tb[ARRAY_SIZE(assoclist_policy)];
	struct blob_attr *cur;
	int num = 0;
	int rem;

	blobmsg_parse(assoclist_policy, ARRAY_SIZE(assoclist_policy),
		      tb, blob_data(msg), blob_len(msg));

	if (!tb[0]) {
		ctx->status = -1;
		return;
	}

	blobmsg_for_each_attr(cur, tb[0], rem) {
		if (WARN_ON(num >= ctx->max))
			break;

		hwaddr_aton(blobmsg_get_string(cur), &ctx->sta[num * 6]);
		num++;
	}

	ctx->num = num;
	ctx->status = 0;
}

int wifi_ubus_get_assoclist(struct ubus_context *ubus_ctx, struct netif_ap *ap,
			   uint8_t *sta, int *num)
{
	const char *ifname = ap->ifname;
	struct blob_buf bb = { 0 };
	char name[256] = {};
	struct ap_assoclist_ctx ctx = {
		.ap = ap,
		.ifname = ifname,
		.sta = sta,
		.num = 0,
		.max = *num,
		.status = -1
	};
	uint32_t id;
	int ret = -1;

	trace("[%s] %s\n", ifname, __func__);

	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
#if (EASYMESH_VERSION >= 6)
	if (ap->is_affiliated_ap)
		snprintf(name, sizeof(name), "wifi.apmld.%s", ap->mld_ifname);
#endif
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	ret = ubus_invoke(ubus_ctx, id, "assoclist", bb.head,
			  wifi_ubus_ap_assoclist_cb, &ctx, 2 * 1000);
	blob_buf_free(&bb);

	*num = ctx.num;
out:
	trace("[%s] %s ret %d num %d\n", ifname, __func__, ret, *num);
	return ret;
}

int wifi_ubus_req_neighbor(struct ubus_context *ubus_ctx, const char *ifname,
			   uint8_t *sta, struct wifi_request_neighbor_param *param)
{
	char client_macstr[18] = { 0 };
	char bssid_macstr[18] = { 0 };
	struct blob_buf bb = { 0 };
	char objname[32] = {0};
	char ssid_str[33] = {0};
	uint32_t id;
	uint8_t *pos;
	int i, j, ret = -1;

	trace("[%s]: %s\n", ifname, __func__);

	blob_buf_init(&bb, 0);

	if (!sta)
		goto out;

	snprintf(objname, 31, "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, objname, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	hwaddr_ntoa(sta, client_macstr);
	blobmsg_add_string(&bb, "client", client_macstr);

	if (param->opclass)
		blobmsg_add_u32(&bb, "opclass", param->opclass);

	if (param->channel)
		blobmsg_add_u32(&bb, "channel", param->channel);

	if (param->bssid) {
		hwaddr_ntoa(param->bssid, bssid_macstr);
		blobmsg_add_string(&bb, "bssid", bssid_macstr);
	}

	/* Start with passive mode by default */
	blobmsg_add_string(&bb, "mode", "passive");
	blobmsg_add_u32(&bb, "duration", 100);
	if (!(param->rrm_mode & NBR_PARAM_BCN_PASSIVE)) {
		/* passive not supported, check for active but keep passive otherwise */
		if (param->rrm_mode & NBR_PARAM_BCN_ACTIVE) {
			blobmsg_add_string(&bb, "mode", "active");
			blobmsg_add_u32(&bb, "duration", 20);
		}
	}

	if (param->reporting_detail)
		blobmsg_add_u32(&bb, "reporting_detail", param->reporting_detail);

	if (param->ssid_len && param->ssid) {
		if (param->ssid_len > 32) {
			trace("%s: ssid too long\n", __func__);
			goto out;
		}
		snprintf(ssid_str, 33, "%s", param->ssid);
		blobmsg_add_string(&bb, "ssid", ssid_str);
	}

	/* If the value of the Number of AP Channel Reports field (h)
	 * in the query is greater than zero and the value of the
	 * Channel Number field in the query is 255, then h AP Channel
	 * Report subelements shall be included in the 802.11 Beacon
	 * request, each containing the specified Operating Class and
	 * Channel List.
	 */
	if (param->num_report && param->channel == 255) {
		void *aa;

		dbg("%s: adding AP Channel Reports\n",
				__func__);

		aa = blobmsg_open_array(&bb, "channel_report");
		pos = param->report;
		for (i = 0; i < param->num_report; i++) {
			struct ap_channel_report *rep =
					(struct ap_channel_report *) pos;
			void *t, *b;

			t = blobmsg_open_table(&bb, "");
			blobmsg_add_u32(&bb, "opclass", rep->opclass);
			b = blobmsg_open_array(&bb, "channels");
			for (j = 0; j < (rep->len - 1); j++)
				blobmsg_add_u32(&bb, NULL, rep->channel[j]);
			blobmsg_close_array(&bb, b);
			blobmsg_close_table(&bb, t);
			pos += 1 + rep->len;
		}
		blobmsg_close_array(&bb, aa);
	}

	if (param->num_element) {
		void *c;

		dbg("%s: adding Element IDs\n",
				__func__);

		c = blobmsg_open_array(&bb, "request_element");
		pos = param->element;
		for (i = 0; i < param->num_element; i++) {
			blobmsg_add_u32(&bb, NULL, *pos);
			pos += 1;
		}
		blobmsg_close_array(&bb, c);
	}

	dbg("%s: ubus call request_neighbor on %s\n",
		__func__, ifname);

	ret = ubus_invoke(ubus_ctx, id, "request_neighbor", bb.head,
		NULL, NULL, 2 * 1000);

	if (ret) {
		trace("%s: ubus call failed for %s send, ret = %d\n",
			__func__, objname, ret);
		goto out;
	}

out:
	blob_buf_free(&bb);

	trace("[%s]: %s ret %d\n", ifname, __func__, ret);
	return ret;
}

struct get_stas_ctx {
	struct netif_ap *ap;
	const char *ifname;
	uint8_t *sta_addr;
	uint8_t mld_macaddr[6];
	struct wifi_sta *sta;
	int num;
	int max;
	int status;
};

static int wifi_ubus_get_sta_caps(struct blob_attr *msg,
				  struct wifi_caps *caps, uint8_t *cbitmap)
{
	static const struct blobmsg_policy caps_policy[] = {
			[0] = { .name = "bitmap", .type = BLOBMSG_TYPE_STRING },
	};
	struct blob_attr *caps_tb[ARRAY_SIZE(caps_policy)];
	int len;

	/* TODO: only updating cbitmap here, update caps (may be redundant) */

	blobmsg_parse(caps_policy, ARRAY_SIZE(caps_policy), caps_tb,
			blobmsg_data(msg), blobmsg_data_len(msg));

	caps->valid &= ~WIFI_CAP_RM_VALID;

	if (!caps_tb[0])
		return -1;

	len = strlen(blobmsg_get_string(caps_tb[0]));
	if (!strtob(blobmsg_get_string(caps_tb[0]), len / 2, cbitmap))
		return -1;

	caps->valid |= WIFI_CAP_RM_VALID;
	return 0;
}

static int wifi_ubus_get_sta_rssi(struct blob_attr *msg,
				  int8_t *rssi, int max)
{
	static const struct blobmsg_policy rssi_policy[] = {
		{ .type = BLOBMSG_TYPE_INT32 },
		{ .type = BLOBMSG_TYPE_INT32 },
		{ .type = BLOBMSG_TYPE_INT32 },
		{ .type = BLOBMSG_TYPE_INT32 },
	};
	struct blob_attr *tb[ARRAY_SIZE(rssi_policy)];

	blobmsg_parse_array(rssi_policy, ARRAY_SIZE(rssi_policy), tb,
			    blobmsg_data(msg), blobmsg_data_len(msg));

	if (!tb[0] || !tb[1] || !tb[2] || !tb[3])
		return -1;

	if (max < 4)
		return -1;

	rssi[0] = blobmsg_get_u32(tb[0]);
	rssi[1] = blobmsg_get_u32(tb[1]);
	rssi[2] = blobmsg_get_u32(tb[2]);
	rssi[3] = blobmsg_get_u32(tb[3]);

	return 0;
}

static int wifi_ubus_get_sta_rate(struct blob_attr *msg,
				  struct wifi_rate *rate)
{
	static const struct blobmsg_policy rate_policy[] = {
		[0] = { .name = "rate", .type = BLOBMSG_TYPE_INT32 },
		[1] = { .name = "mcs", .type = BLOBMSG_TYPE_INT32 },
		[2] = { .name = "bandwidth", .type = BLOBMSG_TYPE_INT32 },
		[3] = { .name = "sgi", .type = BLOBMSG_TYPE_INT32 },
		[4] = { .name = "nss", .type = BLOBMSG_TYPE_INT32 },
		[5] = { .name = "phy", .type = BLOBMSG_TYPE_INT32 },
	};
	struct blob_attr *tb[ARRAY_SIZE(rate_policy)];

	blobmsg_parse(rate_policy, ARRAY_SIZE(rate_policy), tb,
		      blobmsg_data(msg), blobmsg_data_len(msg));

	if (!tb[0] || !tb[1] || !tb[2] || !tb[3] || !tb[4] || !tb[5])
		return -1;

	rate->rate = blobmsg_get_u32(tb[0]);
	rate->m.mcs = blobmsg_get_u32(tb[1]);
	rate->m.bw = bw_to_wifi_bw(blobmsg_get_u32(tb[2]));
	rate->m.sgi = blobmsg_get_u32(tb[3]);
	rate->m.nss = blobmsg_get_u32(tb[4]);
	rate->phy = blobmsg_get_u32(tb[5]);

	return 0;
}


static int wifi_ubus_get_sta_stats(struct blob_attr *msg,
				   struct wifi_sta *sta,
				   struct wifi_sta_stats *stats)
{
	static const struct blobmsg_policy stats_policy[] = {
		[0] = { .name = "tx_total_pkts", .type = BLOBMSG_TYPE_INT64 },
		[1] = { .name = "tx_total_bytes", .type = BLOBMSG_TYPE_INT64 },
		[2] = { .name = "tx_failures", .type = BLOBMSG_TYPE_INT64 },
		[3] = { .name = "tx_pkts_retries", .type = BLOBMSG_TYPE_INT64 },
		[4] = { .name = "rx_data_pkts", .type = BLOBMSG_TYPE_INT64 },
		[5] = { .name = "rx_data_bytes", .type = BLOBMSG_TYPE_INT64 },
		[6] = { .name = "rx_failures", .type = BLOBMSG_TYPE_INT64 },
	};
	struct blob_attr *tb[ARRAY_SIZE(stats_policy)];

	blobmsg_parse(stats_policy, ARRAY_SIZE(stats_policy), tb,
		      blobmsg_data(msg), blobmsg_data_len(msg));


	if (!tb[0] || !tb[1] || !tb[2] || !tb[3] || !tb[4]
			   || !tb[5] || !tb[6])
		return -1;

	stats->tx_pkts = blobmsg_get_u64(tb[0]);
	stats->tx_bytes = blobmsg_get_u64(tb[1]);
	stats->tx_fail_pkts = blobmsg_get_u64(tb[2]);
	stats->tx_retry_pkts = blobmsg_get_u64(tb[3]);

	stats->rx_pkts = blobmsg_get_u64(tb[4]);
	stats->rx_bytes = blobmsg_get_u64(tb[5]);
	stats->rx_fail_pkts = blobmsg_get_u64(tb[6]);

	return 0;
}

static int wifi_ubus_parse_sta(struct blob_attr *cur,
				  struct wifi_sta *sta,
				  char *sta_addr_str)
{
	static const struct blobmsg_policy sta_policy[] = {
			[0] = { .name = "macaddr", .type = BLOBMSG_TYPE_STRING },
			[1] = { .name = "bssid", .type = BLOBMSG_TYPE_STRING },
			[2] = { .name = "channel", .type = BLOBMSG_TYPE_INT32 },
			[3] = { .name = "rssi", .type = BLOBMSG_TYPE_INT32 },
			[4] = { .name = "stats", .type = BLOBMSG_TYPE_TABLE },
			[5] = { .name = "in_network", .type = BLOBMSG_TYPE_INT64 },
			[6] = { .name = "tx_airtime", .type = BLOBMSG_TYPE_INT32 },
			[7] = { .name = "rx_airtime", .type = BLOBMSG_TYPE_INT32 },
			[8] = { .name = "nss", .type = BLOBMSG_TYPE_INT32 },
			[9] = { .name = "bandwidth", .type = BLOBMSG_TYPE_INT32 },
			[10] = { .name = "capabilities", .type = BLOBMSG_TYPE_TABLE },
			[11] = { .name = "maxrate", .type = BLOBMSG_TYPE_INT32 },
			[12] = { .name = "rssi_per_antenna", .type = BLOBMSG_TYPE_ARRAY },
			[13] = { .name = "est_rx_thput", .type = BLOBMSG_TYPE_INT32 },
			[14] = { .name = "est_tx_thput", .type = BLOBMSG_TYPE_INT32 },
			[15] = { .name = "rssi_avg", .type = BLOBMSG_TYPE_INT32 },
			[16] = { .name = "tx_rate_latest", .type = BLOBMSG_TYPE_TABLE },
			[17] = { .name = "rx_rate_latest", .type = BLOBMSG_TYPE_TABLE },
		};
		struct blob_attr *sta_tb[ARRAY_SIZE(sta_policy)];

		blobmsg_parse(sta_policy, ARRAY_SIZE(sta_policy), sta_tb,
			      blobmsg_data(cur), blobmsg_data_len(cur));

		if (!sta_tb[0] || !sta_tb[1] || !sta_tb[2] || (!sta_tb[3] && !sta_tb[15]))
			return -1;

		if (sta_addr_str && strcmp(blobmsg_get_string(sta_tb[0]), sta_addr_str))
			return -1;

		hwaddr_aton(blobmsg_get_string(sta_tb[0]), sta->macaddr);
		hwaddr_aton(blobmsg_get_string(sta_tb[1]), sta->bssid);

		if (sta_tb[3])
			sta->rssi_avg = blobmsg_get_u32(sta_tb[3]);
		else
			sta->rssi_avg = blobmsg_get_u32(sta_tb[15]);

		sta->conn_time = sta_tb[5] ? blobmsg_get_u64(sta_tb[5]) : 0;
		sta->tx_airtime = sta_tb[6] ? blobmsg_get_u32(sta_tb[6]) : 0;
		sta->rx_airtime = sta_tb[7] ? blobmsg_get_u32(sta_tb[7]) : 0;

		sta->est_rx_thput = sta_tb[13] ? blobmsg_get_u32(sta_tb[13]) : 0;
		sta->est_tx_thput = sta_tb[14] ? blobmsg_get_u32(sta_tb[14]) : 0;

		if (sta_tb[12])
			wifi_ubus_get_sta_rssi(sta_tb[12], sta->rssi, sizeof(sta->rssi));
		if (sta_tb[10])
			wifi_ubus_get_sta_caps(sta_tb[10], &sta->caps, sta->cbitmap);
		if (sta_tb[4])
			wifi_ubus_get_sta_stats(sta_tb[4], sta, &sta->stats);
		if (sta_tb[16])
			wifi_ubus_get_sta_rate(sta_tb[16], &sta->tx_rate);
		if (sta_tb[17])
			wifi_ubus_get_sta_rate(sta_tb[17], &sta->rx_rate);

		return 0;
}


static void wifi_ubus_get_stas_cb(struct ubus_request *req,
				  int type,
				  struct blob_attr *msg)
{
	struct get_stas_ctx *ctx = req->priv;
        static const struct blobmsg_policy get_stations_policy[] = {
                [0] = { .name = "stations", .type = BLOBMSG_TYPE_ARRAY },
	};
	struct blob_attr *tb[ARRAY_SIZE(get_stations_policy)];
	char sta_addr_str[18] = {};
	struct blob_attr *cur;
	int num, rem;

	ctx->num = 0;
	num = 0;

	if (ctx->sta_addr)
		hwaddr_ntoa(ctx->sta_addr, sta_addr_str);

	blobmsg_parse(get_stations_policy, ARRAY_SIZE(get_stations_policy),
		      tb, blob_data(msg), blob_len(msg));

	if (!tb[0]) {
		ctx->status = -1;
		return;
	}

	blobmsg_for_each_attr(cur, tb[0], rem) {
		struct wifi_sta *sta;
		int ret;

		if (WARN_ON(num >= ctx->max))
			break;

		sta = &ctx->sta[num];
		ret = wifi_ubus_parse_sta(cur, sta, (ctx->sta_addr ? sta_addr_str : NULL));
		if (ret)
			continue;

		num++;

		/* Single station requested */
		if (ctx->sta_addr)
			break;
	}

	ctx->num = num;
	ctx->status = 0;
}

static int wifi_ubus_get_stas(struct ubus_context *ubus_ctx, struct netif_ap *ap,
			      uint8_t *sta_addr, struct wifi_sta *sta, int *num)
{
	const char *ifname = ap->ifname;
	struct get_stas_ctx ctx = {
		.ap = ap,
		.ifname = ifname,
		.sta = sta,
		.max = *num,
		.num = 0,
		.status = -1,
	};
	struct blob_buf bb = {};
	char sta_addr_str[18] = {};
	char name[256] = {};
	uint32_t id;
	int ret = -1;

	trace("[%s] %s\n", ifname, __func__);

	*num = 0;

	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
#if (EASYMESH_VERSION >= 6)
	if (ap->is_affiliated_ap)
		snprintf(name, sizeof(name), "wifi.apmld.%s", ap->mld_ifname);
#endif
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);

	if (sta_addr) {
		hwaddr_ntoa(sta_addr, sta_addr_str);
		blobmsg_add_string(&bb, "sta", sta_addr_str);
	}

	ret = ubus_invoke(ubus_ctx, id, "stations", bb.head,
			  wifi_ubus_get_stas_cb, &ctx,
			  2 * 1000);

	blob_buf_free(&bb);

	if (ctx.status)
		ret = ctx.status;

	*num = ctx.num;
out:
	trace("[%s] %s ret %d (num %d)\n", ifname, __func__, ret, *num);
	return ret;
}

int wifi_ubus_get_stations(struct ubus_context *ubus_ctx, struct netif_ap *ap,
			   struct wifi_sta *sta, int *num)
{
	return wifi_ubus_get_stas(ubus_ctx, ap, NULL, sta, num);
}

int wifi_ubus_get_station(struct ubus_context *ubus_ctx, struct netif_ap *ap,
			  uint8_t *sta_addr, struct wifi_sta *sta)
{
	int num = 1;

	return wifi_ubus_get_stas(ubus_ctx, ap, sta_addr, sta, &num);
}


#if (EASYMESH_VERSION >= 6)
struct get_links_ctx {
	struct netif_ap *ap;
	const char *ifname;
	uint8_t *sta_addr;
	uint8_t *mld_macaddr;
	uint8_t *link_macaddr;
	struct wifi_mlsta *mlsta;
	int num;
	int max;
	int status;
};

static void wifi_sta_parse_mlo_links(struct ubus_request *req,
				  int type,
				  struct blob_attr *msg)
{
	struct get_links_ctx *ctx = req->priv;
	struct wifi_mlsta *mlsta = NULL;
	struct wifi_sta *sta = NULL;
	struct blob_attr *cur;
	int rem;

	blobmsg_for_each_attr(cur, msg, rem) {
		static const struct blobmsg_policy link_policy[] = {
			[0] = { .name = "link_id", .type = BLOBMSG_TYPE_INT32 },
			[1] = { .name = "link_macaddr", .type = BLOBMSG_TYPE_STRING },
			[2] = { .name = "link_bssid", .type = BLOBMSG_TYPE_STRING },
			[3] = { .name = "channel", .type = BLOBMSG_TYPE_INT32 },
			[4] = { .name = "rssi_avg", .type = BLOBMSG_TYPE_INT32 },
			//[5] = { .name = "stats", .type = BLOBMSG_TYPE_TABLE },
			[6] = { .name = "in_network", .type = BLOBMSG_TYPE_INT64 },
			[7] = { .name = "tx_airtime", .type = BLOBMSG_TYPE_INT32 },
			[8] = { .name = "rx_airtime", .type = BLOBMSG_TYPE_INT32 },
			[9] = { .name = "nss", .type = BLOBMSG_TYPE_INT32 },
			[10] = { .name = "bandwidth", .type = BLOBMSG_TYPE_INT32 },
			[11] = { .name = "capabilities", .type = BLOBMSG_TYPE_TABLE },
			[12] = { .name = "maxrate", .type = BLOBMSG_TYPE_INT32 },
			[13] = { .name = "rssi_per_antenna", .type = BLOBMSG_TYPE_ARRAY },
			[14] = { .name = "est_rx_thput", .type = BLOBMSG_TYPE_INT32 },
			[15] = { .name = "est_tx_thput", .type = BLOBMSG_TYPE_INT32 },
			[16] = { .name = "tx_rate_latest", .type = BLOBMSG_TYPE_TABLE },
			[17] = { .name = "rx_rate_latest", .type = BLOBMSG_TYPE_TABLE },
		};
		struct blob_attr *sta_tb[ARRAY_SIZE(link_policy)];
		char sta_addr_str[18] = {};
		int num = ctx->num;

		blobmsg_parse(link_policy, ARRAY_SIZE(link_policy), sta_tb,
			      blobmsg_data(cur), blobmsg_data_len(cur));

		if (!sta_tb[0] || !sta_tb[1] || !sta_tb[2])
			continue;


		if (ctx->link_macaddr)
			hwaddr_ntoa(ctx->link_macaddr, sta_addr_str);
		if (ctx->link_macaddr && strcmp(blobmsg_get_string(sta_tb[1]), sta_addr_str))
			continue;

		if (WARN_ON(num >= ctx->max))
			break;

		mlsta = &ctx->mlsta[num];
		sta = &(mlsta->sta[mlsta->num_link++]);

		sta->mlo_link_id = blobmsg_get_u32(sta_tb[0]);
		hwaddr_aton(blobmsg_get_string(sta_tb[1]), sta->macaddr);
		hwaddr_aton(blobmsg_get_string(sta_tb[2]), sta->bssid);

		sta->rssi_avg = sta_tb[4] ? blobmsg_get_u32(sta_tb[4]) : 0;
		sta->conn_time = sta_tb[6] ? blobmsg_get_u64(sta_tb[6]) : 0;
		sta->tx_airtime = sta_tb[7] ? blobmsg_get_u32(sta_tb[7]) : 0;
		sta->rx_airtime = sta_tb[8] ? blobmsg_get_u32(sta_tb[8]) : 0;
		sta->est_rx_thput = sta_tb[14] ? blobmsg_get_u32(sta_tb[14]) : 0;
		sta->est_tx_thput = sta_tb[15] ? blobmsg_get_u32(sta_tb[15]) : 0;

		if (sta_tb[13])
			wifi_ubus_get_sta_rssi(sta_tb[13], sta->rssi, sizeof(sta->rssi));
		if (sta_tb[11])
			wifi_ubus_get_sta_caps(sta_tb[11], &sta->caps, sta->cbitmap);
		if (sta_tb[16])
			wifi_ubus_get_sta_rate(sta_tb[16], &sta->tx_rate);
		if (sta_tb[17])
			wifi_ubus_get_sta_rate(sta_tb[17], &sta->rx_rate);
		//wifi_ubus_get_sta_stats(sta_tb[5], sta, &sta->stats);

		ctx->num++;
	}
}

static void wifi_ubus_get_mld_sta_links_cb(struct ubus_request *req, int type,
				       struct blob_attr *msg)
{
	struct get_links_ctx *ctx = req->priv;
        static const struct blobmsg_policy get_stations_policy[] = {
                [0] = { .name = "stations", .type = BLOBMSG_TYPE_ARRAY },
	};
	struct blob_attr *tb[ARRAY_SIZE(get_stations_policy)];
	char sta_addr_str[18] = {};
	struct wifi_mlsta *mlsta;
	struct wifi_sta *sta;
	struct blob_attr *cur;
	int num, rem;

	ctx->num = 0;
	num = 0;

	if (ctx->link_macaddr)
		hwaddr_ntoa(ctx->link_macaddr, sta_addr_str);

	blobmsg_parse(get_stations_policy, ARRAY_SIZE(get_stations_policy),
		      tb, blob_data(msg), blob_len(msg));
	if (!tb[0]) {
		ctx->status = -1;
		return;
	}

	blobmsg_for_each_attr(cur, tb[0], rem) {
		static const struct blobmsg_policy sta_policy[] = {
			[0] = { .name = "macaddr", .type = BLOBMSG_TYPE_STRING },
			[1] = { .name = "bssid", .type = BLOBMSG_TYPE_STRING },
			[2] = { .name = "stats", .type = BLOBMSG_TYPE_TABLE },
			[3] = { .name = "mlo_capable", .type = BLOBMSG_TYPE_BOOL },
			[4] = { .name = "mlo_links", .type = BLOBMSG_TYPE_ARRAY },

		};
		struct blob_attr *sta_tb[ARRAY_SIZE(sta_policy)];
		blobmsg_parse(sta_policy, ARRAY_SIZE(sta_policy), sta_tb,
			      blobmsg_data(cur), blobmsg_data_len(cur));

		if (!sta_tb[0] || !sta_tb[1] || !sta_tb[3])
			continue;

		if (WARN_ON(num >= ctx->max))
			break;

		mlsta = &ctx->mlsta[num];
		sta = &(mlsta->sta[0]);

		hwaddr_aton(blobmsg_get_string(sta_tb[0]), mlsta->macaddr);
		hwaddr_aton(blobmsg_get_string(sta_tb[0]), sta->mld_macaddr);
		hwaddr_aton(blobmsg_get_string(sta_tb[1]), sta->mld_bssid);
		hwaddr_aton(blobmsg_get_string(sta_tb[1]), mlsta->bssid);

		mlsta->mlo_capable = blobmsg_get_bool(sta_tb[3]);
		if (sta_tb[4] && mlsta->mlo_capable) {
			wifi_sta_parse_mlo_links(req, type, sta_tb[4]);
		} else {
			int ret;

			ret = wifi_ubus_parse_sta(cur, sta, (ctx->link_macaddr ? sta_addr_str : NULL));
			if (!ret)
				ctx->num++;
		}

		num = ctx->num;

		if (sta_tb[2])
			wifi_ubus_get_sta_stats(sta_tb[2], &mlsta->sta[0], &mlsta->sta[0].stats); // TODO: Today assigning per-link is not supported
	}

	ctx->num = num;
	ctx->status = 0;
}

static int wifi_ubus_get_mld_sta_links(struct ubus_context *ubus_ctx, struct netif_ap *ap,
			      uint8_t *mld_addr, uint8_t *link_addr, struct wifi_mlsta *mlsta, int *num)
{
	const char *ifname = ap->ifname;
	struct get_links_ctx ctx = {
		.ap = ap,
		.ifname = ifname,
		.mlsta = mlsta,
		.link_macaddr = link_addr,
		.max = *num,
		.num = 0,
		.status = -1,
	};
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s\n", ifname, __func__);

	*num = 0;

	snprintf(name, sizeof(name), "wifi.apmld.%s", ap->mld_ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);

	if (mld_addr && !hwaddr_is_zero(mld_addr)) {
		char mld_addr_str[18] = {};

		hwaddr_ntoa(mld_addr, mld_addr_str);
		blobmsg_add_string(&bb, "sta", mld_addr_str);
		ctx.mld_macaddr = mld_addr;
	}

	ret = ubus_invoke(ubus_ctx, id, "stations", bb.head,
			  wifi_ubus_get_mld_sta_links_cb, &ctx,
			  2 * 1000);

	blob_buf_free(&bb);

	if (ctx.status)
		ret = ctx.status;

	*num = ctx.num;
out:
	trace("[%s] %s ret %d (num %d)\n", ifname, __func__, ret, *num);
	return ret;
}


int wifi_ubus_get_mld_station(struct ubus_context *ubus_ctx, struct netif_ap *ap,
			  uint8_t *mld_addr, uint8_t *link_addr, struct wifi_mlsta *mlsta)
{
	int num = 1;

	return wifi_ubus_get_mld_sta_links(ubus_ctx, ap, mld_addr, link_addr, mlsta, &num);
}

int wifi_ubus_get_mld_stations(struct ubus_context *ubus_ctx, struct netif_ap *ap,
			       struct wifi_mlsta *mlsta, int *num)
{
	return wifi_ubus_get_mld_sta_links(ubus_ctx, ap, NULL, NULL, mlsta, num);
}

#endif

int wifi_ubus_bsta_disconnect(struct ubus_context *ubus_ctx, struct netif_bk *bk,
				uint32_t reason)
{
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s\n", bk->ifname, __func__);

	snprintf(name, sizeof(name), "wifi.bsta.%s", bk->ifname);
#if (EASYMESH_VERSION >= 6)
	if (bk->cfg->is_mld_netdev)
		snprintf(name, sizeof(name), "wifi.bstamld.%s", bk->ifname);
	else if (bk->is_affiliated_sta && bk->agent->cfg.physical_mld)
		snprintf(name, sizeof(name), "wifi.bstamld.%s", bk->mld_ifname);
#endif
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	if (reason)
		blobmsg_add_u32(&bb, "reason", reason);

	ret = ubus_invoke(ubus_ctx, id, "disconnect", bb.head, NULL, NULL, 2 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", bk->ifname, __func__, ret);
	return ret;
}

static int wifi_ubus_subscribe_unsubscribe_frame(struct ubus_context *ubus_ctx,
						 const char *ifname,
						 uint8_t type, uint8_t stype,
						 bool subscribe)
{
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s\n", ifname, __func__);

	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	blobmsg_add_u32(&bb, "type", type);
	blobmsg_add_u32(&bb, "stype", stype);
	ret = ubus_invoke(ubus_ctx, id, subscribe ? "subscribe_frame" : "unsubscribe_frame",
			  bb.head, NULL, NULL, 2 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

int wifi_ubus_subscribe_frame(struct ubus_context *ubus_ctx, const char *ifname,
			      uint8_t type, uint8_t stype)
{
	return wifi_ubus_subscribe_unsubscribe_frame(ubus_ctx, ifname, type, stype, true);
}

int wifi_ubus_unsubscribe_frame(struct ubus_context *ubus_ctx, const char *ifname,
				uint8_t type, uint8_t stype)
{
	return wifi_ubus_subscribe_unsubscribe_frame(ubus_ctx, ifname, type, stype, false);
}

struct ap_stats_ctx {
	struct wifi_ap_stats *stats;
	int status;
};

static void wifi_ubus_ap_stats_cb(struct ubus_request *req,
				  int type,
				  struct blob_attr *msg)
{
	struct ap_stats_ctx *ctx = req->priv;
	static const struct blobmsg_policy stats_policy[] = {
		[0] = { .name = "tx_bytes", .type = BLOBMSG_TYPE_INT64 },
		[1] = { .name = "tx_packets", .type = BLOBMSG_TYPE_INT64 },
		[2] = { .name = "tx_unicast_packets", .type = BLOBMSG_TYPE_INT64 },
		[3] = { .name = "tx_multicast_packets", .type = BLOBMSG_TYPE_INT64 },
		[4] = { .name = "tx_broadcast_packets", .type = BLOBMSG_TYPE_INT64 },
		[5] = { .name = "tx_error_packets", .type = BLOBMSG_TYPE_INT64 },
		[6] = { .name = "tx_retrans_packets", .type = BLOBMSG_TYPE_INT64 },
		[7] = { .name = "tx_retrans_fail_packets", .type = BLOBMSG_TYPE_INT64 },
		[8] = { .name = "tx_retry_packets", .type = BLOBMSG_TYPE_INT64 },
		[9] = { .name = "tx_multi_retry_packets", .type = BLOBMSG_TYPE_INT64 },
		[10] = { .name = "tx_dropped_packets", .type = BLOBMSG_TYPE_INT64 },
		[11] = { .name = "ack_fail_packets", .type = BLOBMSG_TYPE_INT64 },
		[12] = { .name = "aggregate_packets", .type = BLOBMSG_TYPE_INT64 },
		[13] = { .name = "rx_bytes", .type = BLOBMSG_TYPE_INT64 },
		[14] = { .name = "rx_packets", .type = BLOBMSG_TYPE_INT64 },
		[15] = { .name = "rx_unicast_packets", .type = BLOBMSG_TYPE_INT64 },
		[16] = { .name = "rx_multicast_packets", .type = BLOBMSG_TYPE_INT64 },
		[17] = { .name = "rx_broadcast_packets", .type = BLOBMSG_TYPE_INT64 },
		[18] = { .name = "rx_error_packets", .type = BLOBMSG_TYPE_INT64 },
		[19] = { .name = "rx_dropped_packets", .type = BLOBMSG_TYPE_INT64 },
		[20] = { .name = "rx_unknown_packets", .type = BLOBMSG_TYPE_INT64 },
	};
	struct blob_attr *tb[ARRAY_SIZE(stats_policy)];
	struct wifi_ap_stats *stats;
	int i;

	blobmsg_parse(stats_policy, ARRAY_SIZE(stats_policy), tb,
		      blobmsg_data(msg), blobmsg_data_len(msg));

	for (i = 0; i < ARRAY_SIZE(stats_policy); i++) {
		if (!tb[i]) {
			ctx->status = -1;
			return;
		}
	}

	stats = ctx->stats;

	stats->tx_bytes = blobmsg_get_u64(tb[0]);
	stats->tx_pkts = blobmsg_get_u64(tb[1]);
	stats->tx_ucast_pkts = blobmsg_get_u64(tb[2]);
	stats->tx_mcast_pkts = blobmsg_get_u64(tb[3]);
	stats->tx_bcast_pkts = blobmsg_get_u64(tb[4]);
	stats->tx_err_pkts = blobmsg_get_u64(tb[5]);
	stats->tx_rtx_pkts = blobmsg_get_u64(tb[6]);
	stats->tx_rtx_fail_pkts = blobmsg_get_u64(tb[7]);
	stats->tx_retry_pkts = blobmsg_get_u64(tb[8]);
	stats->tx_mretry_pkts = blobmsg_get_u64(tb[9]);
	stats->tx_dropped_pkts = blobmsg_get_u64(tb[10]);
	stats->ack_fail_pkts = blobmsg_get_u64(tb[11]);
	stats->aggr_pkts = blobmsg_get_u64(tb[12]);

	stats->rx_bytes = blobmsg_get_u64(tb[13]);
	stats->rx_pkts = blobmsg_get_u64(tb[14]);
	stats->rx_ucast_pkts = blobmsg_get_u64(tb[15]);
	stats->rx_mcast_pkts = blobmsg_get_u64(tb[16]);
	stats->rx_bcast_pkts = blobmsg_get_u64(tb[17]);
	stats->rx_err_pkts = blobmsg_get_u64(tb[18]);
	stats->rx_dropped_pkts = blobmsg_get_u64(tb[19]);
	stats->rx_unknown_pkts = blobmsg_get_u64(tb[20]);

	ctx->status = 0;
}

int wifi_ubus_ap_stats(struct ubus_context *ubus_ctx, const char *ifname,
		       struct wifi_ap_stats *stats)
{
	struct ap_stats_ctx ctx = {
		.stats = stats,
		.status = -1,
	};
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s\n", ifname, __func__);

	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	ret = ubus_invoke(ubus_ctx, id, "stats", bb.head,
			  wifi_ubus_ap_stats_cb, &ctx, 2 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

struct bsta_status_ctx {
	const char *ifname;
	struct wifi_bsta_status *bsta_status;
	int status;
};

static void wifi_ubus_bsta_status_cb(struct ubus_request *req,
				     int type,
				     struct blob_attr *msg)
{
	struct bsta_status_ctx *ctx = req->priv;
        static const struct blobmsg_policy bsta_status_policy[] = {
		[0] = { .name = "status", .type = BLOBMSG_TYPE_STRING },
		[1] = { .name = "channel", .type = BLOBMSG_TYPE_INT32 },
		[2] = { .name = "bandwidth", .type = BLOBMSG_TYPE_INT32 },
		[3] = { .name = "macaddr", .type = BLOBMSG_TYPE_STRING },
		[4] = { .name = "bssid", .type = BLOBMSG_TYPE_STRING },
		[5] = { .name = "ssid", .type = BLOBMSG_TYPE_STRING },
		[6] = { .name = "4addr", .type = BLOBMSG_TYPE_BOOL },
		[7] = { .name = "frequency", .type = BLOBMSG_TYPE_INT32 },
	};
	struct blob_attr *tb[ARRAY_SIZE(bsta_status_policy)];
	struct wifi_bsta_status *status;

	trace("[%s] %s\n", ctx->ifname, __func__);

	blobmsg_parse(bsta_status_policy, ARRAY_SIZE(bsta_status_policy),
		      tb, blob_data(msg), blob_len(msg));

	if (!tb[0] || !tb[3] || !tb[6]) {
		ctx->status = -1;
		return;
	}

	status = ctx->bsta_status;

	status->mode4addr = blobmsg_get_u8(tb[6]);
	if (hwaddr_aton(blobmsg_get_string(tb[3]), status->sta.macaddr) == NULL) {
		ctx->status = -1;
		return;
	}

	if (tb[1])
		status->channel = blobmsg_get_u32(tb[1]);
	if (tb[2])
		status->bandwidth = bw_to_wifi_bw(blobmsg_get_u32(tb[2]));
	if (tb[4]) {
		if (hwaddr_aton(blobmsg_get_string(tb[4]), status->sta.bssid) == NULL) {
			ctx->status = -1;
			return;
		}
	}
	if (tb[5])
		strncpy((char *) &status->ssid[0], blobmsg_get_string(tb[5]), sizeof(status->ssid) - 1);
	if (tb[7])
		status->frequency = blobmsg_get_u32(tb[7]);

	ctx->status = 0;
}

int wifi_ubus_bsta_status(struct ubus_context *ubus_ctx, const char *ifname,
			  struct wifi_bsta_status *bsta_status)
{
	struct bsta_status_ctx ctx = {
		.ifname = ifname,
		.bsta_status = bsta_status,
		.status = -1,
	};
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;

	trace("[%s] %s\n", ifname, __func__);

	/* Get id from backhaul name */
	snprintf(name, sizeof(name), "wifi.bsta.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	ret = ubus_invoke(ubus_ctx, id, "status", bb.head,
			  wifi_ubus_bsta_status_cb, &ctx,
			  2 * 1000);
	blob_buf_free(&bb);

	if (ctx.status)
		ret = ctx.status;

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

int wifi_ubus_ap_mbo_disallow_assoc(struct ubus_context *ubus_ctx,
				    const char *ifname, bool disallow)
{
	enum { ALLOW = 0, DISALLOW = 2 };
	struct blob_buf bb = { 0 };
	char name[256] = { 0 };
	uint32_t id;
	int ret;

	trace("[%s] %s %s\n", ifname, __func__, disallow ? "DISALLOW" : "ALLOW");

	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	blobmsg_add_u32(&bb, "reason", disallow ? DISALLOW : ALLOW);
	ret = ubus_invoke(ubus_ctx, id, "mbo_disallow_assoc", bb.head, NULL, NULL,
			  2 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

int wifi_ubus_send_action(struct ubus_context *ubus_ctx, const char *ifname, uint8_t *dst_mac,
				uint8_t *src_mac, uint32_t wait, uint32_t freq, const uint8_t *bytes, int len)
{

	struct blob_buf bb = {};
	char name[256] = {};
	char dst_macstr[18] = {};
	char src_macstr[18] = {};
	char *frame_str = NULL;
	uint32_t id;
	int ret;

	if (len == 0 || bytes == NULL) {
		ret = -1;
		goto out;
	}

	hwaddr_ntoa(dst_mac, dst_macstr);
	trace("[%s] %s %s\n", ifname, __func__, dst_macstr);

	if (src_mac)
		hwaddr_ntoa(src_mac, src_macstr);

	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	/* code the octet stream towards hex string */
	frame_str = calloc(len * 2 + 1, sizeof(uint8_t));
	if (!frame_str) {
		ret = -1;
		goto out;
	}
	btostr(bytes, len, frame_str);

	blob_buf_init(&bb, 0);
	/* the client STA MAC addr */
	blobmsg_add_string(&bb, "dst", dst_macstr);
	blobmsg_add_string(&bb, "frame", frame_str);
	/* source MAC addr of the frame*/
	if (src_mac)
		blobmsg_add_string(&bb, "src", src_macstr);
	/* Frequency*/
	if (freq)
		blobmsg_add_u32(&bb, "freq", freq);
	/* Wait time*/
	if (wait)
		blobmsg_add_u32(&bb, "wait", wait);

	ret = ubus_invoke(ubus_ctx, id, "send_action", bb.head,
			  NULL, NULL, 2 * 1000);
	if (ret)
		warn("[%s] %s send_action failed!\n", ifname, __func__);

	blob_buf_free(&bb);

out:
	free(frame_str);
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

int wifi_ubus_initiate_wpspbc(struct ubus_context *ubus_ctx, const char *ifname, bool is_bsta)
{
	struct blob_buf bb = {};
	uint32_t obj_id;
	int ret;
	const char *role = is_bsta ? "bsta" : "registrar";

	ret = ubus_lookup_id(ubus_ctx, "wifi.wps", &obj_id);
	if (ret != UBUS_STATUS_OK) {
		trace("[%s] WiFi WPS object not found on ubus\n", __func__);
		return -1;
	}

	blob_buf_init(&bb, 0);
	blobmsg_add_string(&bb, "ifname", ifname);
	blobmsg_add_string(&bb, "role", role);

	ret = ubus_invoke(ubus_ctx, obj_id, "start", bb.head, NULL, NULL, 2 * 1000);
	blob_buf_free(&bb);

	if (ret != 0) {
		trace("[%s] WPS start failed on %s (ret=%d)\n", __func__, ifname, ret);
	} else {
		trace("[%s] WPS started via ubus on %s\n", __func__, ifname);
	}

	return ret;
}

#if (EASYMESH_VERSION > 2)
int wifi_ubus_set_qos_map(struct ubus_context *ubus_ctx, const char *ifname,
                          uint8_t *qos_map, size_t qos_map_len)
{
	struct blob_buf bb = {};
	char name[256] = {};
	uint32_t id;
	int ret;
	void *arr;
	size_t i;

	/* Get id from ap name */
	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;


	trace("[%s] %s map of len %zu\n", ifname, __func__, qos_map_len);
	blob_buf_init(&bb, 0);

	arr = blobmsg_open_array(&bb, "set");
	for (i = 0; i < qos_map_len; ++i) {
		blobmsg_add_u32(&bb, NULL, qos_map[i]);
	}
	blobmsg_close_array(&bb, arr);

	ret = ubus_invoke(ubus_ctx, id, "set_qos_map", bb.head, NULL, NULL, 1 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

int wifi_ubus_send_qos_map_conf(struct ubus_context *ubus_ctx, const char *ifname,
                                uint8_t *macaddr)
{
	struct blob_buf bb = {};
	char name[256] = {};
	char macstr[18] = {};
	uint32_t id;
	int ret;

	snprintf(macstr, sizeof(macstr), MACFMT, MAC2STR(macaddr)); /* Flawfinder: ignore */

	trace("[%s] %s " MACFMT "\n", ifname, __func__, MAC2STR(macaddr));

	/* Get id from ap name */
	snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK)
		goto out;

	blob_buf_init(&bb, 0);
	/* Setup required params */
	blobmsg_add_string(&bb, "sta", macstr);

	ret = ubus_invoke(ubus_ctx, id, "send_qos_map_conf", bb.head, NULL, NULL, 1 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}

#ifdef USE_LIBDPP
int wifi_ubus_ap_send_actframe(struct ubus_context *ubus_ctx,
			       const char *ifname, uint8_t *dst, uint8_t *frame,
			       size_t framelen, uint32_t freq, bool hostapd)
{
	struct blob_buf bb = {};
	char name[128] = {0};
	uint32_t id;
	int ret = 0;
	char *buf = NULL;
	char macstr[18] = {};

	trace("[%s] %s\n", ifname, __func__);

#ifdef DEBUG
	dump(frame, framelen, "frame we are sending over air");
#endif
	hwaddr_ntoa(dst, macstr);

	/* Get id from ap name */
	if (hostapd)
		snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	else
		snprintf(name, sizeof(name), "wifi.bsta.%s", ifname);

	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK) {
		warn("%s: fail\n", __func__);
		goto out;
	}

	buf = calloc(1, (framelen * 2) + 1);
	if (!buf) {
		warn("%s: fail\n", __func__);
		goto out;
	}

	btostr(frame, framelen, buf);
	blob_buf_init(&bb, 0);
	blobmsg_add_string(&bb, "dst", macstr);
	if (freq)
		blobmsg_add_u32(&bb, "freq", freq);
	blobmsg_add_u32(&bb, "wait", 2000);

	blobmsg_add_string(&bb, "frame", buf);
	ret = ubus_invoke(ubus_ctx, id, "send_action", bb.head, NULL, NULL, 2 * 1000);
	blob_buf_free(&bb);

out:
	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	free(buf);
	return ret;
}

int wifi_ubus_dpp_listen(struct ubus_context *ubus_ctx,
			    const char *ifname, uint32_t freq, bool hostapd)
{
	struct blob_buf bb = {};
	char name[64] = {0};
	uint32_t id;
	int ret = 0;

	trace("[%s] %s\n", ifname, __func__);

	/* Get id from ap name */
	if (hostapd)
		snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	else
		snprintf(name, sizeof(name), "wifi.bsta.%s", ifname);

	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK) {
		warn("%s: fail\n", __func__);
		return -1;
	}

	blob_buf_init(&bb, 0);
	blobmsg_add_u32(&bb, "freq", freq);
	ret = ubus_invoke(ubus_ctx, id, "dpp_listen", bb.head, NULL, NULL, 2 * 1000);

	blob_buf_free(&bb);

	trace("[%s] %s ret %d freq %u\n", ifname, __func__, ret, freq);
	return ret;
}

int wifi_ubus_dpp_stop_listen(struct ubus_context *ubus_ctx,
				 const char *ifname, bool hostapd)
{
	struct blob_buf bb = {};
	char name[64] = {0};
	uint32_t id;
	int ret = 0;

	trace("[%s] %s\n", ifname, __func__);

	/* Get id from ap name */
	if (hostapd)
		snprintf(name, sizeof(name), "wifi.ap.%s", ifname);
	else
		snprintf(name, sizeof(name), "wifi.bsta.%s", ifname);

	ret = ubus_lookup_id(ubus_ctx, name, &id);
	if (ret != UBUS_STATUS_OK) {
		warn("%s: fail\n", __func__);
		return -1;
	}

	blob_buf_init(&bb, 0);
	ret = ubus_invoke(ubus_ctx, id, "dpp_stop_listen", bb.head, NULL, NULL, 2 * 1000);

	blob_buf_free(&bb);

	trace("[%s] %s ret %d\n", ifname, __func__, ret);
	return ret;
}
#endif
#endif
