wlan-ap-Telecominfraproject/feeds/qca/hostapd/patches/r04-002-hostapd-Stream-Classification-Service-Support-for-UL.patch
John Crispin 008ca9618d
Some checks failed
Build OpenWrt/uCentral images / build (cig_wf186h) (push) Has been cancelled
Build OpenWrt/uCentral images / build (cig_wf186w) (push) Has been cancelled
Build OpenWrt/uCentral images / build (cig_wf188n) (push) Has been cancelled
Build OpenWrt/uCentral images / build (cig_wf189) (push) Has been cancelled
Build OpenWrt/uCentral images / build (cig_wf196) (push) Has been cancelled
Build OpenWrt/uCentral images / build (cybertan_eww631-a1) (push) Has been cancelled
Build OpenWrt/uCentral images / build (cybertan_eww631-b1) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_eap101) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_eap102) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_eap104) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_eap105) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_eap111) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_eap112) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_oap101) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_oap101-6e) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_oap101e) (push) Has been cancelled
Build OpenWrt/uCentral images / build (edgecore_oap101e-6e) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4x) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4x_2) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4x_3) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4x_w) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4xe) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4xi) (push) Has been cancelled
Build OpenWrt/uCentral images / build (hfcl_ion4xi_w) (push) Has been cancelled
Build OpenWrt/uCentral images / build (indio_um-305ax) (push) Has been cancelled
Build OpenWrt/uCentral images / build (sercomm_ap72tip) (push) Has been cancelled
Build OpenWrt/uCentral images / build (sonicfi_rap630c-311g) (push) Has been cancelled
Build OpenWrt/uCentral images / build (sonicfi_rap630w-211g) (push) Has been cancelled
Build OpenWrt/uCentral images / build (sonicfi_rap630w-311g) (push) Has been cancelled
Build OpenWrt/uCentral images / build (udaya_a6-id2) (push) Has been cancelled
Build OpenWrt/uCentral images / build (udaya_a6-od2) (push) Has been cancelled
Build OpenWrt/uCentral images / build (wallys_dr5018) (push) Has been cancelled
Build OpenWrt/uCentral images / build (wallys_dr6018) (push) Has been cancelled
Build OpenWrt/uCentral images / build (wallys_dr6018-v4) (push) Has been cancelled
Build OpenWrt/uCentral images / build (yuncore_ax820) (push) Has been cancelled
Build OpenWrt/uCentral images / build (yuncore_ax840) (push) Has been cancelled
Build OpenWrt/uCentral images / build (yuncore_fap640) (push) Has been cancelled
Build OpenWrt/uCentral images / build (yuncore_fap650) (push) Has been cancelled
Build OpenWrt/uCentral images / build (yuncore_fap655) (push) Has been cancelled
Build OpenWrt/uCentral images / trigger-testing (push) Has been cancelled
Build OpenWrt/uCentral images / create-x64_vm-ami (push) Has been cancelled
ipq95xx: import ath12.4-cs kernel and drivers
Signed-off-by: John Crispin <john@phrozen.org>
2024-10-20 09:25:13 +02:00

1369 lines
43 KiB
Diff

