blob: 567d0d3dfa1e24370357eed0ad13a33bce9c8b25 [file] [log] [blame]
From d612e6c3981212812e87374f90b7be07edf9bc2a Mon Sep 17 00:00:00 2001
From: Howard Hsu <howard-yh.hsu@mediatek.com>
Date: Tue, 2 Jul 2024 09:46:26 +0800
Subject: [PATCH 121/126] mtk: hostapd: Add Triggered Uplink Access
Optimization support
Add TUAO feature support.
Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
---
hostapd/Makefile | 1 +
src/ap/Makefile | 3 +-
src/ap/ap_drv_ops.c | 13 ++
src/ap/ap_drv_ops.h | 5 +
src/ap/ieee802_11.c | 6 +
src/ap/scs.c | 247 ++++++++++++++++++++++++++++++
src/ap/scs.h | 30 ++++
src/ap/sta_info.h | 5 +
src/common/ieee802_11_defs.h | 5 +
src/common/mtk_vendor.h | 17 ++
src/drivers/driver.h | 26 ++++
src/drivers/driver_nl80211.c | 62 ++++++++
src/drivers/driver_nl80211.h | 1 +
src/drivers/driver_nl80211_capa.c | 3 +
14 files changed, 423 insertions(+), 1 deletion(-)
create mode 100644 src/ap/scs.c
create mode 100644 src/ap/scs.h
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 8dc6e6216..233176ae5 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -382,6 +382,7 @@ ifdef CONFIG_IEEE80211BE
CONFIG_IEEE80211AX=y
CFLAGS += -DCONFIG_IEEE80211BE
OBJS += ../src/ap/ieee802_11_eht.o
+OBJS += ../src/ap/scs.o
endif
ifdef CONFIG_IEEE80211AX
diff --git a/src/ap/Makefile b/src/ap/Makefile
index a1e9b7c44..49c6d4a13 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -55,6 +55,7 @@ LIB_OBJS= \
wpa_auth_glue.o \
wpa_auth_ie.o \
wps_hostapd.o \
- x_snoop.o
+ x_snoop.o \
+ scs.o
include ../lib.rules
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 2f53c518f..64fd0c04c 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -23,6 +23,10 @@
#include "wpa_auth.h"
#include "ap_drv_ops.h"
+#ifdef CONFIG_IEEE80211BE
+#include "scs.h"
+#endif
+
u32 hostapd_sta_flags_to_drv(u32 flags)
{
@@ -1531,3 +1535,12 @@ int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf)
return 0;
return hapd->driver->csi_dump(hapd->drv_priv, hapd->iconf->band_idx, dump_buf);
}
+
+#ifdef CONFIG_IEEE80211BE
+int hostapd_drv_set_scs(struct hostapd_data *hapd, struct hostapd_scs_desc_info *info)
+{
+ if (!hapd->driver || !hapd->driver->set_scs)
+ return 0;
+ return hapd->driver->set_scs(hapd->drv_priv, info, hapd->mld_link_id);
+}
+#endif
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index a8c60b6f6..3e5743754 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -190,6 +190,11 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
u8 qos_map_set_len);
+#ifdef CONFIG_IEEE80211BE
+int hostapd_drv_set_scs(struct hostapd_data *hapd,
+ struct hostapd_scs_desc_info *info);
+#endif
+
void hostapd_get_ext_capa(struct hostapd_iface *iface);
void hostapd_get_mld_capa(struct hostapd_iface *iface);
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 886a21a66..09aa1141b 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -62,6 +62,9 @@
#ifdef CONFIG_APUP
# include "apup.h"
#endif // def CONFIG_APUP
+#ifdef CONFIG_IEEE80211BE
+#include "scs.h"
+#endif
#ifdef CONFIG_FILS
static struct wpabuf *
@@ -6247,6 +6250,9 @@ static int handle_action(struct hostapd_data *hapd,
hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
return 1;
#endif /* CONFIG_NO_RRM */
+ case WLAN_ACTION_ROBUST_AV_STREAMING:
+ hostapd_handle_scs(hapd, (const u8 *) mgmt, len);
+ return 1;
}
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
diff --git a/src/ap/scs.c b/src/ap/scs.c
new file mode 100644
index 000000000..6f3c0444e
--- /dev/null
+++ b/src/ap/scs.c
@@ -0,0 +1,247 @@
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "scs.h"
+
+static bool hostapd_find_scs_session(struct sta_info *sta, u8 scsid,
+ u8 *session_idx)
+{
+ u8 idx;
+
+ for (idx = 0; idx < SCS_MAX_CFG_CNT; idx++) {
+ if (sta->scs_session[idx].scs_id == scsid) {
+ *session_idx = idx;
+ return sta->scs_session[idx].alive;
+ }
+ }
+
+ return false;
+}
+
+static int hostapd_find_available_scs_session(struct sta_info *sta)
+{
+ u8 idx;
+
+ for (idx = 0; idx < SCS_MAX_CFG_CNT; idx++) {
+ if (!sta->scs_session[idx].alive)
+ return idx;
+ }
+
+ return -1;
+}
+
+static bool hostapd_parse_qos_char_element(const struct element *elem,
+ struct hostapd_scs_desc_info *info)
+{
+#define SCS_DIRECTION_UPLINK 0
+ u8 id_extension = elem->data[0];
+ u32 control_info;
+
+ info->qos_ie_len = elem->datalen + 2;
+
+ if (id_extension != WLAN_EID_EXT_QOS_CHARACTERISTICS ||
+ info->qos_ie_len > sizeof(info->qos_ie))
+ return false;
+
+ control_info = WPA_GET_LE32(&elem->data[1]);
+ info->dir = control_info & 0x3;
+
+ /* Only support Uplink direction SCS request now. */
+ if (info->dir != SCS_DIRECTION_UPLINK)
+ return false;
+
+ os_memcpy(info->qos_ie, elem, info->qos_ie_len);
+
+ return true;
+}
+
+static u16 hostapd_process_scs_descriptor(struct hostapd_data *hapd,
+ struct sta_info *sta, const u8 *payload,
+ u8 scs_desc_len,
+ struct hostapd_scs_desc_info *info)
+{
+ bool scs_avail, qos_char_elem_avail = false;
+ const struct element *elem;
+ u8 session_idx;
+ int ret;
+
+ scs_avail = hostapd_find_scs_session(sta, info->id, &session_idx);
+
+ switch (info->req_type) {
+ case SCS_REQ_TYPE_ADD:
+ case SCS_REQ_TYPE_CHANGE:
+ if ((info->req_type == SCS_REQ_TYPE_ADD && scs_avail) ||
+ (info->req_type == SCS_REQ_TYPE_CHANGE && !scs_avail))
+ goto decline;
+
+ if (info->req_type == SCS_REQ_TYPE_ADD) {
+ session_idx = hostapd_find_available_scs_session(sta);
+ if (session_idx == -1) {
+ wpa_printf(MSG_ERROR, "%s: Out of SCS resource.\n",
+ __func__);
+ goto decline;
+ }
+ }
+
+ for_each_element(elem, payload + 2, scs_desc_len - 2) {
+ switch (elem->id) {
+ case WLAN_EID_EXTENSION:
+ qos_char_elem_avail =
+ hostapd_parse_qos_char_element(elem, info);
+ break;
+ default:
+ /* The rest elements would be ignored now. */
+ break;
+ }
+ }
+
+ if (!qos_char_elem_avail) {
+ wpa_printf(MSG_ERROR, "%s: The content of QoS Charactristics"
+ " element is empty or not supported yet!\n",
+ __func__);
+ goto decline;
+ }
+
+ break;
+ case SCS_REQ_TYPE_REMOVE:
+ if (!scs_avail)
+ goto decline;
+
+ break;
+ default:
+ goto decline;
+ }
+
+ ret = hostapd_drv_set_scs(hapd, info);
+ if (ret)
+ goto decline;
+
+ sta->scs_session[session_idx].scs_id = info->id;
+ sta->scs_session[session_idx].alive =
+ info->req_type == SCS_REQ_TYPE_REMOVE ? false : true;
+
+ return (info->req_type == SCS_REQ_TYPE_REMOVE) ?
+ SCS_REQ_TCLAS_PROCESSING_TERMINATED : SCS_REQ_SUCCESS;
+
+decline:
+ wpa_printf(MSG_ERROR, "%s: Decline Request Type %d\n",
+ __func__, info->req_type);
+
+ return SCS_REQ_DECLINED;
+}
+
+static void send_scs_response(struct hostapd_data *hapd,
+ struct scs_status_duple *scs_status, const u8 *da,
+ u8 dialog_token, u8 count)
+{
+ struct wpabuf *buf;
+ size_t len;
+ u8 i;
+
+ if (count == 0)
+ return;
+
+ /* Reference to 802_11be_D5.0 Figure 9-1183 */
+ len = 4 + count * sizeof(struct scs_status_duple);
+ buf = wpabuf_alloc(len);
+ if (buf == NULL)
+ return;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
+ wpabuf_put_u8(buf, ROBUST_AV_SCS_RESP);
+ wpabuf_put_u8(buf, dialog_token);
+ wpabuf_put_u8(buf, count);
+
+ for (i = 0; i < count && i < SCS_MAX_CFG_CNT; i++) {
+ wpabuf_put_u8(buf, scs_status[i].scs_id);
+ wpabuf_put_le16(buf, scs_status[i].status);
+ }
+
+ len = wpabuf_len(buf);
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da,
+ wpabuf_head(buf), len);
+ wpabuf_free(buf);
+}
+
+static void hostapd_handle_scs_req(struct hostapd_data *hapd,
+ const u8 *buf, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+ struct hostapd_scs_desc_info info;
+ struct sta_info *sta;
+ struct scs_status_duple scs_status_list[SCS_MAX_CFG_CNT];
+ const u8 *pos, *end;
+ u8 token, index = 0;
+ const struct element *elem;
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+
+ if (!sta) {
+ wpa_printf(MSG_ERROR, "Station " MACSTR " not found "
+ "for SCS Request frame\n", MAC2STR(mgmt->sa));
+ return;
+ }
+
+ token = mgmt->u.action.u.scs.dialog_token;
+ pos = mgmt->u.action.u.scs.variable;
+
+ end = buf + len;
+ len = end - pos;
+
+ for_each_element(elem, pos, len) {
+ if (elem->id != WLAN_EID_SCS_DESCRIPTOR) {
+ wpa_printf(MSG_ERROR, "%s: no scs elem %d in scs req frame!\n",
+ __func__, WLAN_EID_SCS_DESCRIPTOR);
+ break;
+ }
+
+ info.id = elem->data[0];
+ if (!info.id) {
+ wpa_printf(MSG_ERROR, "%s: SCSID = 0 is invalid\n", __func__);
+ break;
+ }
+
+ info.req_type = elem->data[1];
+ os_memcpy(info.peer_addr, mgmt->sa, ETH_ALEN);
+ scs_status_list[index].scs_id = info.id;
+ scs_status_list[index].status =
+ hostapd_process_scs_descriptor(hapd, sta, elem->data,
+ elem->datalen, &info);
+ index++;
+ }
+
+ send_scs_response(hapd, scs_status_list, mgmt->sa, token, index);
+}
+
+void hostapd_handle_scs(struct hostapd_data *hapd, const u8 *buf, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+
+ /*
+ * Check for enough bytes: header + (1B)Category + (1B)Action +
+ * (1B)Dialog Token.
+ */
+ if (len < IEEE80211_HDRLEN + 3) {
+ wpa_printf(MSG_ERROR, "%s SCS frame len %lu is not enough!",
+ __func__, len);
+ return;
+ }
+
+ switch (mgmt->u.action.u.scs.action) {
+ case ROBUST_AV_SCS_REQ:
+ hostapd_handle_scs_req(hapd, buf, len);
+ break;
+ case ROBUST_AV_SCS_RESP:
+ /* Not supported yet. */
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/ap/scs.h b/src/ap/scs.h
new file mode 100644
index 000000000..b69190b65
--- /dev/null
+++ b/src/ap/scs.h
@@ -0,0 +1,30 @@
+#ifndef SCS_H
+#define SCS_H
+
+struct hostapd_data;
+
+/* Only support TUAO certification */
+#define SCS_MAX_CFG_CNT 2
+
+struct scs_status_duple {
+ u8 scs_id;
+ u16 status;
+};
+
+struct scs_session_status {
+ u8 scs_id;
+ bool alive;
+};
+
+enum scs_req_type {
+ SCS_REQ_TYPE_ADD,
+ SCS_REQ_TYPE_REMOVE,
+ SCS_REQ_TYPE_CHANGE,
+};
+
+#define SCS_REQ_SUCCESS 0
+#define SCS_REQ_DECLINED 37
+#define SCS_REQ_TCLAS_PROCESSING_TERMINATED 97
+
+void hostapd_handle_scs(struct hostapd_data *hapd, const u8 *buf, size_t len);
+#endif
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 60b33f049..339adf987 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -18,6 +18,10 @@
#include "crypto/sha384.h"
#include "pasn/pasn_common.h"
+#ifdef CONFIG_IEEE80211BE
+#include "scs.h"
+#endif
+
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
#define WLAN_STA_ASSOC BIT(1)
@@ -335,6 +339,7 @@ struct sta_info {
struct mld_info mld_info;
u8 mld_assoc_link_id;
struct sta_info *mld_assoc_sta;
+ struct scs_session_status scs_session[SCS_MAX_CFG_CNT];
#endif /* CONFIG_IEEE80211BE */
u16 max_idle_period; /* if nonzero, the granted BSS max idle period in
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index afcc2f861..4754d5a69 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1196,6 +1196,11 @@ struct ieee80211_mgmt {
u8 action;
u8 variable[];
} STRUCT_PACKED eht_prot;
+ struct {
+ u8 action;
+ u8 dialog_token;
+ u8 variable[];
+ } STRUCT_PACKED scs;
} u;
} STRUCT_PACKED action;
} u;
diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
index 6d75d39c2..1fe459126 100644
--- a/src/common/mtk_vendor.h
+++ b/src/common/mtk_vendor.h
@@ -20,6 +20,7 @@ enum mtk_nl80211_vendor_subcmds {
MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL = 0xce,
+ MTK_NL80211_VENDOR_SUBCMD_SCS_CTRL = 0xd0,
MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
};
@@ -325,6 +326,22 @@ enum mtk_vendor_attr_txpower_ctrl {
NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL - 1
};
+enum mtk_vendor_attr_scs_ctrl {
+ MTK_VENDOR_ATTR_SCS_CTRL_UNSPEC,
+
+ MTK_VENDOR_ATTR_SCS_ID,
+ MTK_VENDOR_ATTR_SCS_REQ_TYPE,
+ MTK_VENDOR_ATTR_SCS_DIR,
+ MTK_VENDOR_ATTR_SCS_QOS_IE,
+ MTK_VENDOR_ATTR_SCS_MAC_ADDR,
+ MTK_VENDOR_ATTR_SCS_LINK_ID,
+
+ /* keep last */
+ NUM_MTK_VENDOR_ATTRS_SCS_CTRL,
+ MTK_VENDOR_ATTR_SCS_CTRL_MAX =
+ NUM_MTK_VENDOR_ATTRS_SCS_CTRL - 1
+};
+
#define CSI_BW20_DATA_COUNT 64
#define CSI_BW40_DATA_COUNT 128
#define CSI_BW80_DATA_COUNT 256
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 55f62f537..f3de2b3bc 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -70,6 +70,25 @@ enum hostapd_chan_width_attr {
#define HOSTAPD_DFS_REGION_ETSI 2
#define HOSTAPD_DFS_REGION_JP 3
+/**
+ * struct hostapd_scs_desc_info - SCS Req information
+ * @id: SCSID of each SCS stream
+ * @req_type: request type in SCS Descriptor element
+ * @dir: Direction in the control info of QoS Characteristics element
+ * @peer_addr: the mac addr of SCS requester station
+ * @qos_ie: QoS Characteristics IE in SCS Descriptor element
+ * @qos_ie_len: the length of QoS Characteristics element
+ */
+#define EID_EXT_QOS_CHAR_MAX_SIZE 44
+struct hostapd_scs_desc_info {
+ u8 id;
+ u8 req_type;
+ u8 dir;
+ u8 peer_addr[ETH_ALEN];
+ u8 qos_ie[EID_EXT_QOS_CHAR_MAX_SIZE];
+ u8 qos_ie_len;
+};
+
/**
* enum reg_change_initiator - Regulatory change initiator
*/
@@ -5424,6 +5443,13 @@ struct wpa_driver_ops {
int (*pp_mode_set)(void *priv, const u8 pp_mode, s8 link_id, u16 punct_bitmap);
#ifdef CONFIG_IEEE80211BE
int (*get_mld_addr)(void *priv, u8 *addr);
+ /**
+ * set_scs - Configure Stream Classification Service
+ * @priv: Private driver interface data
+ * @info: Stream classidication service configuration
+ * @link_id: MLD link id
+ */
+ int (*set_scs)(void *priv, struct hostapd_scs_desc_info *info, u8 link_id);
#endif
/**
* csi_set - Set csi related mode and parameter
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 395bb5926..d2064fbd6 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -42,6 +42,10 @@
#include "common/mtk_vendor.h"
#include "ap/ap_config.h"
+#ifdef CONFIG_IEEE80211BE
+#include "ap/scs.h"
+#endif
+
#ifndef NETLINK_CAP_ACK
#define NETLINK_CAP_ACK 10
@@ -2932,6 +2936,9 @@ static int nl80211_action_subscribe_ap(struct i802_bss *bss)
/* Protected EHT */
if (nl80211_register_action_frame(bss, (u8 *) "\x25", 1) < 0)
ret = -1;
+ /* Robust AV SCS Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x13\x00", 2) < 0)
+ ret = -1;
/* Vendor-specific */
if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0)
ret = -1;
@@ -15387,6 +15394,60 @@ static int nl80211_set_eml_omn(void *priv, u8 link_id, u8 *addr,
return ret;
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+static int
+nl80211_set_scs(void *priv, struct hostapd_scs_desc_info *info, u8 link_id)
+{
+ 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_scs_vendor_cmd_avail) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Driver does not support scs");
+ 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_SCS_CTRL))
+ goto fail;
+
+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!data)
+ goto fail;
+
+ if (nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_ID, info->id) ||
+ nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_REQ_TYPE, info->req_type) ||
+ nla_put(msg, MTK_VENDOR_ATTR_SCS_MAC_ADDR, ETH_ALEN, info->peer_addr) ||
+ nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_LINK_ID, link_id))
+ goto fail;
+
+ if (info->req_type == SCS_REQ_TYPE_ADD ||
+ info->req_type == SCS_REQ_TYPE_CHANGE)
+ if (nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_DIR, info->dir) ||
+ nla_put(msg, MTK_VENDOR_ATTR_SCS_QOS_IE, info->qos_ie_len,
+ info->qos_ie))
+ goto fail;
+
+ nla_nest_end(msg, data);
+ ret = send_and_recv_cmd(drv, msg);
+ if (ret)
+ wpa_printf(MSG_ERROR, "Failed to set scs. ret = %d (%s)",
+ ret, strerror(-ret));
+
+ return ret;
+
fail:
nlmsg_free(msg);
return ret;
@@ -15830,6 +15891,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.pp_mode_set = nl80211_pp_mode_set,
#ifdef CONFIG_IEEE80211BE
.get_mld_addr = nl80211_get_mld_addr,
+ .set_scs = nl80211_set_scs,
#endif
.csi_set = nl80211_csi_set,
.csi_dump = nl80211_csi_dump,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index d32c0ff4d..87ade150b 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -215,6 +215,7 @@ struct wpa_driver_nl80211_data {
unsigned int mtk_csi_vendor_cmd_avail:1;
unsigned int mtk_eml_vendor_cmd_avail:1;
unsigned int mtk_txpower_vendor_cmd_avail:1;
+ unsigned int mtk_scs_vendor_cmd_avail:1;
u32 ignore_next_local_disconnect;
u32 ignore_next_local_deauth;
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index fc7f9062f..5db6bf6e6 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -1182,6 +1182,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
case MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL:
drv->mtk_txpower_vendor_cmd_avail = 1;
break;
+ case MTK_NL80211_VENDOR_SUBCMD_SCS_CTRL:
+ drv->mtk_scs_vendor_cmd_avail = 1;
+ break;
}
}
--
2.18.0