diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index f77f738e4..aa4dbe9eb 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -883,10 +883,10 @@ int hostapd_drv_set_qos_map(struct hostapd_data *hapd, } -static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, - struct hostapd_hw_modes *mode, - int acs_ch_list_all, - int **freq_list) +void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, + struct hostapd_hw_modes *mode, + int acs_ch_list_all, bool allow_disabled, + int **freq_list) { int i; @@ -912,7 +912,7 @@ static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, (!hapd->iface->conf->ieee80211ax && !hapd->iface->conf->ieee80211be))) continue; - if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && + if ((!(chan->flag & HOSTAPD_CHAN_DISABLED) || allow_disabled) && !(hapd->iface->conf->acs_exclude_dfs && (chan->flag & HOSTAPD_CHAN_RADAR)) && !(chan->max_tx_power < hapd->iface->conf->min_tx_power)) @@ -969,7 +969,7 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd) selected_mode != mode->mode) continue; hostapd_get_hw_mode_any_channels(hapd, mode, acs_ch_list_all, - &freq_list); + false, &freq_list); } params.freq_list = freq_list; diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 93b224499..023cbf1f8 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -156,6 +156,11 @@ int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, void hostapd_get_ext_capa(struct hostapd_iface *iface); +void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, + struct hostapd_hw_modes *mode, + int acs_ch_list_all, bool allow_disabled, + int **freq_list); + static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd, int enabled) { diff --git a/src/ap/beacon.c b/src/ap/beacon.c index d66d83173..de944fed3 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -2058,6 +2058,8 @@ void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params) os_free(params->unsol_bcast_probe_resp_tmpl); params->unsol_bcast_probe_resp_tmpl = NULL; #endif /* CONFIG_IEEE80211AX */ + os_free(params->allowed_freqs); + params->allowed_freqs = NULL; } @@ -2069,7 +2071,8 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd) struct hostapd_config *iconf = iface->conf; struct hostapd_hw_modes *cmode = iface->current_mode; struct wpabuf *beacon, *proberesp, *assocresp; - int res, ret = -1; + int res, ret = -1, i; + struct hostapd_hw_modes *mode; if (!hapd->drv_priv) { wpa_printf(MSG_ERROR, "Interface is disabled"); @@ -2144,6 +2147,19 @@ static int __ieee802_11_set_beacon(struct hostapd_data *hapd) &cmode->eht_capab[IEEE80211_MODE_AP]) == 0) params.freq = &freq; + for (i = 0; i < hapd->iface->num_hw_features; i++) { + mode = &hapd->iface->hw_features[i]; + + if (iconf->hw_mode != HOSTAPD_MODE_IEEE80211ANY && + iconf->hw_mode != mode->mode) + continue; + + hostapd_get_hw_mode_any_channels(hapd, mode, + !(iconf->acs_freq_list.num || + iconf->acs_ch_list.num), + true, ¶ms.allowed_freqs); + } + res = hostapd_drv_set_ap(hapd, ¶ms); hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); if (res) diff --git a/src/drivers/driver.h b/src/drivers/driver.h index bcb0c92d6..101f98a72 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1748,6 +1748,16 @@ struct wpa_driver_ap_params { * The driver will use these to include RNR elements in EMA beacons. */ u8 **rnr_elem_offset; + + /** + * allowed_freqs - List of allowed 20 MHz channel center frequencies in + * MHz for AP operation. Drivers which support this parameter will + * generate a new list based on this provided list by filtering out + * channels that cannot be used at that time due to regulatory or other + * constraints. The resulting list is used as the list of all allowed + * channels whenever performing operations like ACS and DFS. + */ + int *allowed_freqs; }; struct wpa_driver_mesh_bss_params { diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 467f46cf4..28f50f11c 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -4753,6 +4753,52 @@ static int nl80211_mbssid(struct nl_msg *msg, #endif /* CONFIG_IEEE80211AX */ +#ifdef CONFIG_DRIVER_NL80211_QCA +static void qca_set_allowed_ap_freqs(struct wpa_driver_nl80211_data *drv, + const int *freqs, int num_freqs) +{ + struct nl_msg *msg; + struct nlattr *params, *freqs_list; + int i, ret; + + if (!drv->set_wifi_conf_vendor_cmd_avail || !drv->qca_ap_allowed_freqs) + return; + + wpa_printf(MSG_DEBUG, "nl80211: Set AP allowed frequency list"); + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA))) + goto err; + + freqs_list = nla_nest_start( + msg, QCA_WLAN_VENDOR_ATTR_CONFIG_AP_ALLOWED_FREQ_LIST); + if (!freqs_list) + goto err; + + for (i = 0; i < num_freqs; i++) { + if (nla_put_u32(msg, i, freqs[i])) + goto err; + } + + nla_nest_end(msg, freqs_list); + nla_nest_end(msg, params); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); + if (ret) + wpa_printf(MSG_ERROR, + "nl80211: Failed set AP alllowed frequency list: %d (%s)", + ret, strerror(-ret)); + + return; +err: + nlmsg_free(msg); +} +#endif /* CONFIG_DRIVER_NL80211_QCA */ + + static int wpa_driver_nl80211_set_ap(void *priv, struct wpa_driver_ap_params *params) { @@ -5075,6 +5121,12 @@ static int wpa_driver_nl80211_set_ap(void *priv, goto fail; } +#ifdef CONFIG_DRIVER_NL80211_QCA + if (cmd == NL80211_CMD_NEW_BEACON && params->allowed_freqs) + qca_set_allowed_ap_freqs(drv, params->allowed_freqs, + int_array_len(params->allowed_freqs)); +#endif /* CONFIG_DRIVER_NL80211_QCA */ + ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)", diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index 8bfbdd571..38b59ab50 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -199,6 +199,7 @@ struct wpa_driver_nl80211_data { unsigned int uses_6ghz:1; unsigned int secure_ranging_ctx_vendor_cmd_avail:1; unsigned int puncturing:1; + unsigned int qca_ap_allowed_freqs:1; u64 vendor_scan_cookie; u64 remain_on_chan_cookie; diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 27ab1d9c2..b904398ca 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -1405,6 +1405,9 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_AP, &info)) drv->capa.flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP; + if (check_feature(QCA_WLAN_VENDOR_FEATURE_AP_ALLOWED_FREQ_LIST, + &info)) + drv->qca_ap_allowed_freqs = 1; os_free(info.flags); }