#if (EASYMESH_VERSION >= 6)


#include "mld.h"

#include "agent_ubus.h"
#include "utils/debug.h"
#include "utils/utils.h"
#include "backhaul.h"
#include "agent.h"

struct sta_mld *stamld_update(struct agent *a, struct netif_ap *ap, uint8_t *mld_macaddr,
		   uint8_t *mld_bssid, uint8_t mlo_link_id, struct sta *s)
{
	struct sta_mld *mld;
	int ret;

	mld = agent_get_stamld(a, mld_macaddr);
	if (!mld) {
		struct netif_mld *apmld;

		apmld = agent_get_apmld(a, ap->mld_macaddr);
		if (!apmld)
			return NULL;

		mld = agent_alloc_stamld(a, apmld, mld_macaddr);
		if (!mld)
			return NULL;
	}

	ret = mld_add_sta(a, mld, s->macaddr);
	if (ret) {
		agnt_dbg(LOG_MLO, "%s: failed to add affiliated STA:"MACFMT" to STAMLD:"MACFMT"\n", __func__, MAC2STR(s->macaddr), MAC2STR(mld_macaddr));
		return NULL;
	}

	memcpy(mld->bssid, mld_bssid, 6);

	memcpy(s->mld_macaddr, mld->macaddr, 6);
	s->is_affiliated_sta = true;
	s->mlo_link_id = mlo_link_id;
	return mld;
}

int mld_del_sta(struct agent *a, struct sta *s)
{
	struct sta_mld *mld;
	int i;

	mld = agent_get_stamld(a, s->mld_macaddr);
	if (!mld)
		return -1;

	for (i = 0; i < mld->num_affiliated_sta; i++) {
		if (memcmp(mld->affiliated_sta[i], s->macaddr, 6))
			continue;

		if (i < mld->num_affiliated_sta)
			memcpy(mld->affiliated_sta[i], mld->affiliated_sta[i + 1], (mld->num_affiliated_sta - (i + 1)) * 6);

		memset(mld->affiliated_sta[mld->num_affiliated_sta - 1], 0, 6);
		mld->num_affiliated_sta--;
		break;
	}

	if (mld->num_affiliated_sta == 0) {
		list_del(&mld->list);
		free(mld);
	}

	return 0;
}

int mld_del_bsta(struct agent *a, struct netif_bk *bk)
{
	struct bsta_mld *mld;
	int i;

	if (!a->bstamld || bk->cfg->is_mld_netdev)
		return -1;

	mld = a->bstamld;
	for (i = 0; i < mld->num_affiliated_bsta; i++) {
		if (memcmp(mld->affiliated_bsta[i], bk->macaddr, 6))
			continue;

		if (i < mld->num_affiliated_bsta)
			memcpy(mld->affiliated_bsta[i], mld->affiliated_bsta[i + 1], (mld->num_affiliated_bsta - (i + 1)) * 6);

		memset(mld->affiliated_bsta[mld->num_affiliated_bsta - 1], 0, 6);
		mld->num_affiliated_bsta--;
	}

	bk->is_affiliated_sta = false;
	return 0;
}

int mld_del_ap(struct agent *a, struct netif_ap *ap)
{
	struct netif_mld *mld;
	int i;

	if (!ap->is_affiliated_ap)
		return -1;

	mld = agent_get_apmld(a, ap->mld_macaddr);
	if (!mld)
		return -1;

	for (i = 0; i < mld->num_affiliated_ap; i++) {
		if (memcmp(mld->affiliated_ap[i], ap->bssid, 6))
			continue;

		if (i < mld->num_affiliated_ap)
			memcpy(mld->affiliated_ap[i], mld->affiliated_ap[i + 1], (mld->num_affiliated_ap - (i + 1)) * 6);

		memset(mld->affiliated_ap[mld->num_affiliated_ap - 1], 0, 6);
		mld->num_affiliated_ap--;
	}

	ap->is_affiliated_ap = false;
	return 0;
}