From 061f80f99ca46941eca7140307711c21b6e4ec95 Mon Sep 17 00:00:00 2001
From: Balamurugan Ramar <quic_bramar@quicinc.com>
Date: Thu, 23 Nov 2023 16:26:52 +0530
Subject: [PATCH] hostapd: Add SCS UL Support.
Stream classification service classifies incoming individually
addressed msdus based upon the parameters provided by the STA.
AP advertise the SCS capability in beacon frame or probe response
or assoc response to the station. Station sends the SCS request
frame to the AP if AP advertise scs capabaility.
AP subscribe robust AV stream action frame to receive the SCS
frame from kernel. AP parse the scs descriptors, qos attributes and
send the latency params to the driver via NL vendor commands.
STA that supports SCS may request use of SCS by sending an SCS Request
frame that includes SCS Descriptor element with the Request Type field
set to Add or Change.
The SCS Descriptor List field in the SCS Descriptor element identifies
how MSDUs are classified and the priority to assign to MSDUs that match
this classification.
Each SCS stream is identified by an SCSID. This SCSID is used by a STA
to request creation, modification, or deletion of an SCS stream. AP send
SCS response back to the clinet once it succssefully processed the SCS
request.
Signed-off-by: Balamurugan Ramar <quic_bramar@quicinc.com>
---
hostapd/Makefile | 1 +
src/ap/Makefile | 3 +-
src/ap/ap_drv_ops.c | 58 +++-
src/ap/ap_drv_ops.h | 4 +
src/ap/beacon.c | 14 +-
src/ap/hostapd.h | 2 +
src/ap/ieee802_11.c | 11 +-
src/ap/ieee802_11_shared.c | 11 +-
src/ap/scs.c | 543 +++++++++++++++++++++++++++++++++++
src/ap/scs.h | 273 ++++++++++++++++++
src/ap/sta_info.c | 5 +
src/ap/sta_info.h | 11 +
src/common/ieee802_11_defs.h | 11 +-
src/common/qca-vendor.h | 7 +
src/drivers/driver.h | 15 +
src/drivers/driver_nl80211.c | 65 ++++-
16 files changed, 1023 insertions(+), 11 deletions(-)
create mode 100644 src/ap/scs.c
create mode 100644 src/ap/scs.h
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -355,6 +355,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
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -56,6 +56,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
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -22,7 +22,9 @@
#include "hs20.h"
#include "wpa_auth.h"
#include "ap_drv_ops.h"
-
+#ifdef CONFIG_IEEE80211BE
+#include "scs.h"
+#endif
u32 hostapd_sta_flags_to_drv(u32 flags)
{
@@ -71,7 +73,7 @@ int hostapd_build_ap_extra_ies(struct ho
struct wpabuf **assocresp_ret)
{
struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL;
- u8 buf[200], *pos;
+ u8 buf[208], *pos;
*beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
@@ -116,6 +118,15 @@ int hostapd_build_ap_extra_ies(struct ho
goto fail;
#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211BE
+ pos = hostapd_add_scs_cap(buf, false, true);
+ if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+ add_buf_data(&proberesp, buf, pos - buf) < 0 ||
+ add_buf_data(&assocresp, buf, pos - buf) < 0) {
+ goto fail;
+ }
+#endif
+
pos = hostapd_eid_rsnxe(hapd, buf, sizeof(buf));
if (add_buf_data(&assocresp, buf, pos - buf) < 0)
goto fail;
@@ -368,6 +379,49 @@ int hostapd_add_sta_node(struct hostapd_
return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
}
+static int hostapd_get_scs_final_value(struct sta_info *sta,
+ struct hostapd_qos_mandatory_scs_param *scs_m)
+{
+ struct hostapd_qos_mandatory_scs_param *scs_db;
+ u8 index = 0;
+
+ while (index < sta->session_cnt && index < HOSTAPD_SCS_MAX_SIZE) {
+ scs_db = sta->scs_data[index];
+ if (scs_db->ac == scs_m->ac) {
+ scs_m->service_interval =
+ HOSTAPD_MIN(scs_db->service_interval, scs_m->service_interval);
+ scs_m->min_data_rate =
+ HOSTAPD_MAX(scs_db->min_data_rate, scs_m->min_data_rate);
+ scs_m->delay_bound =
+ HOSTAPD_MIN(scs_db->delay_bound, scs_m->delay_bound);
+ }
+ ++index;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_IEEE80211BE
+int hostapd_set_scs_params(struct hostapd_data *hapd, struct sta_info *sta,
+ struct hostapd_qos_mandatory_scs_param *scs_m, u8 req_type)
+{
+ struct scs_latency_params scs_drv = {0};
+
+ if (hapd->driver == NULL || hapd->driver->set_scs == NULL)
+ return -EOPNOTSUPP;
+
+ hostapd_get_scs_final_value(sta, scs_m);
+ scs_drv.service_interval = scs_m->service_interval / 1000;
+ scs_drv.user_priority = scs_m->up;
+ scs_drv.burst_size = scs_m->burst_size;
+ scs_drv.req_type = req_type;
+ scs_drv.delay_bound = scs_m->delay_bound / 1000;
+ scs_drv.min_data_rate = scs_m->min_data_rate;
+ scs_drv.ac = scs_m->ac;
+ scs_drv.direction = scs_m->direction;
+ os_memcpy(scs_drv.peer_mac, scs_m->peer_mac, 6);
+ return hapd->driver->set_scs(hapd->drv_priv, &scs_drv);
+}
+#endif
int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
u16 seq, u16 status, const u8 *ie, size_t len)
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -150,6 +150,10 @@ int hostapd_drv_set_secure_ranging_ctx(s
u8 ltf_keyseed_len,
const u8 *ltf_keyseed, u32 action);
+#ifdef CONFIG_IEEE80211BE
+int hostapd_set_scs_params(struct hostapd_data *hapd, struct sta_info *sta,
+ struct hostapd_qos_mandatory_scs_param *scs_m, u8 req_type);
+#endif
#include "drivers/driver.h"
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -32,7 +32,9 @@
#include "dfs.h"
#include "taxonomy.h"
#include "ieee802_11_auth.h"
-
+#ifdef CONFIG_IEEE80211BE
+#include "scs.h"
+#endif
#ifdef NEED_AP_MLME
@@ -987,6 +989,7 @@ static u8 * hostapd_gen_probe_resp(struc
buflen += hostapd_mbo_ie_len(hapd);
buflen += hostapd_eid_owe_trans_len(hapd);
buflen += hostapd_eid_dpp_cc_len(hapd);
+ buflen += hostapd_scs_ie_len(hapd);
resp = os_zalloc(buflen);
if (resp == NULL)
@@ -1171,7 +1174,9 @@ static u8 * hostapd_gen_probe_resp(struc
/* Wi-Fi Alliance WMM */
pos = hostapd_eid_wmm(hapd, pos);
-
+#ifdef CONFIG_IEEE80211BE
+ pos = hostapd_add_scs_cap(pos, false, true);
+#endif
#ifdef CONFIG_WPS
if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) {
os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie),
@@ -2202,6 +2207,7 @@ int ieee802_11_build_ap_params(struct ho
tail_len += hostapd_mbo_ie_len(hapd);
tail_len += hostapd_eid_owe_trans_len(hapd);
tail_len += hostapd_eid_dpp_cc_len(hapd);
+ tail_len += hostapd_scs_ie_len(hapd);
tailpos = tail = os_malloc(tail_len);
if (head == NULL || tail == NULL) {
@@ -2389,7 +2395,9 @@ int ieee802_11_build_ap_params(struct ho
/* Wi-Fi Alliance WMM */
tailpos = hostapd_eid_wmm(hapd, tailpos);
-
+#ifdef CONFIG_IEEE80211BE
+ tailpos = hostapd_add_scs_cap(tailpos, false, true);
+#endif
#ifdef CONFIG_WPS
if (hapd->conf->wps_state && hapd->wps_beacon_ie) {
os_memcpy(tailpos, wpabuf_head(hapd->wps_beacon_ie),
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -506,6 +506,8 @@ struct hostapd_data {
/* Store the partner info for ML probe response */
struct mld_link_info partner_links[MAX_NUM_MLD_LINKS];
+
+ u8 session_cnt;
#endif /* CONFIG_IEEE80211BE */
};
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -57,7 +57,9 @@
#include "gas_query_ap.h"
#include "comeback_token.h"
#include "pasn/pasn_common.h"
-
+#ifdef CONFIG_IEEE80211BE
+#include "scs.h"
+#endif
#ifdef CONFIG_FILS
static struct wpabuf *
@@ -5049,7 +5051,9 @@ rsnxe_done:
if (sta && (sta->flags & WLAN_STA_WMM))
p = hostapd_eid_wmm(hapd, p);
-
+#ifdef CONFIG_IEEE80211BE
+ p = hostapd_add_scs_cap(p, false, true);
+#endif
#ifdef CONFIG_WPS
if (sta &&
((sta->flags & WLAN_STA_WPS) ||
@@ -6041,6 +6045,9 @@ static int handle_action(struct hostapd_
return 1;
}
break;
+ case WLAN_ACTION_ROBUST_AV_STREAMING:
+ hostapd_handle_scs(hapd, (const u8 *)mgmt, len);
+ return 1;
case WLAN_ACTION_RADIO_MEASUREMENT:
hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
return 1;
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -19,7 +19,9 @@
#include "wpa_auth.h"
#include "dpp_hostapd.h"
#include "ieee802_11.h"
-
+#ifdef CONFIG_IEEE80211BE
+#include "scs.h"
+#endif
u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
struct sta_info *sta, u8 *eid)
@@ -483,6 +485,13 @@ u8 * hostapd_eid_ext_capab(struct hostap
*pos |= hapd->conf->ext_capa[i];
}
+#ifdef CONFIG_IEEE80211BE
+ if (i == HOSTAPD_SCS_EXTCAP_INDEX) {
+ *pos |= HOSTAPD_SCS_EXTCAP_SCS_DESC; // SCS Desc capability
+ *pos |= HOSTAPD_SCS_EXTCAP_ROBUST_AV; // Robust AV capability
+ }
+#endif
+
/* Clear bits 83 and 22 if EMA and MBSSID are not enabled
* otherwise association fails with some clients */
if (i == 10 && hapd->iconf->mbssid < ENHANCED_MBSSID_ENABLED)
--- /dev/null
+++ b/src/ap/scs.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#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 u32 hostapd_get_access_cat(u8 tid)
+{
+ u32 ac;
+
+ if ((tid == 0) || (tid == 3))
+ ac = HOSTAPD_SCS_BEST_EFF;
+ else if ((tid == 1) || (tid == 2))
+ ac = HOSTAPD_SCS_BGROUND;
+ else if ((tid == 4) || (tid == 5))
+ ac = HOSTAPD_SCS_VIDEO;
+ else
+ ac = HOSTAPD_SCS_VOICE;
+
+ return ac;
+}
+
+static void hostapd_scs_dump(struct hostapd_scs_desc_list *scs)
+{
+ wpa_printf(MSG_INFO, "scs info\n");
+ wpa_printf(MSG_INFO, "element Id %d\n", scs->elem.id);
+ wpa_printf(MSG_INFO, "element Len %d\n", scs->elem.len);
+ wpa_printf(MSG_INFO, "scs id %d\n", scs->desc.id);
+ wpa_printf(MSG_INFO, "request type %d\n", scs->desc.req_type);
+ wpa_printf(MSG_INFO, "scs intra access category info\n");
+ wpa_printf(MSG_INFO, "element Id %d\n", scs->ia.elem.id);
+ wpa_printf(MSG_INFO, "element Len %d\n", scs->ia.elem.len);
+ wpa_printf(MSG_INFO, "ia piority %d\n", scs->ia.priority);
+ wpa_printf(MSG_INFO, "scs tclass element\n");
+ wpa_printf(MSG_INFO, "element id %d\n", scs->tclas.elem.id);
+ wpa_printf(MSG_INFO, "element len %d\n", scs->tclas.elem.len);
+ wpa_printf(MSG_INFO, "scs qos attributes\n");
+ wpa_printf(MSG_INFO, "element id %d\n", scs->qos_attr.elem.id);
+ wpa_printf(MSG_INFO, "element len %d\n", scs->qos_attr.elem.len);
+ wpa_printf(MSG_INFO, "attribute id %d\n", scs->qos_attr.attr_id);
+ wpa_printf(MSG_INFO, "direction %d\n",
+ scs->qos_attr.ctrl_info.direction);
+ wpa_printf(MSG_INFO, "tid %d\n", scs->qos_attr.ctrl_info.tid);
+ wpa_printf(MSG_INFO, "user priority %d\n", scs->qos_attr.ctrl_info.up);
+ wpa_printf(MSG_INFO, "bitmap %d\n", scs->qos_attr.ctrl_info.bitmap);
+ wpa_printf(MSG_INFO, "link id %d\n", scs->qos_attr.ctrl_info.link_id);
+ wpa_printf(MSG_INFO, "access category %d\n",
+ scs->qos_attr.ctrl_info.access_cat);
+ wpa_printf(MSG_INFO, "minimum interval %d\n", scs->qos_attr.min_interval);
+ wpa_printf(MSG_INFO, "maximum interval %d\n", scs->qos_attr.max_interval);
+ wpa_printf(MSG_INFO, "final service interval %d\n", scs->qos_attr.final_serv_intvl);
+ wpa_printf(MSG_INFO, "minimum data rate %d\n", scs->qos_attr.min_data_rate);
+ wpa_printf(MSG_INFO, "delay bound %d\n", scs->qos_attr.delay_bound);
+ wpa_printf(MSG_INFO, "service start time %d\n", scs->qos_attr.serv_start_time);
+ wpa_printf(MSG_INFO, "mean data rate %d\n", scs->qos_attr.mean_data_rate);
+ wpa_printf(MSG_INFO, "burst size %d\n", scs->qos_attr.burst_size);
+ wpa_printf(MSG_INFO, "final burst size %d\n", scs->qos_attr.final_burst_size);
+ wpa_printf(MSG_INFO, "max msdu size %d\n", scs->qos_attr.max_msdu_size);
+ wpa_printf(MSG_INFO, "msdu life time %d\n", scs->qos_attr.msdu_life_time);
+ wpa_printf(MSG_INFO, "medium time %d\n", scs->qos_attr.medium_time);
+ wpa_printf(MSG_INFO, "service start time link id %d\n",
+ scs->qos_attr.serv_start_time_link_id);
+ wpa_printf(MSG_INFO, "msdu delivery ratio %d\n",
+ scs->qos_attr.msdu_delivery_ratio);
+ wpa_printf(MSG_INFO, "msdu count exponenet %d\n", scs->qos_attr.msdu_cnt_exp);
+}
+
+static u8 hostapd_get_scs_index(struct sta_info *sta, u8 scsid)
+{
+ u8 index = 0;
+
+ if (!sta || !sta->session_cnt)
+ return HOSTAPD_SCS_MAX_SIZE;
+
+ while (index < sta->session_cnt && index < HOSTAPD_SCS_MAX_SIZE) {
+ if (sta->scs_data[index] == NULL)
+ return HOSTAPD_SCS_MAX_SIZE;
+ if (scsid == sta->scs_data[index]->scsid)
+ return index;
+ ++index;
+ }
+ return HOSTAPD_SCS_MAX_SIZE;
+}
+
+static bool hostapd_is_scs_present(struct sta_info *sta, u8 scsid)
+{
+ u8 index = 0;
+
+ index = hostapd_get_scs_index(sta, scsid);
+
+ if (index >= HOSTAPD_SCS_MAX_SIZE)
+ return HOSTAPD_SCS_NOT_PRESENT;
+
+ return HOSTAPD_SCS_PRESENT;
+}
+
+static int hostapd_remove_scs(struct hostapd_data *hapd, struct sta_info *sta, u8 scsid)
+{
+ struct hostapd_qos_mandatory_scs_param scs_m = {0};
+ int ret;
+ u8 index = 0;
+
+ index = hostapd_get_scs_index(sta, scsid);
+ if (index >= HOSTAPD_SCS_MAX_SIZE)
+ goto decline;
+
+ os_memcpy(&scs_m, sta->scs_data[index], sizeof(struct hostapd_qos_mandatory_scs_param));
+ os_free(sta->scs_data[index]);
+
+ while (index < sta->session_cnt - 1) {
+ sta->scs_data[index] = sta->scs_data[index + 1];
+ ++index;
+ }
+
+ --sta->session_cnt;
+ sta->scs_data[index] = NULL;
+
+ if (sta->session_cnt) {
+ scs_m.min_data_rate = 0;
+ scs_m.delay_bound = -1;
+ scs_m.service_interval = -1;
+ } else {
+ scs_m.min_data_rate = 0;
+ scs_m.delay_bound = 0;
+ scs_m.service_interval = 0;
+ }
+ ret = hostapd_set_scs_params(hapd, sta, &scs_m, HOSTAPD_REM_RULE);
+ if (ret)
+ goto decline;
+
+ return HOSTAPD_SCS_REQ_TCLAS_PROCESSING_TERMINATED;
+
+decline:
+ return HOSTAPD_SCS_REQ_DECLINED;
+}
+
+static int copy_scs_mparam(struct hostapd_qos_mandatory_scs_param *scs_m,
+ struct hostapd_scs_desc_list *scs)
+{
+ scs_m->service_interval = scs->qos_attr.final_serv_intvl;
+ scs_m->delay_bound = scs->qos_attr.delay_bound;
+ scs_m->burst_size = scs->qos_attr.burst_size;
+ scs_m->min_data_rate = scs->qos_attr.min_data_rate;
+ scs_m->scsid = scs->desc.id;
+ scs_m->ac = scs->qos_attr.ctrl_info.access_cat;
+ scs_m->up = scs->qos_attr.ctrl_info.up;
+ scs_m->direction = scs->qos_attr.ctrl_info.direction;
+ os_memcpy(scs_m->peer_mac, scs->peer_mac, ETH_ALEN);
+ return 0;
+}
+
+static int hostapd_add_scs(struct hostapd_data *hapd, struct sta_info *sta,
+ struct hostapd_scs_desc_list *scs)
+{
+ struct hostapd_qos_mandatory_scs_param *scs_m = NULL, scs_t = {0};
+ int ret;
+
+ scs_m = os_zalloc(sizeof(struct hostapd_qos_mandatory_scs_param));
+ if (!scs_m)
+ goto decline;
+
+ copy_scs_mparam(scs_m, scs);
+ os_memcpy(&scs_t, scs_m, sizeof(struct hostapd_qos_mandatory_scs_param));
+ sta->scs_data[sta->session_cnt++] = scs_m;
+ ret = hostapd_set_scs_params(hapd, sta, &scs_t, HOSTAPD_ADD_RULE);
+ if (ret)
+ goto decline;
+
+ return HOSTAPD_SCS_REQ_SUCCESS;
+
+decline:
+ return HOSTAPD_SCS_REQ_DECLINED;
+}
+
+static int hostapd_change_scs(struct hostapd_data *hapd, struct sta_info *sta,
+ struct hostapd_scs_desc_list *scs)
+{
+ int ret;
+
+ ret = hostapd_remove_scs(hapd, sta, scs->desc.id);
+ if (ret != HOSTAPD_SCS_REQ_TCLAS_PROCESSING_TERMINATED)
+ return ret;
+
+ ret = hostapd_add_scs(hapd, sta, scs);
+
+ return ret;
+}
+
+int hostapd_free_all_scs(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ while (sta->session_cnt) {
+ if (sta->scs_data[0])
+ hostapd_remove_scs(hapd, sta, sta->scs_data[0]->scsid);
+ }
+ return 0;
+}
+
+static const u8 *hostapd_parse_intra_access(const u8 *payload, struct hostapd_scs_desc_list *scs)
+{
+ scs->ia.elem.id = *payload;
+ if (scs->ia.elem.id == WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY) {
+ ++payload;
+ scs->ia.elem.len = *payload++;
+ scs->ia.priority = *payload++;
+ }
+ return payload;
+}
+
+static const u8 *hostapd_parse_tclas(const u8 *payload, struct hostapd_scs_desc_list *scs)
+{
+ scs->tclas.elem.id = *payload;
+ if (scs->tclas.elem.id == WLAN_EID_TCLAS) {
+ payload += 3;
+ scs->tclas.elem.len = *payload;
+ }
+ return payload;
+}
+
+static const u8 *hostapd_parse_qos_attr(const u8 *payload, struct hostapd_scs_desc_list *scs)
+{
+ u32 ctrl_det, final_serv_intvl, final_burst_size, temp_data_rate, temp_burst_size;
+
+ scs->qos_attr.elem.id = *payload;
+ if (scs->qos_attr.elem.id != WLAN_EID_EXTENSION)
+ return NULL;
+
+ ++payload;
+ scs->qos_attr.elem.len = *payload++;
+ scs->qos_attr.attr_id = *payload++;
+
+ HOSTAPD_GET_QOS_ATTR(ctrl_det, payload, CTRL_INFO, 32);
+ scs->qos_attr.ctrl_info.direction = HOSTAPD_GET_QOS_ATTR_CTRL_INFO(ctrl_det, DIRECTION);
+
+ if (scs->qos_attr.ctrl_info.direction != WMM_TSPEC_DIRECTION_UPLINK) {
+ wpa_printf(MSG_ERROR, "%s: SCS down / direct link is not supported\n", __func__);
+ return NULL;
+ }
+
+ scs->qos_attr.ctrl_info.tid = HOSTAPD_GET_QOS_ATTR_CTRL_INFO(ctrl_det, TID);
+ scs->qos_attr.ctrl_info.up = HOSTAPD_GET_QOS_ATTR_CTRL_INFO(ctrl_det, UP);
+ scs->qos_attr.ctrl_info.bitmap = HOSTAPD_GET_QOS_ATTR_CTRL_INFO(ctrl_det, BMP);
+ scs->qos_attr.ctrl_info.link_id = HOSTAPD_GET_QOS_ATTR_CTRL_INFO(ctrl_det, LINK_ID);
+ scs->qos_attr.ctrl_info.access_cat = hostapd_get_access_cat(scs->qos_attr.ctrl_info.tid);
+
+ HOSTAPD_GET_QOS_ATTR(scs->qos_attr.min_interval, payload, SERVICE_INTERVAL, 32);
+ HOSTAPD_GET_QOS_ATTR(scs->qos_attr.max_interval, payload, SERVICE_INTERVAL, 32);
+
+ scs->qos_attr.final_serv_intvl =
+ (scs->qos_attr.min_interval + scs->qos_attr.max_interval) >> 1;
+ final_serv_intvl = scs->qos_attr.final_serv_intvl / 1000;
+
+ memcpy(&scs->qos_attr.min_data_rate, payload, HOSTAPD_SCS_QOS_ATTR_MIN_DATA_RATE_LEN);
+ HOSTAPD_GET_QOS_ATTR_ACTUAL(scs->qos_attr.min_data_rate, payload,
+ &scs->qos_attr.min_data_rate,
+ MIN_DATA_RATE, 32);
+ temp_data_rate = scs->qos_attr.min_data_rate >> 3;
+ final_burst_size = final_serv_intvl * temp_data_rate;
+
+ memcpy(&scs->qos_attr.delay_bound, payload, HOSTAPD_SCS_QOS_ATTR_DELAY_BOULND_LEN);
+ HOSTAPD_GET_QOS_ATTR_ACTUAL(scs->qos_attr.delay_bound, payload, &scs->qos_attr.delay_bound,
+ DELAY_BOULND, 32);
+
+ if (HOSTAPD_SCS_IS_QOS_ATTR_PRESENT(scs->qos_attr.ctrl_info.bitmap, MAX_MSDU_SIZE))
+ HOSTAPD_GET_QOS_ATTR(scs->qos_attr.max_msdu_size, payload, MAX_MSDU_SIZE, 16);
+
+ if (HOSTAPD_SCS_IS_QOS_ATTR_PRESENT(scs->qos_attr.ctrl_info.bitmap, SERVICE_START_TIME))
+ HOSTAPD_GET_QOS_ATTR(scs->qos_attr.serv_start_time, payload, SERVICE_START_TIME,
+ 32);
+
+ if (HOSTAPD_SCS_IS_QOS_ATTR_PRESENT(scs->qos_attr.ctrl_info.bitmap,
+ SERVICE_START_TIME_LINK_ID))
+ HOSTAPD_GET_QOS_ATTR(scs->qos_attr.serv_start_time_link_id, payload,
+ SERVICE_START_TIME_LINK_ID, 8);
+
+ if (HOSTAPD_SCS_IS_QOS_ATTR_PRESENT(scs->qos_attr.ctrl_info.bitmap, MEAN_DATA_RATE)) {
+ memcpy(&scs->qos_attr.mean_data_rate, payload,
+ HOSTAPD_SCS_QOS_ATTR_MEAN_DATA_RATE_LEN);
+ HOSTAPD_GET_QOS_ATTR_ACTUAL(scs->qos_attr.mean_data_rate, payload,
+ &scs->qos_attr.mean_data_rate, MEAN_DATA_RATE, 32);
+ temp_data_rate = scs->qos_attr.mean_data_rate >> 3;
+ temp_burst_size = final_serv_intvl * temp_data_rate;
+ final_burst_size = final_burst_size < temp_burst_size ?
+ final_burst_size : temp_burst_size;
+ }
+
+ if (HOSTAPD_SCS_IS_QOS_ATTR_PRESENT(scs->qos_attr.ctrl_info.bitmap, BURST_SIZE)) {
+ HOSTAPD_GET_QOS_ATTR(scs->qos_attr.burst_size, payload, BURST_SIZE, 32);
+ final_burst_size = final_burst_size > scs->qos_attr.burst_size ?
+ scs->qos_attr.burst_size : final_burst_size;
+ }
+
+ if (HOSTAPD_SCS_IS_QOS_ATTR_PRESENT(scs->qos_attr.ctrl_info.bitmap, MSDU_LIFETIME))
+ HOSTAPD_GET_QOS_ATTR(scs->qos_attr.msdu_life_time, payload, MSDU_LIFETIME, 16);
+
+ if (HOSTAPD_SCS_IS_QOS_ATTR_PRESENT(scs->qos_attr.ctrl_info.bitmap, MSDU_DELIVERY_RATIO))
+ HOSTAPD_GET_QOS_ATTR(scs->qos_attr.msdu_delivery_ratio, payload,
+ MSDU_DELIVERY_RATIO, 8);
+
+ if (HOSTAPD_SCS_IS_QOS_ATTR_PRESENT(scs->qos_attr.ctrl_info.bitmap, MSDU_COUNT_EXPONENT))
+ HOSTAPD_GET_QOS_ATTR(scs->qos_attr.msdu_cnt_exp, payload, MSDU_COUNT_EXPONENT, 8);
+
+ if (HOSTAPD_SCS_IS_QOS_ATTR_PRESENT(scs->qos_attr.ctrl_info.bitmap, MEDIUM_TIME))
+ HOSTAPD_GET_QOS_ATTR(scs->qos_attr.medium_time, payload, MEDIUM_TIME, 16);
+
+ scs->qos_attr.final_burst_size = final_burst_size;
+ return payload;
+}
+
+static const u8 *hostapd_parse_scs_desc(const u8 *payload, struct hostapd_scs_desc_list *scs)
+{
+ scs->elem.id = *payload++;
+ if (scs->elem.id != WLAN_EID_SCS_DESCRIPTOR) {
+ wpa_printf(MSG_ERROR, "%s scs elem %d is not available in this frame !!!\n",
+ __func__, WLAN_EID_SCS_DESCRIPTOR);
+ return NULL;
+ }
+
+ scs->elem.len = *payload++;
+ scs->desc.id = *payload++;
+ scs->desc.req_type = *payload++;
+
+ return payload;
+}
+
+static int hostapd_process_scs_req(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *payload, struct hostapd_scs_desc_list *scs)
+{
+ int ret = HOSTAPD_SCS_REQ_SUCCESS;
+ u8 scs_avail;
+
+ scs_avail = hostapd_is_scs_present(sta, scs->desc.id);
+ switch (scs->desc.req_type) {
+ case HOSTAPD_SCS_REQ_TYPE_ADD:
+ if (scs_avail == HOSTAPD_SCS_PRESENT) {
+ wpa_printf(MSG_ERROR, "%s:%d> scs id %d is already present\n",
+ __func__, __LINE__, scs->desc.id);
+ goto decline;
+ }
+
+ if (sta->session_cnt >= HOSTAPD_SCS_MAX_SIZE)
+ goto decline;
+
+ payload = hostapd_parse_intra_access(payload, scs);
+ payload = hostapd_parse_tclas(payload, scs);
+ payload = hostapd_parse_qos_attr(payload, scs);
+
+ if (!payload) {
+ wpa_printf(MSG_ERROR, "%s:%d> scs id %d qos is not present\n",
+ __func__, __LINE__, scs->desc.id);
+ goto decline;
+ }
+
+ ret = hostapd_add_scs(hapd, sta, scs);
+ break;
+ case HOSTAPD_SCS_REQ_TYPE_REMOVE:
+ if (scs_avail != HOSTAPD_SCS_PRESENT) {
+ wpa_printf(MSG_ERROR, "%s:%d> scs id %d is not preset\n",
+ __func__, __LINE__, scs->desc.id);
+ goto decline;
+ }
+ ret = hostapd_remove_scs(hapd, sta, scs->desc.id);
+ break;
+ case HOSTAPD_SCS_REQ_TYPE_CHANGE:
+ if (scs_avail != HOSTAPD_SCS_PRESENT) {
+ wpa_printf(MSG_ERROR, "%s:%d> scs id %d is not preset\n",
+ __func__, __LINE__, scs->desc.id);
+ goto decline;
+ }
+ ret = hostapd_change_scs(hapd, sta, scs);
+ break;
+ default:
+ goto decline;
+ }
+ return ret;
+
+decline:
+ wpa_printf(MSG_ERROR, "%s: Decline Request %d\n", __func__, scs->desc.req_type);
+ return HOSTAPD_SCS_REQ_DECLINED;
+}
+
+static void send_scs_response(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *da, int dialog_token, u8 index)
+{
+ struct wpabuf *buf;
+ size_t len;
+ int i = 0;
+ u8 status;
+
+ len = IEEE80211_MGMT_MIN_LEN + sizeof(struct hostapd_scs_resp);
+ 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, (u8)dialog_token);
+ wpabuf_put_u8(buf, index);
+
+ while ((index != 0) && (i < HOSTAPD_SCS_MAX_SIZE)) {
+ wpabuf_put_u8(buf, sta->scs_resp->scsid[i]);
+ status = sta->scs_resp->status[i] & 0x000000ff;
+ wpabuf_put_u8(buf, status);
+ status = (sta->scs_resp->status[i] & 0x0000ff00) >> 8;
+ wpabuf_put_u8(buf, status);
+ index--;
+ i++;
+ }
+ len = wpabuf_len(buf);
+ hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da,
+ wpabuf_head(buf), len);
+ wpa_printf(MSG_INFO, "%s: successfully sent SCS response frame len %d\n", __func__, (int)len);
+ wpabuf_free(buf);
+}
+
+static int 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_list scs = {0};
+ struct sta_info *sta;
+ const u8 *payload, *payload_start;
+ int ret = 0, par_len = 0;
+ u8 token, index = 0;
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+
+ if (!sta)
+ return 0;
+
+ if (!sta->scs_resp)
+ sta->scs_resp =
+ (struct hostapd_scs_resp *)os_zalloc(sizeof(struct hostapd_scs_resp));
+
+ if (sta->scs_resp == NULL) {
+ wpa_printf(MSG_ERROR, "%s warn: mem alloc failure size %d\n", __func__,
+ (int)sizeof(struct hostapd_scs_resp));
+ return 0;
+ }
+
+ token = mgmt->u.action.u.scs.dialog_token;
+ payload_start = mgmt->u.action.u.scs.variable;
+
+ while ((par_len < len) && (index < HOSTAPD_SCS_MAX_SIZE)) {
+ payload = payload_start;
+ payload = hostapd_parse_scs_desc(payload, &scs);
+ if (!payload)
+ break;
+
+ os_memcpy(scs.peer_mac, mgmt->sa, ETH_ALEN);
+ ret = hostapd_process_scs_req(hapd, sta, payload, &scs);
+ sta->scs_resp->scsid[index] = scs.desc.id;
+ sta->scs_resp->status[index] = ret;
+
+ index++;
+ payload_start += (scs.elem.len + 2);
+ par_len += (scs.elem.len + 2);
+ hostapd_scs_dump(&scs);
+ }
+
+ send_scs_response(hapd, sta, mgmt->sa, token, index);
+ os_free(sta->scs_resp);
+ sta->scs_resp = NULL;
+ return ret;
+}
+
+static uint8_t hostapd_scs_mgmt_fill_hdr(struct hostapd_scs_mgmt_hdr *hdr)
+{
+ hdr->elemid = WLAN_EID_VENDOR_SPECIFIC;
+ hdr->len = 0;
+ hdr->oui[0] = HOSTAPD_SCS_VENDOR_OUI1;
+ hdr->oui[1] = HOSTAPD_SCS_VENDOR_OUI2;
+ hdr->oui[2] = HOSTAPD_SCS_VENDOR_OUI3;
+ hdr->oui_type = QM_IE_OUI_TYPE;
+
+ return sizeof(struct hostapd_scs_mgmt_hdr);
+}
+
+uint8_t hostapd_scs_ie_len(struct hostapd_data *hapd) {
+ return (sizeof(struct hostapd_scs_mgmt_hdr) + HOSTAPD_SCS_ATTR_SIZE);
+}
+
+uint8_t *hostapd_add_scs_cap(uint8_t *frm, bool dscp_policy, bool scs)
+{
+ uint8_t scs_cap_attr = 0;
+ uint8_t ie_len = 0;
+ struct hostapd_scs_mgmt_hdr *hostapd_scs_mgmt_hdr = (struct hostapd_scs_mgmt_hdr *)frm;
+
+ ie_len += hostapd_scs_mgmt_fill_hdr(hostapd_scs_mgmt_hdr);
+ frm += ie_len;
+
+ *frm++ = HOSTAPD_SCS_NO_OF_ATTR;
+ scs_cap_attr |= (dscp_policy << HOSTAPD_DSCP_ACTION_CAP_BIT);
+ scs_cap_attr |= (scs << HOSTAPD_SCS_DESCR_CAP_BIT);
+ *frm++ = scs_cap_attr;
+ hostapd_scs_mgmt_hdr->oui_type = WFA_CAPA_OUI_TYPE;
+ ie_len += HOSTAPD_SCS_ATTR_SIZE;
+
+ hostapd_scs_mgmt_hdr->len = ie_len - HOSTAPD_SCS_ATTR_SIZE;
+ return frm;
+}
+
+void hostapd_handle_scs(struct hostapd_data *hapd, const u8 *buf, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+
+ if (len < IEEE80211_HDRLEN + 3) {
+ wpa_printf(MSG_ERROR, "%s SCS frame length error, len %lu from", __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:
+ break;
+ default:
+ break;
+ }
+}
--- /dev/null
+++ b/src/ap/scs.h
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+struct hostapd_data;
+struct sta_info;
+
+#ifndef SCS_H
+#define SCS_H
+
+#define HOSTAPD_MIN(x, y) ((x > y) ? y : x)
+#define HOSTAPD_MAX(x, y) ((x > y) ? x : y)
+
+#define HOSTAPD_DSCP_ACTION_CAP_BIT 0
+#define HOSTAPD_SCS_DESCR_CAP_BIT 2
+
+/* WME stream classes */
+#define HOSTAPD_SCS_BEST_EFF 0 /* best effort */
+#define HOSTAPD_SCS_BGROUND 1 /* background */
+#define HOSTAPD_SCS_VIDEO 2 /* video */
+#define HOSTAPD_SCS_VOICE 3 /* voice */
+#define HOSTAPD_SCS_MAX_AC 4 /* MAX AC Value */
+
+#define HOSTAPD_SCS_EXTCAP_INDEX 6
+#define HOSTAPD_SCS_EXTCAP_SCS_DESC 0x40
+#define HOSTAPD_SCS_EXTCAP_ROBUST_AV 0x08
+
+#define HOSTAPD_SCS_VENDOR_OUI1 0x50
+#define HOSTAPD_SCS_VENDOR_OUI2 0x6f
+#define HOSTAPD_SCS_VENDOR_OUI3 0x9a
+
+#define HOSTAPD_SCS_NO_OF_ATTR 1
+#define HOSTAPD_SCS_ATTR_SIZE 2
+
+#define HOSTAPD_SCS_QOS_ATTR_CTRL_INFO_DIRECTION_MASK 0x3
+#define HOSTAPD_SCS_QOS_ATTR_CTRL_INFO_DIRECTION_SHIFT 0
+#define HOSTAPD_SCS_QOS_ATTR_CTRL_INFO_TID_MASK 0x3C
+#define HOSTAPD_SCS_QOS_ATTR_CTRL_INFO_TID_SHIFT 2
+#define HOSTAPD_SCS_QOS_ATTR_CTRL_INFO_UP_MASK 0x1C0
+#define HOSTAPD_SCS_QOS_ATTR_CTRL_INFO_UP_SHIFT 6
+#define HOSTAPD_SCS_QOS_ATTR_CTRL_INFO_BMP_MASK 0x1FFFE00
+#define HOSTAPD_SCS_QOS_ATTR_CTRL_INFO_BMP_SHIFT 9
+#define HOSTAPD_SCS_QOS_ATTR_CTRL_INFO_LINK_ID_MASK 0x1E000000
+#define HOSTAPD_SCS_QOS_ATTR_CTRL_INFO_LINK_ID_SHIFT 25
+
+#define HOSTAPD_SCS_QOS_ATTR_CTRL_INFO_LEN 4
+#define HOSTAPD_SCS_QOS_ATTR_SERVICE_INTERVAL_LEN 4
+#define HOSTAPD_SCS_QOS_ATTR_MIN_DATA_RATE_LEN 3
+#define HOSTAPD_SCS_QOS_ATTR_DELAY_BOULND_LEN 3
+#define HOSTAPD_SCS_QOS_ATTR_MAX_MSDU_SIZE_LEN 2
+#define HOSTAPD_SCS_QOS_ATTR_SERVICE_START_TIME_LEN 4
+#define HOSTAPD_SCS_QOS_ATTR_SERVICE_START_TIME_LINK_ID_LEN 1
+#define HOSTAPD_SCS_QOS_ATTR_MEAN_DATA_RATE_LEN 3
+#define HOSTAPD_SCS_QOS_ATTR_BURST_SIZE_LEN 4
+#define HOSTAPD_SCS_QOS_ATTR_MSDU_LIFETIME_LEN 2
+#define HOSTAPD_SCS_QOS_ATTR_MSDU_DELIVERY_RATIO_LEN 1
+#define HOSTAPD_SCS_QOS_ATTR_MSDU_COUNT_EXPONENT_LEN 1
+#define HOSTAPD_SCS_QOS_ATTR_MEDIUM_TIME_LEN 2
+
+#define HOSTAPD_SCS_QOS_ATTR_MAX_MSDU_SIZE_MASK 0x0001
+#define HOSTAPD_SCS_QOS_ATTR_SERVICE_START_TIME_MASK 0x0002
+#define HOSTAPD_SCS_QOS_ATTR_SERVICE_START_TIME_LINK_ID_MASK 0x0004
+#define HOSTAPD_SCS_QOS_ATTR_MEAN_DATA_RATE_MASK 0x0008
+#define HOSTAPD_SCS_QOS_ATTR_BURST_SIZE_MASK 0x0010
+#define HOSTAPD_SCS_QOS_ATTR_MSDU_LIFETIME_MASK 0x0020
+#define HOSTAPD_SCS_QOS_ATTR_MSDU_DELIVERY_RATIO_MASK 0x0040
+#define HOSTAPD_SCS_QOS_ATTR_MSDU_COUNT_EXPONENT_MASK 0x0080
+#define HOSTAPD_SCS_QOS_ATTR_MEDIUM_TIME_MASK 0x0100
+
+#define HOSTAPD_SCS_REQ_TYPE_ADD 0
+#define HOSTAPD_SCS_REQ_TYPE_REMOVE 1
+#define HOSTAPD_SCS_REQ_TYPE_CHANGE 2
+
+#define HOSTAPD_ADD_RULE 1
+#define HOSTAPD_REM_RULE 2
+
+#define HOSTAPD_SCS_REQ_SUCCESS 0
+#define HOSTAPD_SCS_REQ_DECLINED 37
+#define HOSTAPD_SCS_REQ_REJECTED_WITH_SUGGESTED_CHANGES 39
+#define HOSTAPD_SCS_REQ_REQUESTED_TCLAS_NOT_SUPPORTED 56
+#define HOSTAPD_SCS_REQ_INSUFFICIENT_TCLAS_PROCESSING_RESOURCES 57
+#define HOSTAPD_SCS_REQ_TCLAS_PROCESSING_TERMINATED 97
+
+#define HOSTAPD_SCS_PRESENT 1
+#define HOSTAPD_SCS_NOT_PRESENT 0
+#define HOSTAPD_SCS_MEM_NOT_AVAILABLE 2
+
+
+#define le8toh(x) x
+
+#define HOSTAPD_GET_QOS_ATTR_CTRL_INFO(ctrl_det, PARAM) \
+ ((ctrl_det & HOSTAPD_SCS_QOS_ATTR_CTRL_INFO_##PARAM##_MASK) >> \
+ HOSTAPD_SCS_QOS_ATTR_CTRL_INFO_##PARAM##_SHIFT)
+
+#define HOSTAPD_GET_QOS_ATTR_ACTUAL(var, payload, temp_payload, PARAM, bit) \
+ do { \
+ var = (le##bit##toh(*((u##bit *)temp_payload))); \
+ payload += HOSTAPD_SCS_QOS_ATTR_##PARAM##_LEN; \
+ } while (0)
+
+#define HOSTAPD_GET_QOS_ATTR(var, payload, PARAM, bit) \
+ do { \
+ var = (le##bit##toh(*((u##bit *)payload))); \
+ payload += HOSTAPD_SCS_QOS_ATTR_##PARAM##_LEN; \
+ } while (0)
+
+#define HOSTAPD_GET_QOS_ATTR_16(var, payload, PARAM) \
+ do { \
+ var = (le16toh(*((u16 *)payload))); \
+ payload += HOSTAPD_SCS_QOS_ATTR_##PARAM##_LEN; \
+ while (0)
+
+#define HOSTAPD_SCS_IS_QOS_ATTR_PRESENT(PARAM1, PARAM2) \
+ (PARAM1 & HOSTAPD_SCS_QOS_ATTR_##PARAM2##_MASK)
+
+enum ip_version {
+ IPV4 = 4,
+ IPV6 = 6,
+};
+
+struct hostapd_ipv4_params {
+ struct in_addr src_ip;
+ struct in_addr dst_ip;
+ u16 src_port;
+ u16 dst_port;
+ u8 dscp;
+ u8 protocol;
+};
+
+struct hostapd_ipv6_params {
+ struct in6_addr src_ip;
+ struct in6_addr dst_ip;
+ u16 src_port;
+ u16 dst_port;
+ u8 dscp;
+ u8 next_header;
+ u8 flow_label[3];
+} STRUCT_PACKED;
+
+struct hostapd_type4_params {
+ u8 classifier_mask;
+ enum ip_version ip_version;
+ union {
+ struct hostapd_ipv4_params v4;
+ struct hostapd_ipv6_params v6;
+ } ip_params;
+} STRUCT_PACKED;
+
+struct hostapd_type10_params {
+ u8 prot_instance;
+ u8 prot_number;
+ u8 *filter_value;
+ u8 *filter_mask;
+ size_t filter_len;
+} STRUCT_PACKED;
+
+
+struct tclas_element {
+ u8 elem_id;
+ u8 len;
+ u8 up;
+ u8 type;
+ u8 mask;
+ union {
+ struct hostapd_type4_params type4_param;
+ struct hostapd_type10_params type10_param;
+ } frame_classifier;
+} STRUCT_PACKED;
+
+/* Final struct*/
+struct hostapd_element {
+ u8 id;
+ u8 len;
+} STRUCT_PACKED;
+
+struct hostapd_scs_desc {
+ u8 id;
+ u8 req_type;
+} STRUCT_PACKED;
+
+struct hostapd_intra_access_cat {
+ struct hostapd_element elem;
+ u8 priority;
+} STRUCT_PACKED;
+
+struct hostapd_tclas {
+ struct hostapd_element elem;
+} STRUCT_PACKED;
+
+struct hostapd_qos_attr_ctrl_info {
+ u32 direction:2;
+ u32 tid:4;
+ u32 up:3;
+ u32 bitmap:16;
+ u32 link_id:4;
+ u32 reserved:3;
+ u8 access_cat;
+} STRUCT_PACKED;
+
+struct hostapd_qos_mandatory_scs_param {
+ u32 service_interval;
+ u32 delay_bound;
+ u32 burst_size;
+ u32 min_data_rate;
+ u8 peer_mac[ETH_ALEN];
+ u8 scsid;
+ u8 ac;
+ u8 up;
+ u8 direction;
+} STRUCT_PACKED;
+
+struct hostapd_qos_attr {
+ struct hostapd_element elem;
+ struct hostapd_qos_attr_ctrl_info ctrl_info;
+ u32 min_interval;
+ u32 max_interval;
+ u32 final_serv_intvl;
+ u32 min_data_rate;
+ u32 delay_bound;
+ u32 serv_start_time;
+ u32 mean_data_rate;
+ u32 burst_size;
+ u32 final_burst_size;
+ u16 max_msdu_size;
+ u16 msdu_life_time;
+ u16 medium_time;
+ u8 attr_id;
+ u8 serv_start_time_link_id;
+ u8 msdu_delivery_ratio;
+ u8 msdu_cnt_exp;
+} STRUCT_PACKED;
+
+struct hostapd_scs_desc_list {
+ struct hostapd_element elem;
+ struct hostapd_scs_desc desc;
+ struct hostapd_intra_access_cat ia;
+ struct hostapd_tclas tclas;
+ struct hostapd_qos_attr qos_attr;
+ u8 peer_mac[ETH_ALEN];
+} STRUCT_PACKED;
+
+struct hostapd_scs_mgmt_hdr {
+ uint8_t elemid;
+ uint8_t len;
+ uint8_t oui[3];
+ uint8_t oui_type;
+} STRUCT_PACKED;
+
+struct hostapd_scs_resp {
+ u8 scsid[HOSTAPD_SCS_MAX_SIZE];
+ u8 status[HOSTAPD_SCS_MAX_SIZE];
+};
+
+void hostapd_handle_scs(struct hostapd_data *hapd, const u8 *buf, size_t len);
+
+uint8_t *hostapd_add_scs_cap(uint8_t *frm, bool dscp_policy, bool scs);
+
+int hostapd_free_all_scs(struct hostapd_data *hapd, struct sta_info *sta);
+
+uint8_t hostapd_scs_ie_len(struct hostapd_data *hapd);
+#endif
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -39,6 +39,9 @@
#include "sta_info.h"
#include "vlan.h"
#include "wps_hostapd.h"
+#ifdef CONFIG_IEEE80211BE
+#include "scs.h"
+#endif
static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
struct sta_info *sta);
@@ -192,6 +195,7 @@ void ap_free_sta(struct hostapd_data *ha
struct hostapd_data *vlan_bss = hapd;
int set_beacon = 0;
+ hostapd_free_all_scs(hapd, sta);
accounting_sta_stop(hapd, sta);
/* just in case */
@@ -848,6 +852,7 @@ static int ap_sta_remove(struct hostapd_
wpa_printf(MSG_DEBUG, "%s: Removing STA " MACSTR " from kernel driver",
hapd->conf->iface, MAC2STR(sta->addr));
+ hostapd_free_all_scs(hapd, sta);
if (hostapd_drv_sta_remove(hapd, sta->addr) &&
sta->flags & WLAN_STA_ASSOC) {
wpa_printf(MSG_DEBUG, "%s: Could not remove station " MACSTR
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -18,6 +18,8 @@
#include "crypto/sha384.h"
#include "pasn/pasn_common.h"
+#define HOSTAPD_SCS_MAX_SIZE 10
+#define HOSTAPD_SCS_AC_MAX 4
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
#define WLAN_STA_ASSOC BIT(1)
@@ -55,6 +57,8 @@
struct hostapd_data;
struct mld_link_info;
+struct hostapd_scs_desc_list;
+struct hostapd_qos_mandatory_scs_param;
struct mbo_non_pref_chan_info {
struct mbo_non_pref_chan_info *next;
@@ -284,6 +288,7 @@ struct sta_info {
u8 fils_snonce[FILS_NONCE_LEN];
u8 fils_session[FILS_SESSION_LEN];
u8 fils_erp_pmkid[PMKID_LEN];
+
u8 *fils_pending_assoc_req;
size_t fils_pending_assoc_req_len;
unsigned int fils_pending_assoc_is_reassoc:1;
@@ -342,6 +347,12 @@ struct sta_info {
#endif /* CONFIG_IEEE80211BE */
struct ieee80211_240mhz_vendor_oper *eht_240mhz_capab;
size_t eht_240mhz_len;
+
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_qos_mandatory_scs_param *scs_data[HOSTAPD_SCS_AC_MAX];
+ struct hostapd_scs_resp *scs_resp;
+ u8 session_cnt;
+#endif
};
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -943,7 +943,8 @@ struct ieee80211_hdr {
#define IEEE80211_BSSID_FROMDS addr2
#define IEEE80211_SA_FROMDS addr3
-#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr))
+#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr))
+#define IEEE80211_MGMT_MIN_LEN 16
#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
@@ -1121,6 +1122,11 @@ struct ieee80211_mgmt {
u8 dialog_token;
u8 variable[];
} STRUCT_PACKED rrm;
+ struct {
+ u8 action;
+ u8 dialog_token;
+ u8 variable[];
+ } STRUCT_PACKED scs;
} u;
} STRUCT_PACKED action;
} u;
@@ -2585,6 +2591,9 @@ struct ieee80211_eht_operation {
EHT_PHYCAP_MU_BEAMFORMER_160MHZ | \
EHT_PHYCAP_MU_BEAMFORMER_320MHZ)
+#define EHT_PHYCAP_SCS_DESC_IDX 5
+#define EHT_PHYCAP_SCS_DESC_BIT ((u8) BIT(1))
+
/* Figure 9-1002ah: Supported EHT-MCS and NSS Set field format */
#define EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY 4
#define EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS 3
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -14664,6 +14664,13 @@ enum qca_wlan_vendor_attr_scs_rule_confi
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_SERVICE_CLASS_ID = 19,
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_DST_MAC_ADDR = 20,
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_NETDEV_IF_INDEX = 21,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_SERVICE_INTERVAL = 22,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_BURST_SIZE = 23,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_DELAY_BOUND = 24,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_MINIMUM_DATA_RATE = 25,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_USER_PRIORITY = 26,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_ACCESS_CATEGORY = 27,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_DIRECTION = 28,
/* Keep last */
QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_AFTER_LAST,
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -107,6 +107,18 @@ struct hostapd_wmm_rule {
int max_txop;
};
+struct scs_latency_params {
+ u32 service_interval;
+ u32 burst_size;
+ u32 delay_bound;
+ u32 min_data_rate;
+ u8 user_priority;
+ u8 req_type;
+ u8 ac;
+ u8 direction;
+ u8 peer_mac[ETH_ALEN];
+};
+
/**
* struct hostapd_channel_data - Channel information
*/
@@ -5278,6 +5290,9 @@ struct wpa_driver_ops {
*/
struct hostapd_multi_hw_info * (*get_multi_hw_info)(void *priv,
u8 *num_multi_hws);
+#ifdef CONFIG_IEEE80211BE
+ int (*set_scs)(void *priv, struct scs_latency_params *scs_drv);
+#endif
};
/**
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -74,6 +74,7 @@ enum nlmsgerr_attrs {
#endif /* ANDROID */
+
static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
{
struct nl_sock *handle;
@@ -2695,6 +2696,9 @@ static int nl80211_action_subscribe_ap(s
if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
ret = -1;
#endif /* CONFIG_FST */
+ /* Robust AV */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x13", 1) < 0)
+ ret = -1;
/* Vendor-specific */
if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0)
ret = -1;
@@ -11875,7 +11879,6 @@ fail:
return -1;
}
-
static int nl80211_add_sta_node(void *priv, const u8 *addr, u16 auth_alg)
{
struct i802_bss *bss = priv;
@@ -14156,6 +14159,63 @@ wpa_driver_get_multi_hw_info(void *priv,
return nl80211_get_multi_hw_info(priv, num_multi_hws);
}
+#ifdef CONFIG_IEEE80211BE
+#define NL_VENDOR_SET_QOS_ATTR(msg, attr, val, bit) \
+ do { \
+ int ret; \
+ ret = nla_put_u##bit(msg, QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_##attr, val); \
+ if (ret) \
+ goto fail; \
+ } while (0)
+
+static int nl80211_set_scs(void *priv, struct scs_latency_params *scs_drv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *params;
+ int ret;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+ if (!msg)
+ return -EINVAL;
+
+ ret = nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA);
+ if (ret)
+ goto fail;
+
+ ret = nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SCS_RULE_CONFIG);
+ if (ret)
+ goto fail;
+
+ params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!params)
+ goto fail;
+
+ NL_VENDOR_SET_QOS_ATTR(msg, SERVICE_INTERVAL, scs_drv->service_interval, 32);
+ NL_VENDOR_SET_QOS_ATTR(msg, BURST_SIZE, scs_drv->burst_size, 32);
+ NL_VENDOR_SET_QOS_ATTR(msg, DELAY_BOUND, scs_drv->delay_bound, 32);
+ NL_VENDOR_SET_QOS_ATTR(msg, MINIMUM_DATA_RATE, scs_drv->min_data_rate, 32);
+ NL_VENDOR_SET_QOS_ATTR(msg, REQUEST_TYPE, scs_drv->req_type, 8);
+ NL_VENDOR_SET_QOS_ATTR(msg, USER_PRIORITY, scs_drv->user_priority, 8);
+ NL_VENDOR_SET_QOS_ATTR(msg, ACCESS_CATEGORY, scs_drv->ac, 8);
+ NL_VENDOR_SET_QOS_ATTR(msg, DIRECTION, scs_drv->direction, 8);
+
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_DST_MAC_ADDR,
+ ETH_ALEN, scs_drv->peer_mac);
+
+ nla_nest_end(msg, params);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ wpa_printf(MSG_ERROR, "%s failed to set qos attr\n", __func__);
+ return -EINVAL;
+}
+#endif
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
@@ -14314,6 +14374,7 @@ const struct wpa_driver_ops wpa_driver_n
.if_link_remove = driver_nl80211_if_link_remove,
.get_drv_shared_status = wpa_driver_get_shared_status,
.can_share_drv = wpa_driver_nl80211_can_share_drv,
+ .set_scs = nl80211_set_scs,
#endif /* CONFIG_IEEE80211BE */
.get_multi_hw_info = wpa_driver_get_multi_hw_info,
};