[][MAC80211][hostapd][Add mtk vendor subcmd EDCCA control]

[Description]
Add vendor command to control EDCCA enable/threshold/compensation.

Support three configuration into hostapd configuration file.
1. edcca_enable=0
2. edcca_compensation=5
3. edcca_threhols=xx yy zz

The config "edcca_enable" is used to config the opeation mode of EDCCA.
The value 0 means force disable and 1 means auto mode.
The config "edcca_compensation" is used to adjust the EDCCA threshold.
Please noted that compensate value is only applied for 6g band
interface.
The config "edcca_threshold" is used to config EDCCA threhsold when
interface up.

hostapd_cli command usage:

hostapd_cli -i <interface> raw SET_EDCCA [mode|compensation|threshold]
[value]
* This command is used to configure EDCCA mode, compensatation or
* threshold.

hostapd_cli -i <interface> raw GET_EDCCA [mode|compensation|threshold]
* This command is used to check current EDCCA configuration in hostapd.

[Release-log]
N/A

Change-Id: Iefe6e82a35fb22ad8c1a87bf50bc6ad60d61fa32
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/6756359
diff --git a/autobuild_mac80211_release/package/network/services/hostapd/patches/99912-hostapd-mtk-Support-new-hostapd-configuration-edcca_enable-and-.patch b/autobuild_mac80211_release/package/network/services/hostapd/patches/99912-hostapd-mtk-Support-new-hostapd-configuration-edcca_enable-and-.patch
index be54e06..be526be 100644
--- a/autobuild_mac80211_release/package/network/services/hostapd/patches/99912-hostapd-mtk-Support-new-hostapd-configuration-edcca_enable-and-.patch
+++ b/autobuild_mac80211_release/package/network/services/hostapd/patches/99912-hostapd-mtk-Support-new-hostapd-configuration-edcca_enable-and-.patch
@@ -1,30 +1,44 @@
-From 166ba374624b2aed360427f087c20b8f511fb2e0 Mon Sep 17 00:00:00 2001
+From 17051f7a8b2193e24556e357f3f1665d67a79330 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 99912/99916] Support new hostapd configuration, edcca_enable
- and edcca_compensation
+Subject: [PATCH] Support new hostapd configuration, edcca_enable and
+ edcca_compensation and implement edcca related handlers.
 
 ---
- hostapd/config_file.c             | 20 +++++++++++++++
- src/ap/ap_config.c                |  3 +++
- src/ap/ap_config.h                | 10 ++++++++
- src/ap/ap_drv_ops.c               |  9 +++++++
- src/ap/ap_drv_ops.h               |  2 +-
- src/ap/hostapd.c                  |  3 +++
- src/drivers/driver.h              |  2 ++
- src/drivers/driver_nl80211.c      | 42 +++++++++++++++++++++++++++++++
- src/drivers/driver_nl80211.h      |  1 +
- src/drivers/driver_nl80211_capa.c |  7 ++++++
- 10 files changed, 98 insertions(+), 1 deletion(-)
+ 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 eda9db021..8a21c9698 100644
+index eda9db0..0ee8952 100644
 --- a/hostapd/config_file.c
 +++ b/hostapd/config_file.c
