[][MAC80211][hostapd][Add vendor command for certification]

[Description]
Add vendor command for certification

[Release-log]
N/A

Change-Id: I839e8c486c0a7d389251fe940e89124c6a1b117f
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7262688
diff --git a/autobuild_mac80211_release/package/network/services/hostapd/patches/mtk-0026-hostapd-mtk-Add-vendor-for-CAPI-certification-comman.patch b/autobuild_mac80211_release/package/network/services/hostapd/patches/mtk-0026-hostapd-mtk-Add-vendor-for-CAPI-certification-comman.patch
new file mode 100644
index 0000000..5b9f4df
--- /dev/null
+++ b/autobuild_mac80211_release/package/network/services/hostapd/patches/mtk-0026-hostapd-mtk-Add-vendor-for-CAPI-certification-comman.patch
@@ -0,0 +1,527 @@
+From 47b475634804175c77621d492ad6129a31dcda4d Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Fri, 17 Mar 2023 16:17:14 +0800
+Subject: [PATCH] hostapd: mtk: Add vendor for CAPI certification commands
+
+---
+ hostapd/ctrl_iface.c              |  95 +++++++++++++++
+ src/ap/ap_drv_ops.c               |  21 ++++
+ src/ap/ap_drv_ops.h               |   3 +
+ src/common/mtk_vendor.h           |  32 +-----
+ src/drivers/driver.h              |  22 ++++
+ src/drivers/driver_nl80211.c      | 185 ++++++++++++++++++++++++++++++
+ src/drivers/driver_nl80211.h      |   1 +
+ src/drivers/driver_nl80211_capa.c |   3 +
+ 8 files changed, 332 insertions(+), 30 deletions(-)
+
+diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
+index e1e9270..58bb710 100644
+--- a/hostapd/ctrl_iface.c
++++ b/hostapd/ctrl_iface.c
+@@ -38,6 +38,7 @@
+ #endif /* CONFIG_DPP */
+ #include "common/wpa_ctrl.h"
+ #include "common/ptksa_cache.h"
++#include "common/mtk_vendor.h"
+ #include "crypto/tls.h"
+ #include "drivers/driver.h"
+ #include "eapol_auth/eapol_auth_sm.h"
+@@ -3715,6 +3716,96 @@ hostapd_ctrl_iface_get_aval_color_bmp(struct hostapd_data *hapd, char *buf,
+ 	return pos - buf;
+ }
+ 
++static int
++hostapd_ctrl_iface_ap_wireless(struct hostapd_data *hapd, char *cmd,
++					 char *buf, size_t buflen)
++{
++	char *pos, *value, *config = cmd;
++	enum mtk_vendor_attr_wireless_ctrl sub_cmd;
++
++	pos = os_strchr(config, '=');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if(pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strncmp(config, "fixed_mcs", 9) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS;
++	else if (os_strncmp(config, "ofdma", 5) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA;
++	else if (os_strncmp(config, "ppdu_type", 9) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE;
++	else if (os_strncmp(config, "nusers_ofdma", 12) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA;
++	else if (os_strncmp(config, "add_ba_req_bufsize", 18) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE;
++	else if (os_strncmp(config, "mimo", 4) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO;
++	else if (os_strncmp(config, "cert", 4) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT ;
++	else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for ap_wireless", config);
++		return -1;
++	}
++
++	if (hostapd_drv_ap_wireless(hapd, (u8) sub_cmd, atoi(value)) != 0)
++		return -1;
++
++	return os_snprintf(buf, buflen, "OK\n");
++}
++
++static int
++hostapd_ctrl_iface_ap_rfeatures(struct hostapd_data *hapd, char *cmd,
++					 char *buf, size_t buflen)
++{
++	char *pos, *value, *type, *config = cmd;
++	enum mtk_vendor_attr_rfeature_ctrl sub_cmd;
++
++	pos = os_strchr(config, '=');
++	if (pos == NULL)
++		return -1;
++	*pos++ = '\0';
++
++	if(pos == NULL)
++		return -1;
++	value = pos;
++
++	if (os_strncmp(config, "he_gi", 5) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI;
++	else if (os_strncmp(config, "he_ltf", 6) == 0)
++		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF;
++	else if (os_strncmp(config, "trig_type", 9) == 0) {
++		pos = os_strchr(value, ',');
++		if (pos == NULL)
++			return -1;
++		*pos++ = '\0';
++		if(pos == NULL)
++			return -1;
++		type = pos;
++		goto trigtype;
++	} else if (os_strcmp(config, "ack_policy") == 0)
++		sub_cmd = MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY;
++	else {
++		wpa_printf(MSG_ERROR,
++			"Unsupported parameter %s for ap_rfeatures", config);
++		return -1;
++	}
++
++	if (hostapd_drv_ap_rfeatures(hapd, (u8) sub_cmd, atoi(value)) != 0)
++		return -1;
++	goto exit;
++
++trigtype:
++	if (hostapd_drv_ap_trig_type(hapd, atoi(value), atoi(type)) != 0)
++		return -1;
++
++exit:
++	return os_snprintf(buf, buflen, "OK\n");
++}
+ 
+ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 					      char *buf, char *reply,
+@@ -4286,6 +4377,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ 		reply_len = hostapd_ctrl_iface_get_bss_color(hapd, reply, reply_size);
+ 	} else if (os_strncmp(buf, "AVAL_COLOR_BMP", 14) == 0) {
+ 		reply_len = hostapd_ctrl_iface_get_aval_color_bmp(hapd, reply, reply_size);
++	} else if (os_strncmp(buf, "ap_wireless ", 12) == 0) {
++		reply_len = hostapd_ctrl_iface_ap_wireless(hapd, buf + 12, reply, reply_size);
++	} else if (os_strncmp(buf, "ap_rfeatures ", 13) == 0) {
++		reply_len = hostapd_ctrl_iface_ap_rfeatures(hapd, buf + 13, reply, reply_size);
+ 	} else {
+ 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+ 		reply_len = 16;
+diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
+index 40b9a20..52d0fff 100644
+--- a/src/ap/ap_drv_ops.c
++++ b/src/ap/ap_drv_ops.c
+@@ -1100,3 +1100,24 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
+ 		return 0;
+ 	return hapd->driver->get_aval_color_bmp(hapd->drv_priv, aval_color_bmp);
+ }
++
++int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
++{
++	if (!hapd->driver || !hapd->driver->ap_wireless)
++		return 0;
++	return hapd->driver->ap_wireless(hapd->drv_priv, sub_vendor_id, value);
++}
++
++int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
++{
++	if (!hapd->driver || !hapd->driver->ap_rfeatures)
++		return 0;
++	return hapd->driver->ap_rfeatures(hapd->drv_priv, sub_vendor_id, value);
++}
++
++int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type)
++{
++	if (!hapd->driver || !hapd->driver->ap_trigtype)
++		return 0;
++	return hapd->driver->ap_trigtype(hapd->drv_priv, enable, type);
++}
+diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
+index 136a3ac..659c3f8 100644
+--- a/src/ap/ap_drv_ops.h
++++ b/src/ap/ap_drv_ops.h
+@@ -151,6 +151,9 @@ int hostapd_drv_amsdu_ctrl(struct hostapd_data *hapd);
+ int hostapd_drv_amsdu_dump(struct hostapd_data *hapd, u8 *amsdu);
+ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd,
+ 				       u64 *aval_color_bmp);
++int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
++int hostapd_drv_ap_rfeatures(struct hostapd_data *hapd, u8 sub_vendor_id, int value);
++int hostapd_drv_ap_trig_type(struct hostapd_data *hapd, u8 enable, u8 type);
+ 
+ #include "drivers/driver.h"
+ 
+diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
+index a99e6f2..32438af 100644
+--- a/src/common/mtk_vendor.h
++++ b/src/common/mtk_vendor.h
+@@ -48,16 +48,6 @@ enum mtk_vendor_attr_edcca_dump {
+ 		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 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
+-	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_3wire_ctrl {
+ 	MTK_VENDOR_ATTR_3WIRE_CTRL_UNSPEC,
+ 
+@@ -69,10 +59,6 @@ enum mtk_vendor_attr_3wire_ctrl {
+ 		NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL - 1
+ };
+ 
+-static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
+-	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_csi_ctrl {
+ 	MTK_VENDOR_ATTR_CSI_CTRL_UNSPEC,
+ 
+@@ -169,7 +155,7 @@ enum mtk_vendor_attr_wireless_ctrl {
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU,
+-	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT,
++	MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT = 9,
+ 	MTK_VENDOR_ATTR_WIRELESS_CTRL_RTS_SIGTA,
+ 
+ 	/* keep last */
+@@ -189,11 +175,6 @@ enum mtk_vendor_attr_wireless_dump {
+ 		NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP - 1
+ };
+ 
+-static const struct nla_policy
+-wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
+-	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_rfeature_ctrl {
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_UNSPEC,
+ 
+@@ -203,6 +184,7 @@ enum mtk_vendor_attr_rfeature_ctrl {
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN,
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE,
+ 	MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY,
++	MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF,
+ 
+ 	/* keep last */
+ 	NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL,
+@@ -244,16 +226,6 @@ enum mtk_vendor_attr_ibf_dump {
+ 		NUM_MTK_VENDOR_ATTRS_IBF_DUMP - 1
+ };
+ 
+-static struct nla_policy
+-ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
+-	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
+-};
+-
+-static struct nla_policy
+-ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
+-	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
+-};
+-
+ enum mtk_vendor_attr_bss_color_ctrl {
+ 	MTK_VENDOR_ATTR_BSS_COLOR_CTRL_UNSPEC,
+ 
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 7f6392f..d4f34b6 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -4779,6 +4779,28 @@ struct wpa_driver_ops {
+ 	 *
+ 	 */
+ 	int (*get_aval_color_bmp)(void *priv, u64 *aval_color_bmp);
++
++	/**
++	* ap_wireless - set wireless command
++	* @priv: Private driver interface data
++	* @value: value
++	*/
++	int (*ap_wireless)(void *priv, u8 mode, int value);
++
++	/**
++	* ap_rfeatures - set ap rf features command
++	* @priv: Private driver interface data
++	* @value: value
++	*/
++	int (*ap_rfeatures)(void *priv, u8 mode, int value);
++
++	/**
++	* ap_trigtype - set trigger type
++	* @priv: Private driver interface data
++	* @enable: enable or disable
++	* @type: trigger type
++	*/
++	int (*ap_trigtype)(void *priv, u8 enable, u8 type);
+ };
+ 
+ /**
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index f9a8763..9c1717f 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -74,6 +74,57 @@ enum nlmsgerr_attrs {
+ #endif /* ANDROID */
+ 
+ 
++static struct nla_policy
++ibf_ctrl_policy[NUM_MTK_VENDOR_ATTRS_IBF_CTRL] = {
++	[MTK_VENDOR_ATTR_IBF_CTRL_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy
++ibf_dump_policy[NUM_MTK_VENDOR_ATTRS_IBF_DUMP] = {
++	[MTK_VENDOR_ATTR_IBF_DUMP_ENABLE] = { .type = NLA_U8 },
++};
++
++static struct nla_policy three_wire_ctrl_policy[NUM_MTK_VENDOR_ATTRS_3WIRE_CTRL] = {
++	[MTK_VENDOR_ATTR_3WIRE_CTRL_MODE] = {.type = NLA_U8 },
++};
++
++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 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC20_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC40_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_SEC80_VAL] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_EDCCA_CTRL_COMPENSATE] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++wireless_dump_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_DUMP] = {
++	[MTK_VENDOR_ATTR_WIRELESS_DUMP_AMSDU] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++rfeature_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_GI] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_HE_LTF] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG] = { .type = NLA_NESTED },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_ACK_PLCY] = { .type = NLA_U8 },
++	[MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TXBF] = { .type = NLA_U8 },
++};
++
++static const struct nla_policy
++wireless_ctrl_policy[NUM_MTK_VENDOR_ATTRS_WIRELESS_CTRL] = {
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_MCS] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_FIXED_OFDMA] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_PPDU_TX_TYPE] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_NUSERS_OFDMA] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_MIMO] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE] = {.type = NLA_U16 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_AMSDU] = {.type = NLA_U8 },
++	[MTK_VENDOR_ATTR_WIRELESS_CTRL_CERT] = {.type = NLA_U8 },
++};
++
+ static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
+ {
+ 	struct nl_sock *handle;
+@@ -13005,6 +13056,137 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++static int nl80211_ap_wireless(void *priv, u8 sub_vendor_id, int 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_wireless_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting ap wireless control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	if (sub_vendor_id == MTK_VENDOR_ATTR_WIRELESS_CTRL_BA_BUFFER_SIZE)
++		nla_put_u16(msg, sub_vendor_id, (u16) value);
++	else
++		nla_put_u8(msg, sub_vendor_id, (u8) value);
++
++	nla_nest_end(msg, data);
++	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set ap_wireless. ret=%d (%s)", ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
++static int nl80211_ap_rfeatures(void *priv, u8 sub_vendor_id, int 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_rfeatures_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting ap rfeatures control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	nla_put_u8(msg, sub_vendor_id, (u8) value);
++
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set rf_features. ret=%d (%s)", ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
++static int nl80211_ap_trigtype(void *priv, u8 enable, u8 type)
++{
++	struct i802_bss *bss = priv;
++	struct wpa_driver_nl80211_data *drv = bss->drv;
++	struct nl_msg *msg;
++	struct nlattr *data, *data2;
++	int ret;
++
++	if (!drv->mtk_rfeatures_vendor_cmd_avail) {
++		wpa_printf(MSG_INFO,
++			   "nl80211: Driver does not support setting ap rfeatures control");
++		return 0;
++	}
++
++	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
++	if (!msg)
++		goto fail;
++
++	if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
++		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL))
++		goto fail;
++
++	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
++	if (!data)
++		goto fail;
++
++	data2 = nla_nest_start(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_CFG);
++	if (!data2)
++		goto fail;
++
++	nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE_EN, enable);
++	nla_put_u8(msg, MTK_VENDOR_ATTR_RFEATURE_CTRL_TRIG_TYPE, type);
++
++	nla_nest_end(msg, data2);
++	nla_nest_end(msg, data);
++
++	ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
++	if (ret)
++		wpa_printf(MSG_ERROR, "Failed to set trig_type. ret=%d (%s)", ret, strerror(-ret));
++
++	return ret;
++
++fail:
++	nlmsg_free(msg);
++	return -ENOBUFS;
++}
++
+ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.name = "nl80211",
+ 	.desc = "Linux nl80211/cfg80211",
+@@ -13162,4 +13344,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+ 	.amsdu_ctrl = nl80211_enable_amsdu,
+ 	.amsdu_dump = nl80211_dump_amsdu,
+ 	.get_aval_color_bmp = nl80211_get_aval_color_bmp,
++	.ap_wireless = nl80211_ap_wireless,
++	.ap_rfeatures = nl80211_ap_rfeatures,
++	.ap_trigtype = nl80211_ap_trigtype,
+ };
+diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
+index 72c7abd..7a03446 100644
+--- a/src/drivers/driver_nl80211.h
++++ b/src/drivers/driver_nl80211.h
+@@ -187,6 +187,7 @@ struct wpa_driver_nl80211_data {
+ 	unsigned int mtk_ibf_vendor_cmd_avail:1;
+ 	unsigned int mtk_wireless_vendor_cmd_avail:1;
+ 	unsigned int mtk_bss_color_vendor_cmd_avail:1;
++	unsigned int mtk_rfeatures_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 e7c6f39..6f4d029 100644
+--- a/src/drivers/driver_nl80211_capa.c
++++ b/src/drivers/driver_nl80211_capa.c
+@@ -1095,6 +1095,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+ 				case MTK_NL80211_VENDOR_SUBCMD_BSS_COLOR_CTRL :
+ 					drv->mtk_bss_color_vendor_cmd_avail = 1;
+ 					break;
++				case MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL:
++					drv->mtk_rfeatures_vendor_cmd_avail = 1;
++					break;
+ 				}
+ 			}
+ 
+-- 
+2.18.0
+