blob: 567d0d3dfa1e24370357eed0ad13a33bce9c8b25 [file] [log] [blame]
developer05f3b2b2024-08-19 19:17:34 +08001From d612e6c3981212812e87374f90b7be07edf9bc2a Mon Sep 17 00:00:00 2001
2From: Howard Hsu <howard-yh.hsu@mediatek.com>
3Date: Tue, 2 Jul 2024 09:46:26 +0800
4Subject: [PATCH 121/126] mtk: hostapd: Add Triggered Uplink Access
5 Optimization support
6
7Add TUAO feature support.
8
9Signed-off-by: Howard Hsu <howard-yh.hsu@mediatek.com>
10---
11 hostapd/Makefile | 1 +
12 src/ap/Makefile | 3 +-
13 src/ap/ap_drv_ops.c | 13 ++
14 src/ap/ap_drv_ops.h | 5 +
15 src/ap/ieee802_11.c | 6 +
16 src/ap/scs.c | 247 ++++++++++++++++++++++++++++++
17 src/ap/scs.h | 30 ++++
18 src/ap/sta_info.h | 5 +
19 src/common/ieee802_11_defs.h | 5 +
20 src/common/mtk_vendor.h | 17 ++
21 src/drivers/driver.h | 26 ++++
22 src/drivers/driver_nl80211.c | 62 ++++++++
23 src/drivers/driver_nl80211.h | 1 +
24 src/drivers/driver_nl80211_capa.c | 3 +
25 14 files changed, 423 insertions(+), 1 deletion(-)
26 create mode 100644 src/ap/scs.c
27 create mode 100644 src/ap/scs.h
28
29diff --git a/hostapd/Makefile b/hostapd/Makefile
30index 8dc6e6216..233176ae5 100644
31--- a/hostapd/Makefile
32+++ b/hostapd/Makefile
33@@ -382,6 +382,7 @@ ifdef CONFIG_IEEE80211BE
34 CONFIG_IEEE80211AX=y
35 CFLAGS += -DCONFIG_IEEE80211BE
36 OBJS += ../src/ap/ieee802_11_eht.o
37+OBJS += ../src/ap/scs.o
38 endif
39
40 ifdef CONFIG_IEEE80211AX
41diff --git a/src/ap/Makefile b/src/ap/Makefile
42index a1e9b7c44..49c6d4a13 100644
43--- a/src/ap/Makefile
44+++ b/src/ap/Makefile
45@@ -55,6 +55,7 @@ LIB_OBJS= \
46 wpa_auth_glue.o \
47 wpa_auth_ie.o \
48 wps_hostapd.o \
49- x_snoop.o
50+ x_snoop.o \
51+ scs.o
52
53 include ../lib.rules
54diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
55index 2f53c518f..64fd0c04c 100644
56--- a/src/ap/ap_drv_ops.c
57+++ b/src/ap/ap_drv_ops.c
58@@ -23,6 +23,10 @@
59 #include "wpa_auth.h"
60 #include "ap_drv_ops.h"
61
62+#ifdef CONFIG_IEEE80211BE
63+#include "scs.h"
64+#endif
65+
66
67 u32 hostapd_sta_flags_to_drv(u32 flags)
68 {
69@@ -1531,3 +1535,12 @@ int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf)
70 return 0;
71 return hapd->driver->csi_dump(hapd->drv_priv, hapd->iconf->band_idx, dump_buf);
72 }
73+
74+#ifdef CONFIG_IEEE80211BE
75+int hostapd_drv_set_scs(struct hostapd_data *hapd, struct hostapd_scs_desc_info *info)
76+{
77+ if (!hapd->driver || !hapd->driver->set_scs)
78+ return 0;
79+ return hapd->driver->set_scs(hapd->drv_priv, info, hapd->mld_link_id);
80+}
81+#endif
82diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
83index a8c60b6f6..3e5743754 100644
84--- a/src/ap/ap_drv_ops.h
85+++ b/src/ap/ap_drv_ops.h
86@@ -190,6 +190,11 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
87 int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
88 u8 qos_map_set_len);
89
90+#ifdef CONFIG_IEEE80211BE
91+int hostapd_drv_set_scs(struct hostapd_data *hapd,
92+ struct hostapd_scs_desc_info *info);
93+#endif
94+
95 void hostapd_get_ext_capa(struct hostapd_iface *iface);
96 void hostapd_get_mld_capa(struct hostapd_iface *iface);
97
98diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
99index 886a21a66..09aa1141b 100644
100--- a/src/ap/ieee802_11.c
101+++ b/src/ap/ieee802_11.c
102@@ -62,6 +62,9 @@
103 #ifdef CONFIG_APUP
104 # include "apup.h"
105 #endif // def CONFIG_APUP
106+#ifdef CONFIG_IEEE80211BE
107+#include "scs.h"
108+#endif
109
110 #ifdef CONFIG_FILS
111 static struct wpabuf *
112@@ -6247,6 +6250,9 @@ static int handle_action(struct hostapd_data *hapd,
113 hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
114 return 1;
115 #endif /* CONFIG_NO_RRM */
116+ case WLAN_ACTION_ROBUST_AV_STREAMING:
117+ hostapd_handle_scs(hapd, (const u8 *) mgmt, len);
118+ return 1;
119 }
120
121 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
122diff --git a/src/ap/scs.c b/src/ap/scs.c
123new file mode 100644
124index 000000000..6f3c0444e
125--- /dev/null
126+++ b/src/ap/scs.c
127@@ -0,0 +1,247 @@
128+#include "utils/includes.h"
129+
130+#include "utils/common.h"
131+#include "common/ieee802_11_defs.h"
132+#include "common/ieee802_11_common.h"
133+#include "hostapd.h"
134+#include "ieee802_11.h"
135+#include "sta_info.h"
136+#include "ap_config.h"
137+#include "ap_drv_ops.h"
138+#include "scs.h"
139+
140+static bool hostapd_find_scs_session(struct sta_info *sta, u8 scsid,
141+ u8 *session_idx)
142+{
143+ u8 idx;
144+
145+ for (idx = 0; idx < SCS_MAX_CFG_CNT; idx++) {
146+ if (sta->scs_session[idx].scs_id == scsid) {
147+ *session_idx = idx;
148+ return sta->scs_session[idx].alive;
149+ }
150+ }
151+
152+ return false;
153+}
154+
155+static int hostapd_find_available_scs_session(struct sta_info *sta)
156+{
157+ u8 idx;
158+
159+ for (idx = 0; idx < SCS_MAX_CFG_CNT; idx++) {
160+ if (!sta->scs_session[idx].alive)
161+ return idx;
162+ }
163+
164+ return -1;
165+}
166+
167+static bool hostapd_parse_qos_char_element(const struct element *elem,
168+ struct hostapd_scs_desc_info *info)
169+{
170+#define SCS_DIRECTION_UPLINK 0
171+ u8 id_extension = elem->data[0];
172+ u32 control_info;
173+
174+ info->qos_ie_len = elem->datalen + 2;
175+
176+ if (id_extension != WLAN_EID_EXT_QOS_CHARACTERISTICS ||
177+ info->qos_ie_len > sizeof(info->qos_ie))
178+ return false;
179+
180+ control_info = WPA_GET_LE32(&elem->data[1]);
181+ info->dir = control_info & 0x3;
182+
183+ /* Only support Uplink direction SCS request now. */
184+ if (info->dir != SCS_DIRECTION_UPLINK)
185+ return false;
186+
187+ os_memcpy(info->qos_ie, elem, info->qos_ie_len);
188+
189+ return true;
190+}
191+
192+static u16 hostapd_process_scs_descriptor(struct hostapd_data *hapd,
193+ struct sta_info *sta, const u8 *payload,
194+ u8 scs_desc_len,
195+ struct hostapd_scs_desc_info *info)
196+{
197+ bool scs_avail, qos_char_elem_avail = false;
198+ const struct element *elem;
199+ u8 session_idx;
200+ int ret;
201+
202+ scs_avail = hostapd_find_scs_session(sta, info->id, &session_idx);
203+
204+ switch (info->req_type) {
205+ case SCS_REQ_TYPE_ADD:
206+ case SCS_REQ_TYPE_CHANGE:
207+ if ((info->req_type == SCS_REQ_TYPE_ADD && scs_avail) ||
208+ (info->req_type == SCS_REQ_TYPE_CHANGE && !scs_avail))
209+ goto decline;
210+
211+ if (info->req_type == SCS_REQ_TYPE_ADD) {
212+ session_idx = hostapd_find_available_scs_session(sta);
213+ if (session_idx == -1) {
214+ wpa_printf(MSG_ERROR, "%s: Out of SCS resource.\n",
215+ __func__);
216+ goto decline;
217+ }
218+ }
219+
220+ for_each_element(elem, payload + 2, scs_desc_len - 2) {
221+ switch (elem->id) {
222+ case WLAN_EID_EXTENSION:
223+ qos_char_elem_avail =
224+ hostapd_parse_qos_char_element(elem, info);
225+ break;
226+ default:
227+ /* The rest elements would be ignored now. */
228+ break;
229+ }
230+ }
231+
232+ if (!qos_char_elem_avail) {
233+ wpa_printf(MSG_ERROR, "%s: The content of QoS Charactristics"
234+ " element is empty or not supported yet!\n",
235+ __func__);
236+ goto decline;
237+ }
238+
239+ break;
240+ case SCS_REQ_TYPE_REMOVE:
241+ if (!scs_avail)
242+ goto decline;
243+
244+ break;
245+ default:
246+ goto decline;
247+ }
248+
249+ ret = hostapd_drv_set_scs(hapd, info);
250+ if (ret)
251+ goto decline;
252+
253+ sta->scs_session[session_idx].scs_id = info->id;
254+ sta->scs_session[session_idx].alive =
255+ info->req_type == SCS_REQ_TYPE_REMOVE ? false : true;
256+
257+ return (info->req_type == SCS_REQ_TYPE_REMOVE) ?
258+ SCS_REQ_TCLAS_PROCESSING_TERMINATED : SCS_REQ_SUCCESS;
259+
260+decline:
261+ wpa_printf(MSG_ERROR, "%s: Decline Request Type %d\n",
262+ __func__, info->req_type);
263+
264+ return SCS_REQ_DECLINED;
265+}
266+
267+static void send_scs_response(struct hostapd_data *hapd,
268+ struct scs_status_duple *scs_status, const u8 *da,
269+ u8 dialog_token, u8 count)
270+{
271+ struct wpabuf *buf;
272+ size_t len;
273+ u8 i;
274+
275+ if (count == 0)
276+ return;
277+
278+ /* Reference to 802_11be_D5.0 Figure 9-1183 */
279+ len = 4 + count * sizeof(struct scs_status_duple);
280+ buf = wpabuf_alloc(len);
281+ if (buf == NULL)
282+ return;
283+
284+ wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
285+ wpabuf_put_u8(buf, ROBUST_AV_SCS_RESP);
286+ wpabuf_put_u8(buf, dialog_token);
287+ wpabuf_put_u8(buf, count);
288+
289+ for (i = 0; i < count && i < SCS_MAX_CFG_CNT; i++) {
290+ wpabuf_put_u8(buf, scs_status[i].scs_id);
291+ wpabuf_put_le16(buf, scs_status[i].status);
292+ }
293+
294+ len = wpabuf_len(buf);
295+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da,
296+ wpabuf_head(buf), len);
297+ wpabuf_free(buf);
298+}
299+
300+static void hostapd_handle_scs_req(struct hostapd_data *hapd,
301+ const u8 *buf, size_t len)
302+{
303+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
304+ struct hostapd_scs_desc_info info;
305+ struct sta_info *sta;
306+ struct scs_status_duple scs_status_list[SCS_MAX_CFG_CNT];
307+ const u8 *pos, *end;
308+ u8 token, index = 0;
309+ const struct element *elem;
310+
311+ sta = ap_get_sta(hapd, mgmt->sa);
312+
313+ if (!sta) {
314+ wpa_printf(MSG_ERROR, "Station " MACSTR " not found "
315+ "for SCS Request frame\n", MAC2STR(mgmt->sa));
316+ return;
317+ }
318+
319+ token = mgmt->u.action.u.scs.dialog_token;
320+ pos = mgmt->u.action.u.scs.variable;
321+
322+ end = buf + len;
323+ len = end - pos;
324+
325+ for_each_element(elem, pos, len) {
326+ if (elem->id != WLAN_EID_SCS_DESCRIPTOR) {
327+ wpa_printf(MSG_ERROR, "%s: no scs elem %d in scs req frame!\n",
328+ __func__, WLAN_EID_SCS_DESCRIPTOR);
329+ break;
330+ }
331+
332+ info.id = elem->data[0];
333+ if (!info.id) {
334+ wpa_printf(MSG_ERROR, "%s: SCSID = 0 is invalid\n", __func__);
335+ break;
336+ }
337+
338+ info.req_type = elem->data[1];
339+ os_memcpy(info.peer_addr, mgmt->sa, ETH_ALEN);
340+ scs_status_list[index].scs_id = info.id;
341+ scs_status_list[index].status =
342+ hostapd_process_scs_descriptor(hapd, sta, elem->data,
343+ elem->datalen, &info);
344+ index++;
345+ }
346+
347+ send_scs_response(hapd, scs_status_list, mgmt->sa, token, index);
348+}
349+
350+void hostapd_handle_scs(struct hostapd_data *hapd, const u8 *buf, size_t len)
351+{
352+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
353+
354+ /*
355+ * Check for enough bytes: header + (1B)Category + (1B)Action +
356+ * (1B)Dialog Token.
357+ */
358+ if (len < IEEE80211_HDRLEN + 3) {
359+ wpa_printf(MSG_ERROR, "%s SCS frame len %lu is not enough!",
360+ __func__, len);
361+ return;
362+ }
363+
364+ switch (mgmt->u.action.u.scs.action) {
365+ case ROBUST_AV_SCS_REQ:
366+ hostapd_handle_scs_req(hapd, buf, len);
367+ break;
368+ case ROBUST_AV_SCS_RESP:
369+ /* Not supported yet. */
370+ break;
371+ default:
372+ break;
373+ }
374+}
375diff --git a/src/ap/scs.h b/src/ap/scs.h
376new file mode 100644
377index 000000000..b69190b65
378--- /dev/null
379+++ b/src/ap/scs.h
380@@ -0,0 +1,30 @@
381+#ifndef SCS_H
382+#define SCS_H
383+
384+struct hostapd_data;
385+
386+/* Only support TUAO certification */
387+#define SCS_MAX_CFG_CNT 2
388+
389+struct scs_status_duple {
390+ u8 scs_id;
391+ u16 status;
392+};
393+
394+struct scs_session_status {
395+ u8 scs_id;
396+ bool alive;
397+};
398+
399+enum scs_req_type {
400+ SCS_REQ_TYPE_ADD,
401+ SCS_REQ_TYPE_REMOVE,
402+ SCS_REQ_TYPE_CHANGE,
403+};
404+
405+#define SCS_REQ_SUCCESS 0
406+#define SCS_REQ_DECLINED 37
407+#define SCS_REQ_TCLAS_PROCESSING_TERMINATED 97
408+
409+void hostapd_handle_scs(struct hostapd_data *hapd, const u8 *buf, size_t len);
410+#endif
411diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
412index 60b33f049..339adf987 100644
413--- a/src/ap/sta_info.h
414+++ b/src/ap/sta_info.h
415@@ -18,6 +18,10 @@
416 #include "crypto/sha384.h"
417 #include "pasn/pasn_common.h"
418
419+#ifdef CONFIG_IEEE80211BE
420+#include "scs.h"
421+#endif
422+
423 /* STA flags */
424 #define WLAN_STA_AUTH BIT(0)
425 #define WLAN_STA_ASSOC BIT(1)
426@@ -335,6 +339,7 @@ struct sta_info {
427 struct mld_info mld_info;
428 u8 mld_assoc_link_id;
429 struct sta_info *mld_assoc_sta;
430+ struct scs_session_status scs_session[SCS_MAX_CFG_CNT];
431 #endif /* CONFIG_IEEE80211BE */
432
433 u16 max_idle_period; /* if nonzero, the granted BSS max idle period in
434diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
435index afcc2f861..4754d5a69 100644
436--- a/src/common/ieee802_11_defs.h
437+++ b/src/common/ieee802_11_defs.h
438@@ -1196,6 +1196,11 @@ struct ieee80211_mgmt {
439 u8 action;
440 u8 variable[];
441 } STRUCT_PACKED eht_prot;
442+ struct {
443+ u8 action;
444+ u8 dialog_token;
445+ u8 variable[];
446+ } STRUCT_PACKED scs;
447 } u;
448 } STRUCT_PACKED action;
449 } u;
450diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
451index 6d75d39c2..1fe459126 100644
452--- a/src/common/mtk_vendor.h
453+++ b/src/common/mtk_vendor.h
454@@ -20,6 +20,7 @@ enum mtk_nl80211_vendor_subcmds {
455 MTK_NL80211_VENDOR_SUBCMD_PP_CTRL = 0xcc,
456 MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL = 0xcd,
457 MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL = 0xce,
458+ MTK_NL80211_VENDOR_SUBCMD_SCS_CTRL = 0xd0,
459 MTK_NL80211_VENDOR_SUBCMD_EML_CTRL = 0xd3,
460 };
461
462@@ -325,6 +326,22 @@ enum mtk_vendor_attr_txpower_ctrl {
463 NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL - 1
464 };
465
466+enum mtk_vendor_attr_scs_ctrl {
467+ MTK_VENDOR_ATTR_SCS_CTRL_UNSPEC,
468+
469+ MTK_VENDOR_ATTR_SCS_ID,
470+ MTK_VENDOR_ATTR_SCS_REQ_TYPE,
471+ MTK_VENDOR_ATTR_SCS_DIR,
472+ MTK_VENDOR_ATTR_SCS_QOS_IE,
473+ MTK_VENDOR_ATTR_SCS_MAC_ADDR,
474+ MTK_VENDOR_ATTR_SCS_LINK_ID,
475+
476+ /* keep last */
477+ NUM_MTK_VENDOR_ATTRS_SCS_CTRL,
478+ MTK_VENDOR_ATTR_SCS_CTRL_MAX =
479+ NUM_MTK_VENDOR_ATTRS_SCS_CTRL - 1
480+};
481+
482 #define CSI_BW20_DATA_COUNT 64
483 #define CSI_BW40_DATA_COUNT 128
484 #define CSI_BW80_DATA_COUNT 256
485diff --git a/src/drivers/driver.h b/src/drivers/driver.h
486index 55f62f537..f3de2b3bc 100644
487--- a/src/drivers/driver.h
488+++ b/src/drivers/driver.h
489@@ -70,6 +70,25 @@ enum hostapd_chan_width_attr {
490 #define HOSTAPD_DFS_REGION_ETSI 2
491 #define HOSTAPD_DFS_REGION_JP 3
492
493+/**
494+ * struct hostapd_scs_desc_info - SCS Req information
495+ * @id: SCSID of each SCS stream
496+ * @req_type: request type in SCS Descriptor element
497+ * @dir: Direction in the control info of QoS Characteristics element
498+ * @peer_addr: the mac addr of SCS requester station
499+ * @qos_ie: QoS Characteristics IE in SCS Descriptor element
500+ * @qos_ie_len: the length of QoS Characteristics element
501+ */
502+#define EID_EXT_QOS_CHAR_MAX_SIZE 44
503+struct hostapd_scs_desc_info {
504+ u8 id;
505+ u8 req_type;
506+ u8 dir;
507+ u8 peer_addr[ETH_ALEN];
508+ u8 qos_ie[EID_EXT_QOS_CHAR_MAX_SIZE];
509+ u8 qos_ie_len;
510+};
511+
512 /**
513 * enum reg_change_initiator - Regulatory change initiator
514 */
515@@ -5424,6 +5443,13 @@ struct wpa_driver_ops {
516 int (*pp_mode_set)(void *priv, const u8 pp_mode, s8 link_id, u16 punct_bitmap);
517 #ifdef CONFIG_IEEE80211BE
518 int (*get_mld_addr)(void *priv, u8 *addr);
519+ /**
520+ * set_scs - Configure Stream Classification Service
521+ * @priv: Private driver interface data
522+ * @info: Stream classidication service configuration
523+ * @link_id: MLD link id
524+ */
525+ int (*set_scs)(void *priv, struct hostapd_scs_desc_info *info, u8 link_id);
526 #endif
527 /**
528 * csi_set - Set csi related mode and parameter
529diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
530index 395bb5926..d2064fbd6 100644
531--- a/src/drivers/driver_nl80211.c
532+++ b/src/drivers/driver_nl80211.c
533@@ -42,6 +42,10 @@
534 #include "common/mtk_vendor.h"
535 #include "ap/ap_config.h"
536
537+#ifdef CONFIG_IEEE80211BE
538+#include "ap/scs.h"
539+#endif
540+
541
542 #ifndef NETLINK_CAP_ACK
543 #define NETLINK_CAP_ACK 10
544@@ -2932,6 +2936,9 @@ static int nl80211_action_subscribe_ap(struct i802_bss *bss)
545 /* Protected EHT */
546 if (nl80211_register_action_frame(bss, (u8 *) "\x25", 1) < 0)
547 ret = -1;
548+ /* Robust AV SCS Request */
549+ if (nl80211_register_action_frame(bss, (u8 *) "\x13\x00", 2) < 0)
550+ ret = -1;
551 /* Vendor-specific */
552 if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0)
553 ret = -1;
554@@ -15387,6 +15394,60 @@ static int nl80211_set_eml_omn(void *priv, u8 link_id, u8 *addr,
555
556 return ret;
557
558+fail:
559+ nlmsg_free(msg);
560+ return ret;
561+}
562+
563+static int
564+nl80211_set_scs(void *priv, struct hostapd_scs_desc_info *info, u8 link_id)
565+{
566+ struct i802_bss *bss = priv;
567+ struct wpa_driver_nl80211_data *drv = bss->drv;
568+ struct nl_msg *msg;
569+ struct nlattr *data;
570+ int ret;
571+
572+ if (!drv->mtk_scs_vendor_cmd_avail) {
573+ wpa_printf(MSG_ERROR,
574+ "nl80211: Driver does not support scs");
575+ return 0;
576+ }
577+
578+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
579+ if (!msg)
580+ goto fail;
581+
582+ if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
583+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
584+ MTK_NL80211_VENDOR_SUBCMD_SCS_CTRL))
585+ goto fail;
586+
587+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
588+ if (!data)
589+ goto fail;
590+
591+ if (nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_ID, info->id) ||
592+ nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_REQ_TYPE, info->req_type) ||
593+ nla_put(msg, MTK_VENDOR_ATTR_SCS_MAC_ADDR, ETH_ALEN, info->peer_addr) ||
594+ nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_LINK_ID, link_id))
595+ goto fail;
596+
597+ if (info->req_type == SCS_REQ_TYPE_ADD ||
598+ info->req_type == SCS_REQ_TYPE_CHANGE)
599+ if (nla_put_u8(msg, MTK_VENDOR_ATTR_SCS_DIR, info->dir) ||
600+ nla_put(msg, MTK_VENDOR_ATTR_SCS_QOS_IE, info->qos_ie_len,
601+ info->qos_ie))
602+ goto fail;
603+
604+ nla_nest_end(msg, data);
605+ ret = send_and_recv_cmd(drv, msg);
606+ if (ret)
607+ wpa_printf(MSG_ERROR, "Failed to set scs. ret = %d (%s)",
608+ ret, strerror(-ret));
609+
610+ return ret;
611+
612 fail:
613 nlmsg_free(msg);
614 return ret;
615@@ -15830,6 +15891,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
616 .pp_mode_set = nl80211_pp_mode_set,
617 #ifdef CONFIG_IEEE80211BE
618 .get_mld_addr = nl80211_get_mld_addr,
619+ .set_scs = nl80211_set_scs,
620 #endif
621 .csi_set = nl80211_csi_set,
622 .csi_dump = nl80211_csi_dump,
623diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
624index d32c0ff4d..87ade150b 100644
625--- a/src/drivers/driver_nl80211.h
626+++ b/src/drivers/driver_nl80211.h
627@@ -215,6 +215,7 @@ struct wpa_driver_nl80211_data {
628 unsigned int mtk_csi_vendor_cmd_avail:1;
629 unsigned int mtk_eml_vendor_cmd_avail:1;
630 unsigned int mtk_txpower_vendor_cmd_avail:1;
631+ unsigned int mtk_scs_vendor_cmd_avail:1;
632
633 u32 ignore_next_local_disconnect;
634 u32 ignore_next_local_deauth;
635diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
636index fc7f9062f..5db6bf6e6 100644
637--- a/src/drivers/driver_nl80211_capa.c
638+++ b/src/drivers/driver_nl80211_capa.c
639@@ -1182,6 +1182,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
640 case MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL:
641 drv->mtk_txpower_vendor_cmd_avail = 1;
642 break;
643+ case MTK_NL80211_VENDOR_SUBCMD_SCS_CTRL:
644+ drv->mtk_scs_vendor_cmd_avail = 1;
645+ break;
646 }
647 }
648
649--
6502.18.0
651