int wifi_process_ml_ie(struct agent *a, struct sta_mld *stamld,
		       uint8_t *assocframe, int framelen)
{
	uint8_t *ml;
	uint8_t *ml_ctrl;
	uint8_t type;
	uint8_t presence_bmp[2];

	bool linkid_info_present;
	bool bss_param_chg_present;
	bool medium_sync_present;
	bool eml_caps_present;

	bool mld_caps_ops_present;
	uint8_t *common_info;
	size_t pos;

	ml = wifi_find_ie_ext(assocframe, framelen, IE_EXT_ML);
	if (!ml) {
		agnt_dbg(LOG_MLO, "%s: frame did not include ML IE\n", __func__);
		return -1;
	}

	ml_ctrl = ml + 3;
	type = ml_ctrl[0] & 0x7;
	presence_bmp[0] = (ml_ctrl[0] & 0xf0) >> 4;
	presence_bmp[1] = ml_ctrl[1];

	if (type != 0) {
		agnt_dbg(LOG_MLO, "%s: ML IE type non-zero\n", __func__);
		return -1;
	}

	linkid_info_present = !!(presence_bmp[0] & 0x1);
	bss_param_chg_present = !!(presence_bmp[0] & 0x2);
	medium_sync_present = !!(presence_bmp[0] & 0x4);
	eml_caps_present = !!(presence_bmp[0] & 0x8);

	mld_caps_ops_present = !!(presence_bmp[1] & 0x1);
	common_info = &ml_ctrl[2];
	pos = 1;

	pos += 6; /* skip apmld macaddr */

	if (linkid_info_present)
		pos += 1;

	if (bss_param_chg_present)
		pos += 1;

	if (medium_sync_present)
		pos += 2;

	if (eml_caps_present) {
		uint8_t *eml_caps = &common_info[pos];

		stamld->emlsr_enabled = !!(eml_caps[0] & BIT(0)) ? true : false;
		stamld->emlmr_enabled = !!(eml_caps[0] & BIT(7)) ? true : false;
		pos += 2;
	}

	if (mld_caps_ops_present) {
		uint8_t *mld_caps = &common_info[pos];
		uint8_t max_simlinks = (mld_caps[0] & 0x0f);

		pos += 2;
		if (max_simlinks >= 1) {
			stamld->nstr_enabled = false;
			stamld->str_enabled = true;
		} else {
			stamld->nstr_enabled = true;
			stamld->str_enabled = false;
		}
	}

	return 0;
}

void mld_event_handler(void *agent, struct blob_attr *msg)
{
	char ifname[16] = {0}, event[16] = {0};
	struct agent *a = (struct agent *) agent;
	struct blob_attr *tb[3];
	static const struct blobmsg_policy ev_attr[3] = {
		[0] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
		[1] = { .name = "event", .type = BLOBMSG_TYPE_STRING },
		[2] = { .name = "data", .type = BLOBMSG_TYPE_TABLE },
	};
	bool add, del;

	blobmsg_parse(ev_attr, 3, tb, blob_data(msg), blob_len(msg));

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

	strncpy(ifname,	blobmsg_data(tb[0]), sizeof(ifname) - 1);
	strncpy(event, blobmsg_data(tb[1]), sizeof(event) - 1);

	add = !strcmp(event, "mlo-link-added");
	del = !strcmp(event, "mlo-link-remove");

	if (add)
		timer_set(&a->init_ifaces_scheduler, IFACE_TIMEOUT * 1000);
	else if (del)
		timer_set(&a->init_ifaces_scheduler, IFACE_TIMEOUT * 1000);
}

void apmld_refresh_stations(struct agent *a, struct netif_mld *mld)
{
	int i;

	for (i = 0; i < mld->num_affiliated_ap; i++) {
		struct wifi_mlsta mlsta[128] = {0};
		int num_mlsta = 128;
		struct netif_ap *ap;

		ap = agent_get_ap(a, mld->affiliated_ap[i]);
		if (!ap)
			continue;

		if (!wifi_get_mld_stations(ap, mlsta, &num_mlsta)) {
			int j;

			for (j = 0; j < num_mlsta; j++) {
				if (!memcmp(mlsta[j].sta->bssid, ap->bssid, 6))
					wifi_add_sta(a, ap->ifname, mlsta[j].sta->macaddr);
			}
		}
	}
}

