[rdkb][common][bsp][Refactor and sync wifi from openwrt]
[Description]
3a2eef0b [MAC80211][Release][Update release note for Filogic 880/860 MLO Beta release]
cfbd2411 [MAC80211][Release][Filogic 880/860 MLO Beta release]
6c180e3f [MAC80211][WiFi7][misc][Add Eagle BE14000 efem default bin]
a55f34db [MAC80211][Release][Prepare for Filogic 880/860 release]
5b45ebca [MAC80211][WiFi7][hostapd][Add puncture bitmap to ucode]
95bbea73 [MAC80211][WiFi6][mt76][Add PID to only report data-frame TX rate]
b15ced26 [MAC80211][WiFi6][hostapd][Fix DFS channel selection issue]
d59133cb [MAC80211][WiFi6][mt76][Fix pse info not correct information]
3921b4b2 [MAC80211][WiFi6][mt76][Fix incomplete QoS-map setting to FW]
4e7690c7 [MAC80211][WiFi6/7][app][Change ATECHANNEL mapping cmd]
eb37af90 [MAC80211][WiFi7][app][Add support for per-packet bw & primary selection]
0ea82adf [MAC80211][WiFi6][core][Fix DFS CAC issue after CSA]
[Release-log]
Change-Id: I9bec97ec1b2e1c49ed43a812a07a5b21fcbb70a6
diff --git a/recipes-wifi/hostapd/files/patches-2.10.3/0121-mtk-hostapd-Add-Triggered-Uplink-Access-Optimization.patch b/recipes-wifi/hostapd/files/patches-2.10.3/0121-mtk-hostapd-Add-Triggered-Uplink-Access-Optimization.patch
new file mode 100644
index 0000000..567d0d3
--- /dev/null
+++ b/recipes-wifi/hostapd/files/patches-2.10.3/0121-mtk-hostapd-Add-Triggered-Uplink-Access-Optimization.patch
@@ -0,0 +1,651 @@
+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
+