blob: f34a8adafcdd679d1db8632cc1d313a6f45565bd [file] [log] [blame]
From d46df816cadde59bde528c08db281923874152cc Mon Sep 17 00:00:00 2001
From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
Date: Tue, 23 Apr 2024 16:03:19 +0800
Subject: [PATCH 085/126] mtk: hostapd: add critical update support
Add critical update support
modification: wmm configuration
inclusion: channel switch
(affiliated link's per-STA profile CSA/eCSA countdown &
channel switch wrapper is included)
Note that max channel switch time IE is not implemented
in hostapd yet.
1.Add max channel switch time IE
Note that fw will find the MCST IE on its own,
so there is no need to send the offset of the MCST IE to the kernel.
The MCST's value is currently hardcoded to 500 TUs, since the WFA test plan
restricts it to not exceeding 500 TUs (APUT 4.22.1).
2. Add critical update support for radar triggered channel switch
According to the test plan of APUT 4.44, the CSA after beacon should also
include the CU flag and increase BPCC due to operation IE modification.
Additionally, avoid setting beacons in hostapd_event_ch_switch after CSA
is finished since it would conflict with the CSA after beacon.
Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
---
hostapd/ctrl_iface.c | 88 +++++++++++++++++++
hostapd/hostapd_cli.c | 8 ++
hostapd/main.c | 4 +-
src/ap/beacon.c | 161 ++++++++++++++++++++++++++++++++++-
src/ap/beacon.h | 37 ++++++++
src/ap/dfs.c | 12 +++
src/ap/drv_callbacks.c | 1 -
src/ap/hostapd.c | 85 +++++++++++++++++-
src/ap/hostapd.h | 5 ++
src/ap/ieee802_11.c | 15 ++++
src/ap/ieee802_11_eht.c | 7 +-
src/ap/sta_info.h | 6 ++
src/ap/ucode.c | 9 +-
src/common/ieee802_11_defs.h | 1 +
src/drivers/driver.h | 1 +
src/drivers/driver_nl80211.c | 53 +++++++-----
src/drivers/nl80211_copy.h | 8 ++
17 files changed, 470 insertions(+), 31 deletions(-)
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index e19502d1d..ebe9053cf 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -2911,6 +2911,18 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
ret = err;
num_err++;
}
+
+#ifdef CONFIG_IEEE80211BE
+ if (iface->bss[i]->conf->mld_ap)
+ hostapd_update_aff_link_beacon(iface->bss[i], settings.cs_count);
+
+ /*
+ * Currently, no FW notification event for clearing CU flag after DTIM period.
+ * Also, another CU or set beacon is not allowed during CSA period.
+ * Therefore, just clear it manually here for workaround.
+ */
+ iface->bss[i]->eht_mld_bss_critical_update = 0;
+#endif /* CONFIG_IEEE80211BE */
}
return (iface->num_bss == num_err) ? ret : 0;
@@ -5279,6 +5291,79 @@ hostapd_ctrl_iface_dump_csi(struct hostapd_data *hapd, char *cmd,
return 0;
}
+static int
+hostapd_ctrl_iface_wmm(struct hostapd_data *hapd, char *cmd, char *buf,
+ size_t buflen)
+{
+ char *pos = cmd, *ac, *token, *context = NULL;
+ struct hostapd_wmm_ac_params *acp;
+ int num;
+
+ if (!hapd->conf->mld_ap)
+ return -1;
+
+ ac = pos;
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ *pos++ = '\0';
+
+ if (os_strncmp(ac, "BE", 2) == 0) {
+ num = 0;
+ } else if (os_strncmp(ac, "BK", 2) == 0) {
+ num = 1;
+ } else if (os_strncmp(ac, "VI", 2) == 0) {
+ num = 2;
+ } else if (os_strncmp(ac, "VO", 2) == 0) {
+ num = 3;
+ } else {
+ wpa_printf(MSG_ERROR, "Unknown AC name '%s'", ac);
+ return -1;
+ }
+
+ acp = &hapd->iconf->wmm_ac_params[num];
+
+ /* if only ac is provied, show wmm params */
+ if (!pos)
+ return os_snprintf(buf, buflen,
+ "link=%d ac=%s cwmin=%d cwmax=%d aifs=%d txop_limit=%d\n",
+ hapd->mld_link_id, ac, acp->cwmin, acp->cwmax, acp->aifs, acp->txop_limit);
+
+ while ((token = str_token(pos, " ", &context))) {
+ if (os_strncmp(token, "cwmin=", 6) == 0) {
+ acp->cwmin = atoi(token + 6);
+ continue;
+ }
+
+ if (os_strncmp(token, "cwmax=", 6) == 0) {
+ acp->cwmax = atoi(token + 6);
+ continue;
+ }
+
+ if (os_strncmp(token, "aifs=", 5) == 0) {
+ acp->aifs = atoi(token + 5);
+ continue;
+ }
+
+ if (os_strncmp(token, "txop_limit=", 11) == 0) {
+ acp->txop_limit = atoi(token + 11);
+ continue;
+ }
+
+ wpa_printf(MSG_ERROR, "CTRL: Invalid WMM parameter: %s", token);
+ return -1;
+ }
+
+ if (acp->cwmin > acp->cwmax)
+ return -1;
+
+ ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EDCA);
+
+ if (ieee802_11_set_beacon(hapd))
+ return -1;
+
+ return os_snprintf(buf, buflen, "OK\n");
+}
+
static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
char *buf, char *reply,
int reply_size,
@@ -5946,6 +6031,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strncmp(buf, "DUMP_CSI ", 8) == 0) {
reply_len = hostapd_ctrl_iface_dump_csi(hapd, buf + 9,
reply, reply_size);
+ } else if (os_strncmp(buf, "WMM", 3) == 0) {
+ reply_len = hostapd_ctrl_iface_wmm(hapd, buf + 4,
+ reply, reply_size);
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 4e94db21d..e578c5b7e 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1767,6 +1767,12 @@ static int hostapd_cli_cmd_get_pp(struct wpa_ctrl *ctrl, int argc,
return hostapd_cli_cmd(ctrl, "get_pp", 1, argc, argv);
}
+static int hostapd_cli_cmd_wmm(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "WMM", 1, argc, argv);
+}
+
struct hostapd_cli_cmd {
const char *cmd;
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -2023,6 +2029,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
" = Set preamble puncture mode"},
{ "get_pp", hostapd_cli_cmd_get_pp, NULL,
" = Get preamble puncture status"},
+ { "wmm", hostapd_cli_cmd_wmm, NULL,
+ " = <ac> [cwmin=] [cwmax=] [aifs=] [txop_limit=]"},
{ NULL, NULL, NULL, NULL }
};
diff --git a/hostapd/main.c b/hostapd/main.c
index e790f18ce..8ecec6c1a 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -330,8 +330,8 @@ setup_mld:
return -1;
}
- /* Initialize the BSS parameter change to 1 */
- hapd->eht_mld_bss_param_change = 1;
+ /* Initialize the BSS parameter change to 0 */
+ hapd->eht_mld_bss_param_change = 0;
wpa_printf(MSG_DEBUG,
"MLD: Set link_id=%u, mld_addr=" MACSTR
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 88e35acc2..9b5c4fc1e 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -514,6 +514,23 @@ static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid)
}
+static u8 * hostapd_eid_max_chsw_time(struct hostapd_data *hapd, u8 *eid)
+{
+ u32 max_chsw_time = 800;
+
+ if (!hapd->cs_freq_params.channel)
+ return eid;
+
+ *eid++ = WLAN_EID_EXTENSION;
+ *eid++ = 4;
+ *eid++ = WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME;
+ WPA_PUT_LE24(eid, max_chsw_time);
+ eid += 3;
+
+ return eid;
+}
+
+
static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid)
{
u8 op_class, channel;
@@ -878,6 +895,7 @@ static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
#endif /* CONFIG_IEEE80211AX */
pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
+ pos = hostapd_eid_max_chsw_time(hapd, pos);
if (!params->is_ml_sta_info)
pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP,
@@ -2185,6 +2203,63 @@ static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
#endif /* CONFIG_FILS */
+static void hostapd_fill_bcn_sta_profile(struct hostapd_data *hapd,
+ struct mld_info *info)
+{
+ struct hostapd_data *h;
+
+ if (!info)
+ return;
+
+ os_memset(info, 0, sizeof(*info));
+
+ for_each_mld_link(h, hapd) {
+ unsigned int link_id = h->mld_link_id;
+ struct mld_link_info *link = &info->links[link_id];
+ u8 *epos, *csa_pos, buf[EHT_ML_MAX_STA_PROF_LEN];
+
+ if (!h->started || h == hapd ||
+ h->eht_mld_bss_critical_update != BSS_CRIT_UPDATE_ALL)
+ continue;
+
+ link->valid = true;
+ os_memcpy(link->local_addr, h->own_addr, ETH_ALEN);
+
+ /* Build per-STA profile */
+ epos = buf;
+ /* Capabilities */
+ WPA_PUT_LE16(epos, hostapd_own_capab_info(h));
+ epos += 2;
+
+ /* CSA IE */
+ csa_pos = hostapd_eid_csa(h, epos);
+ if (csa_pos != epos)
+ link->sta_prof_csa_offset = csa_pos - 1 - buf;
+ epos = csa_pos;
+
+ /* eCSA IE */
+ csa_pos = hostapd_eid_ecsa(h, epos);
+ if (csa_pos != epos)
+ link->sta_prof_ecsa_offset = csa_pos - 1 - buf;
+ epos = csa_pos;
+
+ /* channel switch wrapper */
+ epos = hostapd_eid_wb_chsw_wrapper(h, epos);
+
+ /* max channel switch time */
+ epos = hostapd_eid_max_chsw_time(h, epos);
+
+ link->resp_sta_profile_len = epos - buf;
+ link->resp_sta_profile = os_memdup(buf, link->resp_sta_profile_len);
+
+ /* TODO:
+ * 1. add other IEs
+ * 2. handle per-STA profile inheritance
+ * 3. handle csa offset if fragmentation is required
+ */
+ }
+}
+
int ieee802_11_build_ap_params(struct hostapd_data *hapd,
struct wpa_driver_ap_params *params)
{
@@ -2394,6 +2469,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
#endif /* CONFIG_IEEE80211AX */
tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
+ tailpos = hostapd_eid_max_chsw_time(hapd, tailpos);
tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true);
tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
@@ -2423,9 +2499,30 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
- if (hapd->conf->mld_ap)
- tailpos = hostapd_eid_eht_ml_beacon(hapd, NULL,
+ if (hapd->conf->mld_ap) {
+ struct hostapd_data *h;
+ struct mld_info info;
+ struct mld_link_info *link;
+ u32 base;
+ u8 link_id, *ml_pos = tailpos;
+
+ hostapd_fill_bcn_sta_profile(hapd, &info);
+ tailpos = hostapd_eid_eht_ml_beacon(hapd, &info,
tailpos, false);
+
+ for_each_mld_link(h, hapd) {
+ link_id = h->mld_link_id;
+ link = &info.links[link_id];
+ base = ml_pos - tail + link->sta_prof_offset;
+ if (link->sta_prof_csa_offset)
+ hapd->cs_c_off_sta_prof[link_id] =
+ base + link->sta_prof_csa_offset;
+ if (link->sta_prof_ecsa_offset)
+ hapd->cs_c_off_ecsa_sta_prof[link_id] =
+ base + link->sta_prof_ecsa_offset;
+ }
+ ap_sta_free_sta_profile(&info);
+ }
tailpos = hostapd_eid_eht_capab(hapd, tailpos,
IEEE80211_MODE_AP);
tailpos = hostapd_eid_eht_operation(hapd, tailpos);
@@ -2803,7 +2900,8 @@ void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd)
int ieee802_11_set_beacon(struct hostapd_data *hapd)
{
struct hostapd_iface *iface = hapd->iface;
- int ret;
+ struct hostapd_data *h;
+ int ret, link_id;
size_t i, j;
bool is_6g, hapd_mld = false;
@@ -2846,6 +2944,17 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
}
}
+#ifdef CONFIG_IEEE80211BE
+ /* clear critical update flag for UPDATE_SINGLE type, for other types,
+ * we should get some notified events from driver
+ */
+ if (hapd->conf->mld_ap) {
+ for_each_mld_link(h, hapd)
+ if (h->eht_mld_bss_critical_update == BSS_CRIT_UPDATE_SINGLE)
+ h->eht_mld_bss_critical_update = 0;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
return 0;
}
@@ -2880,4 +2989,50 @@ int ieee802_11_update_beacons(struct hostapd_iface *iface)
return ret;
}
+
+int ieee802_11_set_bss_critical_update(struct hostapd_data *hapd,
+ enum bss_crit_update_event event)
+{
+ if (!hapd->conf->mld_ap)
+ return 0;
+
+ switch (event) {
+ case BSS_CRIT_UPDATE_EVENT_CSA:
+ case BSS_CRIT_UPDATE_EVENT_ECSA:
+ case BSS_CRIT_UPDATE_EVENT_QUIET:
+ case BSS_CRIT_UPDATE_EVENT_WBCS:
+ case BSS_CRIT_UPDATE_EVENT_CS_WRAP:
+ case BSS_CRIT_UPDATE_EVENT_OP_MODE_NOTIF:
+ case BSS_CRIT_UPDATE_EVENT_QUIET_CH:
+ case BSS_CRIT_UPDATE_EVENT_CCA:
+ case BSS_CRIT_UPDATE_EVENT_BCAST_TWT:
+ case BSS_CRIT_UPDATE_EVENT_BCAST_TWT_PARAM_SET:
+ case BSS_CRIT_UPDATE_EVENT_IDX_ADJUST_FACTOR:
+ case BSS_CRIT_UPDATE_EVENT_TPE:
+ hapd->eht_mld_bss_param_change += 1;
+ hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_ALL;
+ return 0;
+ case BSS_CRIT_UPDATE_EVENT_EDCA:
+ case BSS_CRIT_UPDATE_EVENT_DSSS:
+ case BSS_CRIT_UPDATE_EVENT_HT_OPERATION:
+ case BSS_CRIT_UPDATE_EVENT_VHT_OPERATION:
+ case BSS_CRIT_UPDATE_EVENT_HE_OPERATION:
+ case BSS_CRIT_UPDATE_EVENT_MU_EDCA:
+ case BSS_CRIT_UPDATE_EVENT_SR:
+ case BSS_CRIT_UPDATE_EVENT_UORA:
+ case BSS_CRIT_UPDATE_EVENT_EHT_OPERATION:
+ hapd->eht_mld_bss_param_change += 1;
+ hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_SINGLE;
+ return 0;
+ case BSS_CRIT_UPDATE_EVENT_RECONFIG:
+ case BSS_CRIT_UPDATE_EVENT_ADD_LINK:
+ case BSS_CRIT_UPDATE_EVENT_ATTLM:
+ hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_FLAG;
+ return 0;
+ default:
+ hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_NONE;
+ return -1;
+ }
+}
+
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index e381542a8..809393902 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -12,12 +12,49 @@
struct ieee80211_mgmt;
+enum bss_crit_update_event {
+ BSS_CRIT_UPDATE_EVENT_CSA,
+ BSS_CRIT_UPDATE_EVENT_ECSA,
+ BSS_CRIT_UPDATE_EVENT_EDCA,
+ BSS_CRIT_UPDATE_EVENT_QUIET,
+ BSS_CRIT_UPDATE_EVENT_DSSS,
+ BSS_CRIT_UPDATE_EVENT_HT_OPERATION,
+ BSS_CRIT_UPDATE_EVENT_WBCS,
+ BSS_CRIT_UPDATE_EVENT_CS_WRAP,
+ BSS_CRIT_UPDATE_EVENT_OP_MODE_NOTIF,
+ BSS_CRIT_UPDATE_EVENT_QUIET_CH,
+ BSS_CRIT_UPDATE_EVENT_VHT_OPERATION,
+ BSS_CRIT_UPDATE_EVENT_HE_OPERATION,
+ BSS_CRIT_UPDATE_EVENT_BCAST_TWT,
+ BSS_CRIT_UPDATE_EVENT_BCAST_TWT_PARAM_SET,
+ BSS_CRIT_UPDATE_EVENT_CCA,
+ BSS_CRIT_UPDATE_EVENT_MU_EDCA,
+ BSS_CRIT_UPDATE_EVENT_SR,
+ BSS_CRIT_UPDATE_EVENT_UORA,
+ BSS_CRIT_UPDATE_EVENT_IDX_ADJUST_FACTOR,
+ BSS_CRIT_UPDATE_EVENT_EHT_OPERATION,
+ BSS_CRIT_UPDATE_EVENT_TPE,
+ BSS_CRIT_UPDATE_EVENT_CH_CHANGED,
+ BSS_CRIT_UPDATE_EVENT_RECONFIG,
+ BSS_CRIT_UPDATE_EVENT_ADD_LINK,
+ BSS_CRIT_UPDATE_EVENT_ATTLM
+};
+
+enum {
+ BSS_CRIT_UPDATE_NONE,
+ BSS_CRIT_UPDATE_SINGLE,
+ BSS_CRIT_UPDATE_ALL,
+ BSS_CRIT_UPDATE_FLAG
+};
+
void handle_probe_req(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int ssi_signal);
void ieee802_11_set_beacon_per_bss_only(struct hostapd_data *hapd);
int ieee802_11_set_beacon(struct hostapd_data *hapd);
int ieee802_11_set_beacons(struct hostapd_iface *iface);
+int ieee802_11_set_bss_critical_update(struct hostapd_data *hapd,
+ enum bss_crit_update_event event);
int ieee802_11_update_beacons(struct hostapd_iface *iface);
int ieee802_11_build_ap_params(struct hostapd_data *hapd,
struct wpa_driver_ap_params *params);
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index fc7699973..697e6364c 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -1120,6 +1120,18 @@ static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
err = hostapd_switch_channel(iface->bss[i], &csa_settings);
if (err)
num_err++;
+
+#ifdef CONFIG_IEEE80211BE
+ if (iface->bss[i]->conf->mld_ap)
+ hostapd_update_aff_link_beacon(iface->bss[i], csa_settings.cs_count);
+
+ /*
+ * Currently, no FW notification event for clearing CU flag after DTIM period.
+ * Also, another CU or set beacon is not allowed during CSA period.
+ * Therefore, just clear it manually here for workaround.
+ */
+ iface->bss[i]->eht_mld_bss_critical_update = 0;
+#endif /* CONFIG_IEEE80211BE */
}
if (num_err == iface->num_bss) {
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 1107fd70e..705acfb67 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -1314,7 +1314,6 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
if (hapd->csa_in_progress &&
freq == hapd->cs_freq_params.freq) {
hostapd_cleanup_cs_params(hapd);
- ieee802_11_set_beacon(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
"freq=%d dfs=%d", freq, is_dfs);
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 2c9e54b23..f03a6242f 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -4524,6 +4524,9 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
old_punct_bitmap = iface->conf->punct_bitmap;
iface->conf->punct_bitmap = settings->punct_bitmap;
#endif /* CONFIG_IEEE80211BE */
+
+ /* Another CU in the new channel due to OP element modification */
+ ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EHT_OPERATION);
ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
/* change back the configuration */
@@ -4541,20 +4544,32 @@ static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
hapd->cs_count = settings->cs_count;
hapd->cs_block_tx = settings->block_tx;
+#ifdef CONFIG_IEEE80211BE
+ /* Restore BPCC to build the CSA beacon */
+ hapd->eht_mld_bss_param_change--;
+ hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_ALL;
+#endif /* CONFIG_IEEE80211BE */
+
ret = hostapd_build_beacon_data(hapd, &settings->beacon_csa);
if (ret) {
free_beacon_data(&settings->beacon_after);
return ret;
}
+ /* Change back to the final BPCC and CU flag */
+ ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EHT_OPERATION);
+
settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon;
settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp;
settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon;
settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp;
settings->link_id = -1;
+ settings->freq_params.link_id = -1;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap)
+ if (hapd->conf->mld_ap) {
settings->link_id = hapd->mld_link_id;
+ settings->freq_params.link_id = hapd->mld_link_id;
+ }
#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_IEEE80211AX
@@ -4616,6 +4631,8 @@ int hostapd_switch_channel(struct hostapd_data *hapd,
return -1;
}
+ ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_CSA);
+
ret = hostapd_fill_csa_settings(hapd, settings);
if (ret)
return ret;
@@ -4637,6 +4654,72 @@ int hostapd_switch_channel(struct hostapd_data *hapd,
return 0;
}
+int hostapd_update_aff_link_beacon(struct hostapd_data *hapd, u8 cs_count)
+{
+ struct hostapd_data *h;
+ unsigned int cs_link_id = hapd->mld_link_id;
+ int cs_channel = hapd->cs_freq_params.channel;
+
+ /* TODO: add beacon offload driver flag */
+ for_each_mld_link(h, hapd) {
+ struct hostapd_config *conf = h->iconf;
+ struct hostapd_hw_modes *mode = h->iface->current_mode;
+ struct csa_settings settings = {};
+ unsigned int link_id = h->mld_link_id;
+ int ret;
+
+ if (!h->started || h == hapd)
+ continue;
+
+ hostapd_set_freq_params(&settings.freq_params, conf->hw_mode,
+ hostapd_hw_get_freq(h, conf->channel),
+ conf->channel, conf->enable_edmg,
+ conf->edmg_channel, conf->ieee80211n,
+ conf->ieee80211ac, conf->ieee80211ax,
+ conf->ieee80211be, conf->secondary_channel,
+ hostapd_get_oper_chwidth(conf),
+ hostapd_get_oper_centr_freq_seg0_idx(conf),
+ hostapd_get_oper_centr_freq_seg1_idx(conf),
+ conf->vht_capab,
+ mode ? &mode->he_capab[IEEE80211_MODE_AP] : NULL,
+ mode ? &mode->eht_capab[IEEE80211_MODE_AP] : NULL,
+ hostapd_get_punct_bitmap(h));
+ hapd->cs_freq_params.channel = 0;
+ ret = hostapd_build_beacon_data(h, &settings.beacon_after);
+ if (ret)
+ return ret;
+
+ hapd->cs_freq_params.channel = cs_channel;
+ /* Restore BPCC to build the RNR for the CS link */
+ hapd->eht_mld_bss_param_change--;
+ hapd->eht_mld_bss_critical_update = BSS_CRIT_UPDATE_ALL;
+ ret = hostapd_build_beacon_data(h, &settings.beacon_csa);
+ if (ret) {
+ free_beacon_data(&settings.beacon_after);
+ return ret;
+ }
+
+ /* Change back to the final BPCC and CU flag */
+ ieee802_11_set_bss_critical_update(hapd, BSS_CRIT_UPDATE_EVENT_EHT_OPERATION);
+
+ settings.counter_offset_sta_prof[cs_link_id][0] =
+ h->cs_c_off_sta_prof[cs_link_id];
+ settings.counter_offset_sta_prof[cs_link_id][1] =
+ h->cs_c_off_ecsa_sta_prof[cs_link_id];
+ settings.link_id = cs_link_id;
+ settings.freq_params.link_id = link_id;
+ settings.cs_count = cs_count;
+ settings.punct_bitmap = conf->punct_bitmap;
+ ret = hostapd_drv_switch_channel(h, &settings);
+ free_beacon_data(&settings.beacon_csa);
+ free_beacon_data(&settings.beacon_after);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
void
hostapd_switch_channel_fallback(struct hostapd_iface *iface,
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 4db67096b..99d5a01d6 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -342,6 +342,9 @@ struct hostapd_data {
unsigned int cs_c_off_ecsa_beacon;
unsigned int cs_c_off_ecsa_proberesp;
+ unsigned int cs_c_off_sta_prof[MAX_NUM_MLD_LINKS];
+ unsigned int cs_c_off_ecsa_sta_prof[MAX_NUM_MLD_LINKS];
+
#ifdef CONFIG_IEEE80211AX
bool cca_in_progress;
u8 cca_count;
@@ -501,6 +504,7 @@ struct hostapd_data {
#ifdef CONFIG_IEEE80211BE
u8 eht_mld_bss_param_change;
+ u8 eht_mld_bss_critical_update;
struct hostapd_mld *mld;
struct dl_list link;
u8 mld_link_id;
@@ -850,6 +854,7 @@ void hostapd_chan_switch_config(struct hostapd_data *hapd,
struct hostapd_freq_params *freq_params);
int hostapd_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings);
+int hostapd_update_aff_link_beacon(struct hostapd_data *hapd, u8 cs_count);
void
hostapd_switch_channel_fallback(struct hostapd_iface *iface,
const struct hostapd_freq_params *freq_params);
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 1365bc822..bb508fe79 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -282,6 +282,7 @@ u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
u16 hostapd_own_capab_info(struct hostapd_data *hapd)
{
+ struct hostapd_data *h;
int capab = WLAN_CAPABILITY_ESS;
int privacy = 0;
int dfs;
@@ -342,6 +343,18 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd)
}
}
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ for_each_mld_link(h, hapd) {
+ if (h->eht_mld_bss_critical_update) {
+ capab |= WLAN_CAPABILITY_PBCC;
+ break;
+ }
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+
return capab;
}
@@ -7838,6 +7851,8 @@ static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
(MAX_NUM_MLD_LINKS | 0xF0);
/* BPCC (Bit 3 to Bit 0) */
*eid = is_partner ? ((param_ch & 0xF0) >> 4) : 0x0F;
+ if (bss->eht_mld_bss_critical_update == BSS_CRIT_UPDATE_ALL)
+ *eid |= RNR_TBTT_INFO_MLD_PARAM2_ALL_UPDATE_INC;
#ifdef CONFIG_TESTING_OPTIONS
if (bss->conf->mld_indicate_disabled)
*eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index a04eb23e0..2b5c06d6d 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -495,7 +495,7 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
wpabuf_put_u8(buf, hapd->mld_link_id);
/* Currently hard code the BSS Parameters Change Count to 0x1 */
- wpabuf_put_u8(buf, 0x1);
+ wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
wpa_printf(MSG_DEBUG, "MLD: EML Capabilities=0x%x",
hapd->iface->mld_eml_capa);
@@ -593,11 +593,14 @@ static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
wpabuf_put_u8(buf, link_bss->conf->dtim_period);
/* BSS Parameters Change Count */
- wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
+ wpabuf_put_u8(buf, link_bss->eht_mld_bss_param_change);
if (!link->resp_sta_profile)
continue;
+#define EXT_EID_TAG_LEN 3
+ link->sta_prof_offset = wpabuf_len(buf) + EXT_EID_TAG_LEN;
+
/* Fragment the sub element if needed */
if (total_len <= 255) {
wpabuf_put_data(buf, link->resp_sta_profile,
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 67b97671a..60b33f049 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -89,6 +89,12 @@ struct mld_info {
u16 status;
u16 resp_sta_profile_len;
u8 *resp_sta_profile;
+
+ u32 sta_prof_csa_offset;
+ u32 sta_prof_ecsa_offset;
+ u32 sta_prof_offset;
+
+ const u8 *rsne, *rsnxe;
} links[MAX_NUM_MLD_LINKS];
};
diff --git a/src/ap/ucode.c b/src/ap/ucode.c
index da1c4c1ac..a72193282 100644
--- a/src/ap/ucode.c
+++ b/src/ap/ucode.c
@@ -738,9 +738,16 @@ uc_hostapd_iface_switch_channel(uc_vm_t *vm, size_t nargs)
wpa_printf(MSG_INFO, " * center_freq2 is %d\n",
csa.freq_params.center_freq2);
- for (i = 0; i < iface->num_bss; i++)
+ for (i = 0; i < iface->num_bss; i++) {
ret = hostapd_switch_channel(iface->bss[i], &csa);
+ if (iface->bss[i]->conf->mld_ap)
+ hostapd_update_aff_link_beacon(iface->bss[i], csa.cs_count);
+
+ /* FIXME: remove this line after CU event merged */
+ iface->bss[i]->eht_mld_bss_critical_update = 0;
+ }
+
return ucv_boolean_new(!ret);
}
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index c380d0c7e..efb584c66 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -504,6 +504,7 @@
#define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38
#define WLAN_EID_EXT_SPATIAL_REUSE 39
#define WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT 42
+#define WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME 52
#define WLAN_EID_EXT_OCV_OCI 54
#define WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION 55
#define WLAN_EID_EXT_NON_INHERITANCE 56
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index b75580374..924a1baba 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -2763,6 +2763,7 @@ struct csa_settings {
u16 counter_offset_beacon[2];
u16 counter_offset_presp[2];
+ u16 counter_offset_sta_prof[MAX_NUM_MLD_LINKS][2];
u16 punct_bitmap;
int link_id;
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 8b64c3983..555c97bf7 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -11417,9 +11417,10 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nlattr *beacon_csa;
- int ret = -ENOBUFS;
- int csa_off_len = 0;
- int i;
+ int i, csa_off_len = 0, ret = -ENOBUFS;
+ unsigned int cs_link_id = settings->link_id;
+ u16 *counter_offset_beacon = settings->counter_offset_beacon;
+ u16 *counter_offset_presp = settings->counter_offset_presp;
wpa_printf(MSG_DEBUG,
"nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d channel=%d sec_channel_offset=%d width=%d cf1=%d cf2=%d puncturing_bitmap=0x%04x link_id=%d%s%s%s)",
@@ -11431,7 +11432,7 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
settings->freq_params.center_freq1,
settings->freq_params.center_freq2,
settings->punct_bitmap,
- settings->link_id,
+ settings->freq_params.link_id,
settings->freq_params.ht_enabled ? " ht" : "",
settings->freq_params.vht_enabled ? " vht" : "",
settings->freq_params.he_enabled ? " he" : "");
@@ -11451,18 +11452,19 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
* counters match. This implementation assumes that there are only two
* counters.
*/
- if (settings->counter_offset_beacon[0] &&
- !settings->counter_offset_beacon[1]) {
+ if (cs_link_id != settings->freq_params.link_id) {
+ counter_offset_beacon = settings->counter_offset_sta_prof[cs_link_id];
+ counter_offset_presp = NULL;
+ }
+
+ if (counter_offset_beacon[0] && !counter_offset_beacon[1]) {
csa_off_len = 1;
- } else if (settings->counter_offset_beacon[1] &&
- !settings->counter_offset_beacon[0]) {
+ } else if (counter_offset_beacon[1] && !counter_offset_beacon[0]) {
csa_off_len = 1;
- settings->counter_offset_beacon[0] =
- settings->counter_offset_beacon[1];
- settings->counter_offset_presp[0] =
- settings->counter_offset_presp[1];
- } else if (settings->counter_offset_beacon[1] &&
- settings->counter_offset_beacon[0]) {
+ counter_offset_beacon[0] = counter_offset_beacon[1];
+ if (counter_offset_presp)
+ counter_offset_presp[0] = counter_offset_presp[1];
+ } else if (counter_offset_beacon[1] && counter_offset_beacon[0]) {
csa_off_len = 2;
} else {
wpa_printf(MSG_ERROR, "nl80211: No CSA counters provided");
@@ -11481,14 +11483,18 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
return -EINVAL;
for (i = 0; i < csa_off_len; i++) {
- u16 csa_c_off_bcn = settings->counter_offset_beacon[i];
- u16 csa_c_off_presp = settings->counter_offset_presp[i];
+ u16 csa_c_off_bcn = counter_offset_beacon[i];
+ u16 csa_c_off_presp;
if ((settings->beacon_csa.tail_len <= csa_c_off_bcn) ||
(settings->beacon_csa.tail[csa_c_off_bcn] !=
settings->cs_count))
return -EINVAL;
+ if (!counter_offset_presp)
+ continue;
+
+ csa_c_off_presp = counter_offset_presp[i];
if (settings->beacon_csa.probe_resp &&
((settings->beacon_csa.probe_resp_len <=
csa_c_off_presp) ||
@@ -11506,8 +11512,8 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
(settings->punct_bitmap &&
nla_put_u32(msg, NL80211_ATTR_PUNCT_BITMAP,
settings->punct_bitmap)) ||
- (settings->link_id != NL80211_DRV_LINK_ID_NA &&
- nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, settings->link_id)))
+ (settings->freq_params.link_id != NL80211_DRV_LINK_ID_NA &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, settings->freq_params.link_id)))
goto error;
/* beacon_after params */
@@ -11528,9 +11534,14 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
if (ret)
goto error;
- if (nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
- csa_off_len * sizeof(u16),
- settings->counter_offset_beacon) ||
+ if ((cs_link_id == settings->freq_params.link_id &&
+ nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
+ csa_off_len * sizeof(u16),
+ settings->counter_offset_beacon)) ||
+ (cs_link_id != settings->freq_params.link_id &&
+ nla_put(msg, NL80211_ATTR_CSA_C_OFF_STA_PROF,
+ csa_off_len * sizeof(u16),
+ settings->counter_offset_sta_prof[cs_link_id])) ||
(settings->beacon_csa.probe_resp &&
nla_put(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
csa_off_len * sizeof(u16),
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index d425c797c..13837297c 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -2868,6 +2868,10 @@ enum nl80211_commands {
* nested item, it contains attributes defined in
* &enum nl80211_if_combination_attrs.
*
+ * @NL80211_ATTR_CNTDWN_OFFS_STA_PROF: An array of offsets (u16) to the channel
+ * switch or color change counters in the per-STA profile corresponding to
+ * the affected AP.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3418,6 +3422,9 @@ enum nl80211_attrs {
/* add attributes here, update the policy in nl80211.c */
+ /* MTK internal */
+ NL80211_ATTR_CNTDWN_OFFS_STA_PROF,
+
__NL80211_ATTR_AFTER_LAST,
NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
@@ -3430,6 +3437,7 @@ enum nl80211_attrs {
#define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA
#define NL80211_ATTR_CSA_C_OFF_BEACON NL80211_ATTR_CNTDWN_OFFS_BEACON
#define NL80211_ATTR_CSA_C_OFF_PRESP NL80211_ATTR_CNTDWN_OFFS_PRESP
+#define NL80211_ATTR_CSA_C_OFF_STA_PROF NL80211_ATTR_CNTDWN_OFFS_STA_PROF
/*
* Allow user space programs to use #ifdef on new attributes by defining them
--
2.18.0