int mld_parse_affiliated_ap(struct ubus_request *req, int type,
		struct blob_attr *msg)
{
	struct netif_ap *ap = (struct netif_ap *) req->priv;
	static const struct blobmsg_policy ap_attr[] = {
		[0] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
		[1] = { .name = "band", .type = BLOBMSG_TYPE_STRING },
		[2] = { .name = "channel", .type = BLOBMSG_TYPE_INT32 },
		[3] = { .name = "ccfs0", .type = BLOBMSG_TYPE_INT32 },
		[4] = { .name = "ccfs1", .type = BLOBMSG_TYPE_INT32 },
		[5] = { .name = "bandwidth", .type = BLOBMSG_TYPE_INT32 },
		[6] = { .name = "macaddr", .type = BLOBMSG_TYPE_STRING },
		[7] = { .name = "security", .type = BLOBMSG_TYPE_STRING },
		[8] = { .name = "encryption", .type = BLOBMSG_TYPE_STRING },
		[9] = { .name = "standard", .type = BLOBMSG_TYPE_STRING },
		[10] = { .name = "num_stations", .type = BLOBMSG_TYPE_INT32 },
		[11] = { .name = "max_stations", .type = BLOBMSG_TYPE_INT32 },
		[12] = { .name = "utilization", .type = BLOBMSG_TYPE_INT32 },
		[13] = { .name = "adm_capacity", .type = BLOBMSG_TYPE_INT32 },
		[14] = { .name = "hidden", .type = BLOBMSG_TYPE_BOOL },
		[15] = { .name = "supp_security", .type = BLOBMSG_TYPE_ARRAY },
		[16] = { .name = "capabilities", .type = BLOBMSG_TYPE_TABLE },
		[17] = { .name = "enabled", .type = BLOBMSG_TYPE_BOOL },
	};
	struct blob_attr *tb[ARRAY_SIZE(ap_attr)];
	enum wifi_band band;

	if (!ap)
		return -1;

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

	if (!tb[0] || !tb[1] || !tb[6]) {
		return -1;
	}

	band = wifi_bandstr_to_band(blobmsg_get_string(tb[1]));
	if (band != ap->band)
		return -1;

	ap->mlo_link_id = blobmsg_get_u32(tb[0]);

	hwaddr_aton(blobmsg_data(tb[6]), ap->bssid);
	if (tb[2])
		ap->channel = blobmsg_get_u32(tb[2]);

	if (tb[5])
		ap->bandwidth = blobmsg_get_u32(tb[5]);

	if (tb[9])
		strncpy(ap->standard, blobmsg_data(tb[9]), sizeof(ap->standard) - 1);

	if (tb[10])
		ap->num_sta = blobmsg_get_u32(tb[10]);

	ap->enabled = tb[17] ? blobmsg_get_bool(tb[17]) : false;

	return 0;
}

void parse_apmld_stats(struct ubus_request *req, int type,
		       struct blob_attr *msg)
{
	static const struct blobmsg_policy apmld_stats_attr[] = {
		[0] = { .name = "stats", .type = BLOBMSG_TYPE_TABLE},
	};
	struct blob_attr *tb[ARRAY_SIZE(apmld_stats_attr)];

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

	if (!tb[0])
		return;

	parse_ap_stats(req, type, tb[0]);
}

void apmld_parse(struct ubus_request *req, int type,
		struct blob_attr *msg)
{
	static const struct blobmsg_policy ap_attr[] = {
		[0] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
		[1] = { .name = "macaddr", .type = BLOBMSG_TYPE_STRING },
		[2] = { .name = "ssid", .type = BLOBMSG_TYPE_STRING },
		[3] = { .name = "mode", .type = BLOBMSG_TYPE_STRING },
		[4] = { .name = "links", .type = BLOBMSG_TYPE_ARRAY },
		[5] = { .name = "bssid", .type = BLOBMSG_TYPE_STRING },
	};
	struct netif_ap *ap = (struct netif_ap *)req->priv;
	struct blob_attr *tb[ARRAY_SIZE(ap_attr)];
	struct agent *a = ap->agent;
	struct netif_mld *mld;
	const char *ifname;
	const char *ssid;
	uint8_t bssid[6] = {0};

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

	if (!tb[0] || !tb[1] || !tb[2] || !tb[3] || !tb[4] || !tb[5]) {
		agnt_warn(LOG_MLO, "%s: UBUS output missing some mandatory field!\n", __func__);
		return;
	}

