[][MAC80211][misc][rework hostapd and mac80211 patches]

[Description]
Fix missing subject of "mtk" in hostapd and mac80211 patches.

[Release-log]
N/A

iChange-Id: Ifcb9794269732be3a27c9b7cf031f0e0c3547064
Change-Id: I43591694c8dabec9f9bd8cf55e9017d62f09f8c4
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/6836847
diff --git a/autobuild_mac80211_release/package/network/services/hostapd/patches/99904-hostapd-mtk-Support-EDCCA-hostapd-configuration.patch b/autobuild_mac80211_release/package/network/services/hostapd/patches/99904-hostapd-mtk-Support-EDCCA-hostapd-configuration.patch
new file mode 100644
index 0000000..40dded6
--- /dev/null
+++ b/autobuild_mac80211_release/package/network/services/hostapd/patches/99904-hostapd-mtk-Support-EDCCA-hostapd-configuration.patch
@@ -0,0 +1,619 @@
+From cef7f515eafeeaa99933cc9e66c79b705e3ab065 Mon Sep 17 00:00:00 2001
+From: Howard Hsu <howard-yh.hsu@mediatek.com>
+Date: Mon, 30 May 2022 16:31:34 +0800
+Subject: [PATCH 99904/99909] hostapd: mtk: Support EDCCA hostapd configuration
+
+edcca_enable and edcca_compensation and implement edcca related handlers.
+---
+ hostapd/config_file.c             |  32 ++++++
+ hostapd/ctrl_iface.c              | 125 ++++++++++++++++++++++
+ src/ap/ap_config.c                |   4 +
+ src/ap/ap_config.h                |  29 ++++++
+ src/ap/ap_drv_ops.c               |  24 +++++
+ src/ap/ap_drv_ops.h               |   5 +-
+ src/ap/hostapd.c                  |   7 ++
+ src/common/mtk_vendor.h           |  19 ++--
+ src/drivers/driver.h              |   4 +
+ src/drivers/driver_nl80211.c      | 165 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   7 ++
+ 12 files changed, 415 insertions(+), 7 deletions(-)
+
+diff --git a/hostapd/config_file.c b/hostapd/config_file.c
+index eda9db0..0ee8952 100644
+--- a/hostapd/config_file.c
++++ b/hostapd/config_file.c
+@@ -4753,6 +4753,38 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+ 	} else if (os_strcmp(buf, "eht_mu_beamformer") == 0) {
+ 		conf->eht_phy_capab.mu_beamformer = atoi(pos);
+ #endif /* CONFIG_IEEE80211BE */
++	} else if (os_strcmp(buf, "edcca_threshold") == 0) {
++		if (hostapd_parse_intlist(&conf->edcca_threshold, pos) ||
++		    conf->edcca_threshold[0] < EDCCA_MIN_CONFIG_THRES ||
++		    conf->edcca_threshold[0] > EDCCA_MAX_CONFIG_THRES ||
++		    conf->edcca_threshold[1] < EDCCA_MIN_CONFIG_THRES ||
++		    conf->edcca_threshold[1] > EDCCA_MAX_CONFIG_THRES ||
++		    conf->edcca_threshold[2] < EDCCA_MIN_CONFIG_THRES ||
++		    conf->edcca_threshold[2] > EDCCA_MAX_CONFIG_THRES) {
++			wpa_printf(MSG_ERROR, "Line %d: invalid edcca threshold",
++				   line);
++			return 1;
++		}
++	} else if (os_strcmp(buf, "edcca_enable") == 0) {
++		int mode = atoi(pos);
++		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++			wpa_printf(MSG_ERROR, "Line %d: Invalid edcca_enable %d;"
++				  " allowed value 0 (Force Disable) or 1(Auto) ",
++				   line, mode);
++			return 1;
++		}
++		conf->edcca_enable = (u8) mode;
++	} else if (os_strcmp(buf, "edcca_compensation") == 0) {
++		int val = atoi(pos);
++		if (val < EDCCA_MIN_COMPENSATION ||
++		    val > EDCCA_MAX_COMPENSATION) {
++			wpa_printf(MSG_ERROR, "Line %d: Invalid compensation"
++				   " value %d; allowed value %d ~ %d.",
++				   line, val, EDCCA_MIN_COMPENSATION,
++				   EDCCA_MAX_COMPENSATION);
++			return 1;
++		}
++		conf->edcca_compensation = (s8) val;
+ 	} else {
+ 		wpa_printf(MSG_ERROR,
+ 			   "Line %d: unknown configuration item '%s'",
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index bb8c74f..9c70d54 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -598,6 +598,19 @@ static const char * pbc_status_str(enum pbc_status status)
+ }
+ 
+ 
++static const char * edcca_mode_str(enum edcca_mode status)
++{
++	switch (status) {
++		case EDCCA_MODE_FORCE_DISABLE:
++			return "Force Disable";
++		case EDCCA_MODE_AUTO:
++			return "Auto";
++		default:
++			return "Unknown";
++	}
++}
++
++
+ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
+ 					     char *buf, size_t buflen)
+ {
+@@ -3322,6 +3335,112 @@ static int hostapd_ctrl_iface_driver_cmd(struct hostapd_data *hapd, char *cmd,
+ #endif /* ANDROID */
+ 
+ 
++static int
++hostapd_ctrl_iface_set_edcca(struct hostapd_data *hapd, char *cmd,
++					 char *buf, size_t buflen)
++{
++	char *pos, *config, *value;
++	config = cmd;
++	pos = os_strchr(config, ' ');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if(pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strcmp(config, "enable") == 0) {
++		int mode = atoi(value);
++		if (mode < EDCCA_MODE_FORCE_DISABLE || mode > EDCCA_MODE_AUTO) {
++			wpa_printf(MSG_ERROR, "Invalid value for edcca enable");
++			return -1;
++		}
++		hapd->iconf->edcca_enable = (u8) mode;
++		if (hostapd_drv_configure_edcca_enable(hapd) != 0)
++			return -1;
++	} else if (os_strcmp(config, "compensation") == 0) {
++		int compensation = atoi(value);
++		if (compensation < EDCCA_MIN_COMPENSATION ||
++		    compensation > EDCCA_MAX_COMPENSATION) {
++			wpa_printf(MSG_ERROR, "Invalid value for edcca compensation");
++			return -1;
++		}
++		hapd->iconf->edcca_compensation = (s8) compensation;
++		if (hostapd_drv_configure_edcca_enable(hapd) != 0)
++			return -1;
++	} else if (os_strcmp(config, "threshold") == 0) {
++		char *thres_value;
++		thres_value = os_strchr(value, ':');
++		if (thres_value == NULL)
++			return -1;
++		*thres_value++ = '\0';
++
++		if(thres_value == NULL)
++			return -1;
++		int bw_idx= atoi(value);
++		int threshold = atoi(thres_value);
++
++		if (bw_idx < EDCCA_BW_20 || bw_idx > EDCCA_BW_80) {
++			wpa_printf(MSG_ERROR,
++				   "Unsupported Bandwidth idx %d for SET_EDCCA",
++				   bw_idx);
++			return -1;
++		}
++		if (threshold < EDCCA_MIN_CONFIG_THRES ||
++		    threshold > EDCCA_MAX_CONFIG_THRES) {
++			wpa_printf(MSG_ERROR,
++				   "Unsupported threshold %d for SET_EDCCA",
++				   threshold);
++			return -1;
++		}
++
++		int threshold_arr[EDCCA_MAX_BW_NUM];
++		/* 0x7f means keep the origival value in firmware */
++		os_memset(threshold_arr, 0x7f, sizeof(threshold_arr));
++		threshold_arr[bw_idx] = threshold;
++
++		if (hostapd_drv_configure_edcca_threshold(hapd, threshold_arr) != 0)
++			return -1;
++	} else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for SET_EDCCA", config);
++		return -1;
++	}
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++
++static int
++hostapd_ctrl_iface_get_edcca(struct hostapd_data *hapd, char *cmd, char *buf,
++			     size_t buflen)
++{
++	char *pos, *end;
++
++	pos = buf;
++	end = buf + buflen;
++	u8 value[EDCCA_MAX_BW_NUM] = {0};
++
++	if (os_strcmp(cmd, "enable") == 0) {
++		return os_snprintf(pos, end - pos, "Enable: %s\n",
++				   edcca_mode_str(hapd->iconf->edcca_enable));
++	} else if (os_strcmp(cmd, "compensation") == 0) {
++		return os_snprintf(pos, end - pos, "Compensation: %d\n",
++				  hapd->iconf->edcca_compensation);
++	} else if (os_strcmp(cmd, "threshold") == 0) {
++		if (hostapd_drv_get_edcca(hapd, EDCCA_CTRL_GET_THRES, &value) != 0)
++			return -1;
++		return os_snprintf(pos, end - pos,
++				   "Threshold BW20: 0x%x, BW40: 0x%x, BW80: 0x%x\n",
++				   value[0], value[1], value[2]);
++	} else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for GET_EDCCA", cmd);
++		return -1;
++	}
++}
++
++
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+ 					      int reply_size,
+@@ -3868,6 +3987,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_driver_cmd(hapd, buf + 7, reply,
+ 							  reply_size);
+ #endif /* ANDROID */
++	} else if (os_strncmp(buf, "SET_EDCCA ", 10) == 0) {
++		reply_len = hostapd_ctrl_iface_set_edcca(hapd, buf+10, reply,
++							  reply_size);
++	} else if (os_strncmp(buf, "GET_EDCCA ", 10) == 0) {
++		reply_len = hostapd_ctrl_iface_get_edcca(hapd, buf+10, reply,
++							  reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
+index 4a20eb4..344585a 100644
+--- a/src/ap/ap_config.c
++++ b/src/ap/ap_config.c
+@@ -294,6 +294,9 @@ struct hostapd_config * hostapd_config_defaults(void)
+ 	conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
+ #endif /* CONFIG_AIRTIME_POLICY */
+ 
++	conf->edcca_enable = EDCCA_MODE_AUTO;
++	conf->edcca_compensation = EDCCA_DEFAULT_COMPENSATION;
++
+ 	return conf;
+ }
+ 
+@@ -1007,6 +1010,7 @@ void hostapd_config_free(struct hostapd_config *conf)
+ #ifdef CONFIG_ACS
+ 	os_free(conf->acs_chan_bias);
+ #endif /* CONFIG_ACS */
++	os_free(conf->edcca_threshold);
+ 	wpabuf_free(conf->lci);
+ 	wpabuf_free(conf->civic);
+ 
+diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
+index 3f68e76..775c567 100644
+--- a/src/ap/ap_config.h
++++ b/src/ap/ap_config.h
+@@ -1153,8 +1153,37 @@ struct hostapd_config {
+ #define CH_SWITCH_EHT_ENABLED BIT(0)
+ #define CH_SWITCH_EHT_DISABLED BIT(1)
+ 	unsigned int ch_switch_eht_config;
++	u8 edcca_enable;
++	s8 edcca_compensation;
++	int *edcca_threshold;
+ };
+ 
++enum edcca_mode {
++	EDCCA_MODE_FORCE_DISABLE = 0,
++	EDCCA_MODE_AUTO = 1,
++};
++
++enum edcca_bw_id {
++	EDCCA_BW_20 = 0,
++	EDCCA_BW_40,
++	EDCCA_BW_80,
++	EDCCA_MAX_BW_NUM,
++};
++
++enum mtk_vendor_attr_edcca_ctrl_mode {
++	EDCCA_CTRL_SET_EN = 0,
++	EDCCA_CTRL_SET_THRES,
++	EDCCA_CTRL_GET_EN,
++	EDCCA_CTRL_GET_THRES,
++	EDCCA_CTRL_NUM,
++};
++
++#define EDCCA_DEFAULT_COMPENSATION -6
++#define EDCCA_MIN_COMPENSATION -126
++#define EDCCA_MAX_COMPENSATION 126
++#define EDCCA_MIN_CONFIG_THRES -126
++#define EDCCA_MAX_CONFIG_THRES 0
++
+ 
+ static inline enum oper_chan_width
+ hostapd_get_oper_chwidth(struct hostapd_config *conf)
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 0c7aee2..25e967d 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1015,3 +1015,27 @@ int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable)
+ 		return 0;
+ 	return hapd->driver->dpp_listen(hapd->drv_priv, enable);
+ }
++
++int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd)
++{
++	if (!hapd->driver || !hapd->driver->configure_edcca_enable)
++		return 0;
++	return hapd->driver->configure_edcca_enable(hapd->drv_priv,
++				hapd->iconf->edcca_enable,
++				hapd->iconf->edcca_compensation);
++}
++
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
++					  const int *threshold)
++{
++	if (!hapd->driver || !hapd->driver->configure_edcca_threshold)
++		return 0;
++	return hapd->driver->configure_edcca_threshold(hapd->drv_priv, threshold);
++}
++
++int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value)
++{
++	if (!hapd->driver || !hapd->driver->get_edcca)
++		return 0;
++	return hapd->driver->get_edcca(hapd->drv_priv, mode, value);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index b4fb766..70a99f4 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -138,7 +138,10 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd);
+ int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
+ 			     u16 reason_code, const u8 *ie, size_t ielen);
+ int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable);
+-
++int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd);
++int hostapd_drv_configure_edcca_threshold(struct hostapd_data *hapd,
++					  const int *threshold);
++int hostapd_drv_get_edcca(struct hostapd_data *hapd, const u8 mode, u8 *value);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index 0dd8c13..d05f948 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -2295,6 +2295,13 @@ dfs_offload:
+ 	}
+ #endif /* CONFIG_MESH */
+ 
++	if (hostapd_drv_configure_edcca_enable(hapd) < 0)
++		goto fail;
++
++	if (hostapd_drv_configure_edcca_threshold(hapd,
++						  hapd->iconf->edcca_threshold) < 0)
++		goto fail;
++
+ 	wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+ 		   iface->bss[0]->conf->iface);
+ 	if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index 528387f..7056126 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -29,14 +29,21 @@ enum mtk_vendor_attr_edcca_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL - 1
+ };
+ 
+-enum mtk_vendor_attr_edcca_ctrl_mode {
+-	EDCCA_CTRL_SET_EN = 0,
+-	EDCCA_CTRL_SET_THERS,
+-	EDCCA_CTRL_GET_EN,
+-	EDCCA_CTRL_GET_THERS,
+-	EDCCA_CTRL_NUM,
++enum mtk_vendor_attr_edcca_dump {
++	MTK_VENDOR_ATTR_EDCCA_DUMP_UNSPEC = 0,
++
++	MTK_VENDOR_ATTR_EDCCA_DUMP_MODE,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL,
++
++	/* keep last */
++	NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP,
++	MTK_VENDOR_ATTR_EDCCA_DUMP_MAX =
++		NUM_MTK_VENDOR_ATTRS_EDCCA_DUMP - 1
+ };
+ 
++
+ static struct nla_policy edcca_ctrl_policy[NUM_MTK_VENDOR_ATTRS_EDCCA_CTRL] = {
+ 	[MTK_VENDOR_ATTR_EDCCA_CTRL_MODE] = { .type = NLA_U8 },
+ 	[MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL] = { .type = NLA_U8 },
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 1d2b1b2..3559974 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -4676,6 +4676,10 @@ struct wpa_driver_ops {
+ 			      const u8 *match, size_t match_len,
+ 			      bool multicast);
+ #endif /* CONFIG_TESTING_OPTIONS */
++	int (*configure_edcca_enable)(void *priv, const u8 edcca_enable,
++				  const s8 edcca_compensation);
++	int (*configure_edcca_threshold)(void *priv, const int *threshold);
++	int (*get_edcca)(void *priv, const u8 mode, u8 *value);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 5eba0ea..9c2782c 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -35,6 +35,8 @@
+ #include "radiotap_iter.h"
+ #include "rfkill.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
++#include "ap/ap_config.h"
+ 
+ 
+ #ifndef NETLINK_CAP_ACK
+@@ -12368,6 +12370,165 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
+ 
+ #endif /* CONFIG_TESTING_OPTIONS */
+ 
++static int nl80211_configure_edcca_enable(void *priv,
++					  const u8 edcca_enable,
++					  const s8 edcca_compensation)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_edcca_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting EDCCA enable");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_EN) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, edcca_enable) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE,
++		edcca_compensation)) {
++		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to configure EDCCA enable. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
++static int nl80211_configure_edcca_threshold(void *priv, const int *threshold)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_edcca_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting EDCCA threshold");
++		return 0;
++	}
++
++	if (!threshold) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Input EDCCA threshold is empty!");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, EDCCA_CTRL_SET_THRES) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_PRI20_VAL, threshold[0] & 0xff) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL, threshold[1] & 0xff) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL, threshold[2] & 0xff)) {
++		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to configure EDCCA threshold. ret=%d (%s) ",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
++
++static int edcca_info_handler(struct nl_msg *msg, void *arg)
++{
++	u8 *info = (u8*) arg;
++	struct nlattr *tb[NL80211_ATTR_MAX + 1];
++	struct nlattr *tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_MAX + 1];
++	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++	struct nlattr *nl_vend, *attr;
++
++	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++		  genlmsg_attrlen(gnlh, 0), NULL);
++
++	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
++	if (!nl_vend)
++		return NL_SKIP;
++
++	nla_parse(tb_vendor, MTK_VENDOR_ATTR_EDCCA_DUMP_MAX,
++		  nla_data(nl_vend), nla_len(nl_vend), NULL);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_PRI20_VAL");
++		return NL_SKIP;
++	}
++
++	*info++ = nla_get_u8(attr);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC40_VAL");
++		return NL_SKIP;
++	}
++
++	*info++ = nla_get_u8(attr);
++
++	attr = tb_vendor[MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL];
++	if (!attr) {
++		wpa_printf(MSG_ERROR, "nl80211: MTK_VENDOR_ATTR_EDCCA_DUMP_SEC80_VAL");
++		return NL_SKIP;
++	}
++
++	*info = nla_get_u8(attr);
++	return NL_SKIP;
++}
++
++
++static int nl80211_get_edcca(void *priv, const u8 mode, u8 *value)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data;
++	int ret;
++
++	if (!drv->mtk_edcca_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting EDCCA threshold");
++		return 0;
++	}
++
++	if (!(msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR)) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
++			MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL) ||
++	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED)) ||
++	    nla_put_u8(msg, MTK_VENDOR_ATTR_EDCCA_CTRL_MODE, mode)) {
++		wpa_printf (MSG_ERROR, "Prepare nl80211 msg fail");
++		nlmsg_free(msg);
++		return -ENOBUFS;
++	}
++	nla_nest_end(msg, data);
++	ret = send_and_recv_msgs(drv, msg, edcca_info_handler, value, NULL, NULL);
++	if (ret) {
++		wpa_printf(MSG_ERROR, "Failed to get EDCCA configuration. ret=%d (%s)",
++			   ret, strerror(-ret));
++	}
++	return ret;
++}
++
+ 
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+@@ -12514,4 +12675,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.register_frame = testing_nl80211_register_frame,
+ 	.radio_disable = testing_nl80211_radio_disable,
+ #endif /* CONFIG_TESTING_OPTIONS */
++/* Need ifdef CONFIG_DRIVER_NL80211_MTK */
++	.configure_edcca_enable = nl80211_configure_edcca_enable,
++	.configure_edcca_threshold = nl80211_configure_edcca_threshold,
++	.get_edcca = nl80211_get_edcca,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 6e40d55..13e5d24 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -181,6 +181,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int qca_do_acs:1;
+ 	unsigned int brcm_do_acs:1;
+ 	unsigned int uses_6ghz:1;
++	unsigned int mtk_edcca_vendor_cmd_avail:1;
+ 
+ 	u64 vendor_scan_cookie;
+ 	u64 remain_on_chan_cookie;
+diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
+index 7ede0d0..732ae29 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -18,6 +18,7 @@
+ #include "common/qca-vendor-attr.h"
+ #include "common/brcm_vendor.h"
+ #include "driver_nl80211.h"
++#include "common/mtk_vendor.h"
+ 
+ 
+ static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+@@ -1050,6 +1051,12 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 					break;
+ 				}
+ #endif /* CONFIG_DRIVER_NL80211_BRCM */
++			} else if (vinfo->vendor_id == OUI_MTK) {
++				switch (vinfo->subcmd) {
++				case MTK_NL80211_VENDOR_SUBCMD_EDCCA_CTRL :
++					drv->mtk_edcca_vendor_cmd_avail = 1;
++					break;
++				}
+ 			}
+ 
+ 			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
+-- 
+2.36.1
+