-@@ -4753,6 +4753,26 @@ static int hostapd_config_fill(struct hostapd_config *conf,
+@@ -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) {
@@ -37,7 +51,7 @@
 +	} else if (os_strcmp(buf, "edcca_compensation") == 0) {
 +		int val = atoi(pos);
 +		if (val < EDCCA_MIN_COMPENSATION ||
-+			val > EDCCA_MAX_COMPENSATION) {
++		    val > EDCCA_MAX_COMPENSATION) {
 +			wpa_printf(MSG_ERROR, "Line %d: Invalid compensation"
 +				   " value %d; allowed value %d ~ %d.",
 +				   line, val, EDCCA_MIN_COMPENSATION,
@@ -48,8 +62,158 @@
  	} 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 4a20eb4e1..373879f79 100644
+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)
@@ -62,16 +226,25 @@
  	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 3f68e76d5..3ac2ae070 100644
+index 3f68e76..775c567 100644
 --- a/src/ap/ap_config.h
 +++ b/src/ap/ap_config.h
-@@ -1153,8 +1153,18 @@ struct hostapd_config {
+@@ -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 {
@@ -79,99 +252,173 @@
 +	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 0c7aee276..5d2d79c38 100644
+index 0c7aee2..25e967d 100644
 --- a/src/ap/ap_drv_ops.c
 +++ b/src/ap/ap_drv_ops.c
-@@ -1015,3 +1015,12 @@ int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable)
+@@ -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_threshold(struct hostapd_data *hapd)
++int hostapd_drv_configure_edcca_enable(struct hostapd_data *hapd)
 +{
-+	if (!hapd->driver || !hapd->driver->configure_edcca_threshold)
++	if (!hapd->driver || !hapd->driver->configure_edcca_enable)
 +		return 0;
-+	return hapd->driver->configure_edcca_threshold(hapd->drv_priv,
++	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 b4fb766ee..f8fef1929 100644
+index b4fb766..70a99f4 100644
 --- a/src/ap/ap_drv_ops.h
 +++ b/src/ap/ap_drv_ops.h
-@@ -138,7 +138,7 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd);
+@@ -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_threshold(struct hostapd_data *hapd);
++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 0dd8c13b7..6babb036a 100644
+index 0dd8c13..d05f948 100644
 --- a/src/ap/hostapd.c
 +++ b/src/ap/hostapd.c
-@@ -2295,6 +2295,9 @@ dfs_offload:
+@@ -2295,6 +2295,13 @@ dfs_offload:
  	}
  #endif /* CONFIG_MESH */
  
-+	if (hostapd_drv_configure_edcca_threshold(hapd) < 0)
++	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 1d2b1b265..575448a93 100644
+index 1d2b1b2..3559974 100644
 --- a/src/drivers/driver.h
 +++ b/src/drivers/driver.h
-@@ -4676,6 +4676,8 @@ struct wpa_driver_ops {
+@@ -4676,6 +4676,10 @@ struct wpa_driver_ops {
  			      const u8 *match, size_t match_len,
  			      bool multicast);
  #endif /* CONFIG_TESTING_OPTIONS */
-+	int (*configure_edcca_threshold)(void *priv, const u8 edcca_enable,
++	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 5eba0ea1b..76680dfd3 100644
+index 5eba0ea..9c2782c 100644
 --- a/src/drivers/driver_nl80211.c
 +++ b/src/drivers/driver_nl80211.c
-@@ -35,6 +35,7 @@
+@@ -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 +12369,45 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
+@@ -12368,6 +12370,165 @@ static int testing_nl80211_radio_disable(void *priv, int disabled)
  
  #endif /* CONFIG_TESTING_OPTIONS */
  
-+static int nl80211_configure_edcca_threshold(void *priv,
-+						const u8 edcca_enable,
-+						const s8 edcca_compensation)
++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;
-+	/* Prepare nl80211 cmd */
 +	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");
++			   "nl80211: Driver does not support setting EDCCA enable");
 +		return 0;
 +	}
 +
@@ -191,7 +438,128 @@
 +	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 configure EDCCA. ret=%d (%s) ",
++		wpa_printf(MSG_ERROR, "Failed to get EDCCA configuration. ret=%d (%s)",
 +			   ret, strerror(-ret));
 +	}
 +	return ret;
@@ -200,15 +568,17 @@
  
  const struct wpa_driver_ops wpa_driver_nl80211_ops = {
  	.name = "nl80211",
-@@ -12514,4 +12554,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+@@ -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 6e40d5556..13e5d248c 100644
+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 {
@@ -220,7 +590,7 @@
  	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 7ede0d030..732ae292d 100644
+index 7ede0d0..732ae29 100644
 --- a/src/drivers/driver_nl80211_capa.c
 +++ b/src/drivers/driver_nl80211_capa.c
 @@ -18,6 +18,7 @@
@@ -245,5 +615,5 @@
  
  			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
 -- 
-2.25.1
+2.18.0