	ifname = blobmsg_get_string(tb[0]);
	if (strlen(ifname) == 0)
		return;

	mld = agent_get_apmld_by_ifname(a, ifname);
	if (!mld) {
		mld = agent_alloc_apmld(a, ifname);
		if (!mld) {
			err("%s: failed to alloc MLD\n", __func__);
			return;
		}
	}

	if (!hwaddr_aton(blobmsg_data(tb[1]), mld->macaddr)) {
		agnt_warn(LOG_MLO, "%s: MLD macaddr not valid\n", __func__);
		return;
	}

	if (!hwaddr_aton(blobmsg_data(tb[5]), bssid)) {
		agnt_warn(LOG_MLO, "%s: MLD bssid not valid\n", __func__);
		return;
	}

	/* In case of absence mld interface in the system, macaddres will be zero, use bssid */
	if (hwaddr_is_zero(mld->macaddr) && !hwaddr_is_zero(bssid))
		memcpy(mld->macaddr, bssid, 6);

	ssid = blobmsg_get_string(tb[2]);
	strncpy(mld->ssid, ssid, sizeof(mld->ssid));

	if (tb[4]) {
		struct blob_attr *link = NULL;
		int rem_links = 0;

		blobmsg_for_each_attr(link, tb[4], rem_links) {
			struct wifi_radio_element *re = NULL;
			struct wifi7_radio_capabilities *caps;
			int num_ap = 0;
			int ret;

			if (blobmsg_type(link) != BLOBMSG_TYPE_TABLE) {
				agnt_warn(LOG_MLO, "%s: link was not a table\n", __func__);
				continue;
			}

			ret = mld_parse_affiliated_ap(req, type, link);
			if (ret)
				continue;

			strncpy(ap->mld_ifname, ifname, sizeof(ap->mld_ifname) - 1);
			memcpy(ap->mld_macaddr, mld->macaddr, 6);
			ap->is_affiliated_ap = true;
			strncpy(ap->ssid, ssid, sizeof(ap->ssid));

			num_ap = mld->num_affiliated_ap;
			if (!mld_contains_ap(a, mld, ap->bssid) &&
			    num_ap < MAX_AFFILIATED_AP_LINKS_NUM) {
				memcpy(mld->affiliated_ap[num_ap], ap->bssid, 6);
				mld->num_affiliated_ap++;
			} else {
				agnt_dbg(LOG_MLO, "%s: link with bssid:"MACFMT \
					 " was already in the MLD (or num_ap"\
					 " too high: (curr: %d max:%d)\n",
					 __func__, MAC2STR(ap->bssid),
					 mld->num_affiliated_ap,
					 MAX_AFFILIATED_AP_LINKS_NUM);
			}

			re = agent_get_radio_with_ifname(a, ap->ifname);
			if (re) {
				caps = &re->wifi7_caps;
				if (caps) {
					/* TODO: revisit: */
					/* Enable based on capabilities */
					if (caps->ap_str_support)
						mld->ap_str_enabled = true;
					if (caps->ap_nstr_support)
						mld->ap_nstr_enabled = true;
					if (caps->ap_emlsr_support)
						mld->ap_emlsr_enabled = true;
					if (caps->ap_emlmr_support)
						mld->ap_emlmr_enabled = true;
				}
			}
			break;
		}

		if (mld->num_affiliated_ap == 0) {
			agnt_info(LOG_MLO, "%s: no affiliated APs found in MLD, schedule reload\n", __func__);
			timer_set(&a->init_ifaces_scheduler, IFACE_TIMEOUT * 1000);
		}

	}
}

void mld_parse_affiliated_bsta(struct agent *a, struct bsta_mld *mld,
				 struct netif_bk *bk, int type,
				 struct blob_attr *msg)
{

	struct blob_attr *link;
	int rem_links;

	blobmsg_for_each_attr(link, msg, rem_links) {
		static const struct blobmsg_policy mlo_links[] = {
			[0] = { .name = "mlo_link_id", .type = BLOBMSG_TYPE_INT32 },
			[1] = {	.name = "macaddr", .type = BLOBMSG_TYPE_STRING },
			[2] = { .name = "bssid", .type = BLOBMSG_TYPE_STRING },
			[3] = { .name = "band", .type = BLOBMSG_TYPE_STRING },
			[4] = { .name = "frequency", .type = BLOBMSG_TYPE_INT32 },
			[5] = { .name = "setup_link", .type = BLOBMSG_TYPE_BOOL },
		};
		struct blob_attr *data[ARRAY_SIZE(mlo_links)];
		uint8_t link_id;
		int num_sta;
		enum wifi_band band;

		blobmsg_parse(mlo_links, ARRAY_SIZE(mlo_links), data,
			blobmsg_data(msg), blobmsg_data_len(msg));

		if (!data[0] || !data[1] || !data[2] || !data[3] || !data[4] || !data[5]) {
			agnt_warn(LOG_MLO, "%s: missing mlo_links data for ifname:%s\n", __func__, bk->ifname);
			continue;
		}


		link_id = (uint8_t) blobmsg_get_u32(data[0]);

		band = wifi_bandstr_to_band(blobmsg_get_string(data[3]));
		if (band != bk->cfg->band)
			continue;

		hwaddr_aton(blobmsg_data(data[1]), bk->macaddr);
		hwaddr_aton(blobmsg_data(data[2]), bk->bssid);

		num_sta = mld->num_affiliated_bsta;
		memcpy(bk->mld_macaddr, mld->macaddr, 6);
		bk->is_affiliated_sta = true;
		bk->mlo_link_id = link_id;

		if (blobmsg_get_bool(data[5]))
			a->bstamld->setup_link = bk;

		if (!mld_contains_bsta(a, mld, bk->macaddr) &&
		    num_sta < MAX_AFFILIATED_STA_LINKS_NUM) {
			memcpy(mld->affiliated_bsta[num_sta], bk->macaddr, 6);
			mld->num_affiliated_bsta++;
		}

		break;
	}
}

void bstamld_parse(struct ubus_request *req, int type,
		struct blob_attr *msg)
{
	static const struct blobmsg_policy ap_attr[] = {
		[0] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
		[1] = { .name = "status", .type = BLOBMSG_TYPE_STRING },
		[2] = { .name = "macaddr", .type = BLOBMSG_TYPE_STRING },
		[3] = { .name = "bssid", .type = BLOBMSG_TYPE_STRING },
		[4] = { .name = "ssid", .type = BLOBMSG_TYPE_STRING },
		[5] = { .name = "4addr", .type = BLOBMSG_TYPE_BOOL },
		[6] = { .name = "mlo_links", .type = BLOBMSG_TYPE_ARRAY },
	};
	struct netif_bk *bk = (struct netif_bk *)req->priv;
	struct blob_attr *tb[ARRAY_SIZE(ap_attr)];
	struct agent *a = bk->agent;
	uint8_t macaddr[6] = {0};
	const char *ssid = NULL;
	uint8_t bssid[6] = {0};
	const char *ifname;

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

	if (!a->bstamld) {
		agnt_warn(LOG_MLO, "%s: BSTAMLD is not yet allocated\n", __func__);
		return;
	}

	if (!tb[0] || !tb[1] || !tb[2] || !tb[5]) {
		agnt_warn(LOG_MLO, "%s: missing some mandatory field(s)!\n", __func__);
		return;
	}

	if (tb[4])
		ssid = blobmsg_get_string(tb[4]);

	if (!hwaddr_aton(blobmsg_data(tb[2]), macaddr)) {
		agnt_warn(LOG_MLO, "%s: MLD macaddr not valid\n", __func__);
		return;
	}

	if (tb[3] && !hwaddr_aton(blobmsg_data(tb[3]), bssid)) {
		agnt_warn(LOG_MLO, "%s: MLD bssid not valid\n", __func__);
		return;
	}

	ifname = blobmsg_get_string(tb[0]);

	memcpy(a->bstamld->macaddr, macaddr, 6);
	memcpy(a->bstamld->bssid, bssid, 6);

	if (bk->cfg->is_mld_netdev && !hwaddr_is_zero(bssid)) {
		memcpy(bk->mld_macaddr, a->bstamld->macaddr, 6);
		memcpy(bk->macaddr, macaddr, 6);
		memcpy(bk->bssid, bssid, 6);
	}

	if (tb[6] && !bk->cfg->is_mld_netdev) {
		struct blob_attr *link = NULL;
		int rem_links = 0;

		blobmsg_for_each_attr(link, tb[6], rem_links) {
			if (blobmsg_type(link) != BLOBMSG_TYPE_TABLE) {
				agnt_dbg(LOG_MLO, "%s: link was not a table\n", __func__);
				continue;
			}

			mld_parse_affiliated_bsta(a, a->bstamld, bk, type, link);
			strncpy(bk->mld_ifname, ifname, sizeof(bk->mld_ifname) - 1);
			memcpy(bk->mld_macaddr, a->bstamld->macaddr, 6);
			bk->is_affiliated_sta = true;
			if (ssid)
				strncpy(bk->ssid, ssid, sizeof(bk->ssid));
			bk->enabled = true;
		}
	}

	if (bk->cfg->is_mld_netdev || !a->cfg.physical_mld)
		bk_toggle(a, bk, bssid, 0);
}

struct netif_mld *agent_get_apmld(struct agent *a, uint8_t *macaddr)
{
	struct netif_mld *mld = NULL;

	list_for_each_entry(mld, &a->apmldlist, list) {
		if (!memcmp(macaddr, mld->macaddr, 6))
			return mld;
	}

	return NULL;
}

struct netif_mld *agent_get_apmld_by_id(struct agent *a, uint8_t id)
{
	struct netif_mld *mld = NULL;

	list_for_each_entry(mld, &a->apmldlist, list) {
		if (mld->id == id)
			return mld;
	}

	return NULL;
}

struct netif_mld *agent_get_apmld_by_ifname(struct agent *a, const char *ifname)
{
	struct netif_mld *mld = NULL;

	list_for_each_entry(mld, &a->apmldlist, list) {
		if (!strncmp(ifname, mld->ifname, sizeof(mld->ifname)))
			return mld;
	}

	return NULL;
}

struct netif_mld *agent_alloc_apmld(struct agent *a, const char *ifname)
{
	char objname[32] = {0};
	struct netif_mld *mld;

	if (strlen(ifname) == 0) {
		agnt_warn(LOG_MLO, "%s: MLD can not have empty ifname\n", __func__);
		return NULL;
	}

	mld = calloc(1, sizeof(*mld));
	if (!mld)
		return NULL;

	agnt_dbg(LOG_MLO, "%s: allocated mld ifname:%s\n", __func__, ifname);

	INIT_LIST_HEAD(&mld->stamldlist);

	strncpy(mld->ifname, ifname, sizeof(mld->ifname));
	list_add_tail(&mld->list, &a->apmldlist);
	a->num_ap_mld++;
	snprintf(objname, sizeof(objname), "wifi.apmld.%s", ifname);
	mld->obj_id = ubus_get_object(a->ubus_ctx, objname);
	if (mld->obj_id == WIFI_OBJECT_INVALID)
		agnt_warn(LOG_MLO, "%s: object:%s missing over UBUS\n", __func__, objname);

	return mld;
}

int mld_add_ap(struct agent *a, struct netif_mld *mld,
			     uint8_t *macaddr)
{
	int num_ap = mld->num_affiliated_ap;

	if (mld_contains_ap(a, mld, macaddr))
		return 0;

	if (num_ap >= MAX_AFFILIATED_AP_LINKS_NUM)
		return -1;

	memcpy(mld->affiliated_ap[num_ap], macaddr, 6);
	mld->num_affiliated_ap++;
	return 0;
}

int mld_add_sta(struct agent *a, struct sta_mld *mld,
			     uint8_t *macaddr)
{
	int num_sta = mld->num_affiliated_sta;

	if (mld_contains_sta(a, mld, macaddr))
		return 0;

	if (num_sta >= MAX_AFFILIATED_STA_LINKS_NUM)
		return -1;

	memcpy(mld->affiliated_sta[num_sta], macaddr, 6);
	mld->num_affiliated_sta++;
	return 0;
}


int mld_add_bsta(struct agent *a, struct bsta_mld *mld,
			     uint8_t *macaddr)
{
	int num_bsta = mld->num_affiliated_bsta;

	if (mld_contains_bsta(a, mld, macaddr))
		return 0;

	if (num_bsta >= MAX_AFFILIATED_BSTA_LINKS_NUM)
		return -1;

	memcpy(mld->affiliated_bsta[num_bsta], macaddr, 6);
	mld->num_affiliated_bsta++;
	return 0;
}



bool mld_contains_ap(struct agent *a, struct netif_mld *mld,
			     uint8_t *macaddr)
{
	int i;

	for (i = 0; i < mld->num_affiliated_ap; i++) {
		if (!memcmp(mld->affiliated_ap[i], macaddr, 6))
			return true;
	}

	return false;
}

bool mld_contains_sta(struct agent *a, struct sta_mld *mld,
			     uint8_t *macaddr)
{
	int i;

	for (i = 0; i < mld->num_affiliated_sta; i++) {
		if (!memcmp(mld->affiliated_sta[i], macaddr, 6))
			return true;
	}

	return false;
}


bool mld_contains_bsta(struct agent *a, struct bsta_mld *mld,
			     uint8_t *macaddr)
{
	int i;

	for (i = 0; i < mld->num_affiliated_bsta; i++) {
		if (!memcmp(mld->affiliated_bsta[i], macaddr, 6))
			return true;
	}

	return false;
}


struct sta_mld *agent_get_stamld(struct agent *a, uint8_t *macaddr)
{
	struct netif_mld *mld;

	list_for_each_entry(mld, &a->apmldlist, list) {
		struct sta_mld *s;

		list_for_each_entry(s, &mld->stamldlist, list) {
			if (!memcmp(macaddr, s->macaddr, 6))
				return s;
		}
	}

	return NULL;
}

struct sta_mld *agent_alloc_stamld(struct agent *a, struct netif_mld *mld,
				    uint8_t *macaddr)
{
	struct sta_mld *s;

	s = calloc(1, sizeof(*s));
	if (!s)
		return NULL;

	agnt_trace(LOG_MLO, "%s: allocate sta mac:"MACFMT"\n", __func__, MAC2STR(macaddr));
	memcpy(s->macaddr, macaddr, 6);

	list_add_tail(&s->list, &mld->stamldlist);

	return s;
}

struct bsta_mld *agent_alloc_bstamld(struct agent *a, const char *ifname)
{
	struct bsta_mld *bstamld;

	bstamld = calloc(1, sizeof(*bstamld));
	if (!bstamld)
		return NULL;

	strncpy(bstamld->ifname, ifname, sizeof(bstamld->ifname) - 1);
	bstamld->setup_link = NULL;
	agnt_dbg(LOG_MLO, "%s: allocated bsta mld:%s\n", __func__, bstamld->ifname);
	return bstamld;
}

struct bsta_mld *agent_init_bstamld(struct agent *a, char *ifname,
				     struct wifi7_radio_capabilities *caps)
{
	struct bsta_mld *bsta = a->bstamld;

	strncpy(bsta->ifname, ifname, sizeof(bsta->ifname));
	agnt_dbg(LOG_MLO, "%s: allocated bsta mld:%s\n", __func__, bsta->ifname);

	bsta->str_enabled = caps->bsta_str_support;
	bsta->nstr_enabled = caps->bsta_nstr_support;
	bsta->emlsr_enabled = caps->bsta_emlsr_support;
	bsta->emlmr_enabled = caps->bsta_emlmr_support;

	return bsta;
}

void stamld_clean(struct agent *a, struct sta_mld *mld)
{
	int i;

	for (i = (mld->num_affiliated_sta - 1); i >= 0; i--) {
		struct netif_ap *ap;
		struct sta *s;

		s = agent_get_sta(a, mld->affiliated_sta[i]);
		if (!s)
			continue;
		memset(s->mld_macaddr, 0, 6);
		s->is_affiliated_sta = false;

		ap = agent_get_ap(a, s->bssid);
		if (ap)
			wifi_del_sta(a, ap->ifname, s->macaddr);

		memset(mld->affiliated_sta[i], 0, 6);
	}

	mld->num_affiliated_sta = 0;
}


void stamld_free(struct agent *a, struct sta_mld *mld)
{
	stamld_clean(a, mld);

	list_del(&mld->list);
	free(mld);
}

void bstamld_clean(struct agent *a, struct bsta_mld *mld)
{
	int i;

	for (i = 0; i < mld->num_affiliated_bsta; i++) {
		struct netif_bk *bk;

		bk = agent_get_bsta(a, mld->affiliated_bsta[i]);
		if (!bk)
			continue;

		memset(bk->mld_macaddr, 0, 6);
		bk->is_affiliated_sta = false;
		memset(mld->affiliated_bsta[i], 0, 6);
	}

	mld->num_affiliated_bsta = 0;
	mld->setup_link = NULL;  /* Reset setup_link when cleaning */
}


void agent_clear_bsta_mld(struct agent *a)
{
	struct bsta_mld *mld = a->bstamld;

	bstamld_clean(a, mld);
	free(mld);
}

void apmld_clean(struct agent *a, struct netif_mld *mld)
{
	int i;

	for (i = (mld->num_affiliated_ap - 1); i >= 0; i--) {
		struct netif_ap *ap;

		ap = agent_get_ap(a, mld->affiliated_ap[i]);
		if (!ap)
			continue;

		memset(ap->mld_macaddr, 0, 6);
		ap->is_affiliated_ap = false;
		memset(mld->affiliated_ap[i], 0, 6);
	}

	mld->num_affiliated_ap = 0;
}

void apmld_free(struct agent *a, struct netif_mld *mld)
{
	struct sta_mld *smld = NULL, *stmp;

	list_for_each_entry_safe(smld, stmp,  &mld->stamldlist, list)
		stamld_free(a, smld);

	apmld_clean(a, mld);

	list_del(&mld->list);
	free(mld);
}

void apmld_free_all(struct agent *a)
{
	struct netif_mld *mld = NULL, *tmp;

	list_for_each_entry_safe(mld, tmp,  &a->apmldlist, list) {
		apmld_free(a, mld);
	}

	a->num_ap_mld = 0;
}

bool is_affiliated_ap(struct agent *a, uint8_t *bssid)
{
	struct netif_ap *ap;

	ap = agent_get_ap(a, bssid);
	if (ap && ap->is_affiliated_ap)
		return true;

	return false;
}


bool agent_radio_support_ap_mlo(struct wifi7_radio_capabilities *wifi7_caps)
{
	if (wifi7_caps->ap_str_support || wifi7_caps->ap_nstr_support ||
		wifi7_caps->ap_emlsr_support || wifi7_caps->ap_emlmr_support)
		return true;

	return false;
}

bool agent_radio_support_bsta_mlo(struct wifi7_radio_capabilities *wifi7_caps)
{
	if (wifi7_caps->bsta_str_support || wifi7_caps->bsta_nstr_support ||
		wifi7_caps->bsta_emlsr_support || wifi7_caps->bsta_emlmr_support)
		return true;

	return false;
}


int bstamld_teardown_band(struct agent *a, enum wifi_band band)
{
	char fmt[128] = {0};
	const char *bandstr;

	bandstr = band_to_str(band);
	snprintf(fmt, sizeof(fmt), "teardown_bsta_mld %s", bandstr);
	agent_exec_platform_scripts(fmt);

	if (a->bstamld) {
		a->bstamld->bk.cfg->band &= ~band;
		if (a->bstamld->bk.cfg->band == 0) {
			clean_bk(a->bstamld->bk.cfg);
			agent_clear_bsta_mld(a);
		}
	}

	agent_config_reload(a);
	return 0;
}

void mld_set_config_id(char *ifname, uint8_t mld_id)
{
	char mldname[16] = {0};
	char idstr[4] = {0};

	/* write empty string if mld id is not set */
	if (mld_id) {
		snprintf(idstr, sizeof(idstr), "%d", mld_id);
		snprintf(mldname, sizeof(mldname), "mld%d", mld_id);
	}

	uci_set_wireless_interface_option("mapagent", "ap",
				  "ifname", ifname,
				  "mld_id", idstr);

	uci_set_wireless_interface_option("wireless",
				  "wifi-iface",
				  "ifname", ifname,
				  "mld", mldname);

	uci_set_wireless_interface_option("mapagent", "ap",
				  "ifname", ifname,
				  "enabled", mld_id ? "1" : "0");

	uci_set_wireless_interface_option("wireless",
				  "wifi-iface",
				  "ifname", ifname,
				  "disabled", mld_id ? "0" : "1");
}
#endif
