Support for S1G in ACS. The key changes here leverage the inclusion of frequency_khz in hostapd. These changes allow hostapd to scan S1G frequencies. This commit includes the nl80211_band to hostapd_hw_modes. The change was made to distinguish different bands who use the same hostapd_hw_mode, such as a/ac/ax/ah. --- src/ap/acs.c | 110 ++++++++++++++++++++++++++++-- src/ap/hw_features.c | 7 ++ src/common/ieee802_11_common.c | 27 ++++++++ src/drivers/driver.h | 16 ++++- src/drivers/driver_nl80211.c | 8 ++- src/drivers/driver_nl80211_capa.c | 2 + src/drivers/driver_nl80211_scan.c | 14 +++- src/drivers/linux_wext.h | 1 + wpa_supplicant/scan.c | 2 + 9 files changed, 173 insertions(+), 14 deletions(-) diff --git a/src/ap/acs.c b/src/ap/acs.c index 8b5760918..826ce4ce1 100644 --- a/src/ap/acs.c +++ b/src/ap/acs.c @@ -452,6 +452,23 @@ static int acs_get_bw_center_chan(int freq, enum bw_type bw) } +static int acs_s1g_chan_get_bw(const struct hostapd_channel_data *chan) +{ + if ((chan->allowed_bw & HOSTAPD_CHAN_WIDTH_1)) + return 0; + else if ((chan->allowed_bw & HOSTAPD_CHAN_WIDTH_2)) + return 1; + else if ((chan->allowed_bw & HOSTAPD_CHAN_WIDTH_4)) + return 2; + else if ((chan->allowed_bw & HOSTAPD_CHAN_WIDTH_8)) + return 3; + else if ((chan->allowed_bw & HOSTAPD_CHAN_WIDTH_16)) + return 4; + else + return -1; +} + + static int acs_survey_is_sufficient(struct freq_survey *survey) { if (!(survey->filled & SURVEY_HAS_NF)) { @@ -622,6 +639,26 @@ static void acs_survey_all_chans_interference_factor( } +static struct hostapd_channel_data * +acs_find_chan_mode_khz(struct hostapd_hw_modes *mode, int freq) +{ + struct hostapd_channel_data *chan; + int i; + + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + if (chan->freq_khz == freq) + return chan; + } + + return NULL; +} + + static struct hostapd_channel_data * acs_find_chan_mode(struct hostapd_hw_modes *mode, int freq) { @@ -634,7 +671,7 @@ acs_find_chan_mode(struct hostapd_hw_modes *mode, int freq) if (chan->flag & HOSTAPD_CHAN_DISABLED) continue; - if (chan->freq == freq) + if (chan->freq == freq || chan->freq_khz == freq) return chan; } @@ -662,6 +699,26 @@ acs_find_mode(struct hostapd_iface *iface, int freq) } +static struct hostapd_channel_data * +acs_find_chan_khz(struct hostapd_iface *iface, int freq_khz) +{ + int i; + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + + for (i = 0; i < iface->num_hw_features; i++) { + mode = &iface->hw_features[i]; + if (!hostapd_hw_skip_mode(iface, mode)) { + chan = acs_find_chan_mode_khz(mode, freq_khz); + if (chan) + return chan; + } + } + + return NULL; +} + + static struct hostapd_channel_data * acs_find_chan(struct hostapd_iface *iface, int freq) { @@ -695,6 +752,16 @@ static int is_common_24ghz_chan(int chan) } +static int is_s1g_chan(struct hostapd_channel_data *chan) +{ + return !(chan->flag & HOSTAPD_CHAN_DISABLED) && + (chan->flag & (HOSTAPD_CHAN_WIDTH_1 | + HOSTAPD_CHAN_WIDTH_2 | + HOSTAPD_CHAN_WIDTH_4 | + HOSTAPD_CHAN_WIDTH_8 | + HOSTAPD_CHAN_WIDTH_16)); +} + #ifndef ACS_ADJ_WEIGHT #define ACS_ADJ_WEIGHT 0.85 #endif /* ACS_ADJ_WEIGHT */ @@ -798,7 +865,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface, * acs_update_puncturing_bitmap() should be changed to the index * of the primary channel */ - if (!chan_pri_allowed(chan)) + if (!chan_pri_allowed(chan) && !is_s1g_chan(chan)) continue; if ((chan->flag & HOSTAPD_CHAN_RADAR) && @@ -865,11 +932,19 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface, total_weight = 1; for (j = 1; j < n_chans; j++) { - adj_chan = acs_find_chan(iface, chan->freq + (j * 20)); + if (iface->conf->ieee80211ah) + adj_chan = acs_find_chan_khz(iface, + chan->freq_khz + (j * 500)); + else + adj_chan = acs_find_chan(iface, + chan->freq + (j * 20)); if (!adj_chan) break; - if (!chan_bw_allowed(adj_chan, bw, 1, 0)) { + /* We want to factor in interference for adj channels + * for S1G even if their bw doesn't match the specified + * BW */ + if (!chan_bw_allowed(adj_chan, bw, 1, 0) && !iface->conf->ieee80211ah) { wpa_printf(MSG_DEBUG, "ACS: PRI Channel %d: secondary channel %d BW %u is not supported", chan->chan, adj_chan->chan, bw); @@ -1016,6 +1091,12 @@ acs_find_ideal_chan(struct hostapd_iface *iface) u32 bw; struct hostapd_hw_modes *mode; + if (iface->conf->ieee80211ah) { + bw = op_class_to_bandwidth(iface->conf->op_class); + n_chans = bw; + goto bw_selected; + } + if (is_6ghz_op_class(iface->conf->op_class)) { bw = op_class_to_bandwidth(iface->conf->op_class); n_chans = bw / 20; @@ -1064,8 +1145,11 @@ bw_selected: } if (ideal_chan) { - wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg", - ideal_chan->chan, ideal_chan->freq, ideal_factor); + wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d %s) with total interference factor of %Lg", + ideal_chan->chan, + ideal_chan->freq_khz ? ideal_chan->freq_khz : ideal_chan->freq, + KHZ_PRINT_FREQ_UNITS(ideal_chan->freq_khz), + ideal_factor); #ifdef CONFIG_IEEE80211BE if (iface->conf->punct_acs_threshold) @@ -1188,6 +1272,12 @@ static void acs_study(struct hostapd_iface *iface) iface->conf->channel = ideal_chan->chan; iface->freq = ideal_chan->freq; + +#ifdef CONFIG_IEEE80211AH + iface->conf->s1g_oper_chwidth = acs_s1g_chan_get_bw(ideal_chan); + iface->freq_khz = ideal_chan->freq_khz; +#endif + #ifdef CONFIG_IEEE80211BE iface->conf->punct_bitmap = ideal_chan->punct_bitmap; #endif /* CONFIG_IEEE80211BE */ @@ -1284,7 +1374,10 @@ static int * acs_request_scan_add_freqs(struct hostapd_iface *iface, iface->conf->country[2] == 0x4f) continue; - *freq++ = chan->freq; + if (iface->conf->ieee80211ah) + *freq++ = chan->freq_khz; + else + *freq++ = chan->freq; } return freq; @@ -1300,6 +1393,9 @@ static int acs_request_scan(struct hostapd_iface *iface) os_memset(¶ms, 0, sizeof(params)); + if (iface->conf->ieee80211ah) + params.freq_in_khz = 1; + num_channels = 0; for (i = 0; i < iface->num_hw_features; i++) { mode = &iface->hw_features[i]; diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 0009a19e5..c77bf031f 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -837,6 +837,9 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface, if (!iface->current_mode) return 0; + if (is_s1g_freq(frequency_khz)) + return 1; + chan = hw_get_channel_freq(iface->current_mode->mode, frequency, frequency_khz, NULL, iface->hw_features, iface->num_hw_features); @@ -1386,6 +1389,10 @@ int hostapd_hw_skip_mode(struct hostapd_iface *iface, { int i; + /* If we are configured as an S1G AP, rely on the band to see if + * this specific hw mode is S1G */ + if (iface->conf->ieee80211ah) + return !(mode->band == NL80211_BAND_S1GHZ); if (iface->current_mode) return mode != iface->current_mode; if (mode->mode != HOSTAPD_MODE_IEEE80211B) diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index becc23a7d..6eb453c6d 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -3061,6 +3061,33 @@ int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed, int op_class_to_bandwidth(u8 op_class) { switch (op_class) { + /* Start: S1G op class - channels differ per regulatory domain + * Mappings retrieved from IEEE Std 802.11-2020: Table E-5 + * */ + case 66: + return 1; + case 67: + return 2; + case 68: + return 1; + case 69: + return 2; + case 70: + return 4; + case 71: + return 8; + case 72: + return 16; + case 73: + case 74: + return 1; + case 75: + return 2; + case 76: + return 4; + case 77: + return 1; + /* End: S1G op class */ case 81: case 82: return 20; diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 16dabbac6..7879c7976 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -22,6 +22,7 @@ #include "common/defs.h" #include "common/ieee802_11_defs.h" #include "common/wpa_common.h" +#include "nl80211_copy.h" #ifdef CONFIG_MACSEC #include "pae/ieee802_1x_kay.h" #endif /* CONFIG_MACSEC */ @@ -328,6 +329,12 @@ struct hostapd_hw_modes { * s1g_mcs - S1G (IEEE 802 11ah) supported MCS and NSS params */ u8 s1g_mcs[5]; + + /** + * Band - the nl80211 band - This is now relevant as + * HOSTAPD_MODE_IEEE80211A is used for multible bands. + */ + enum nl80211_band band; }; @@ -492,10 +499,13 @@ struct wpa_driver_scan_params { /** * freqs - Array of frequencies to scan or %NULL for all frequencies * - * The frequency is set in MHz. The array is zero-terminated. + * The frequency is set in MHz unless freq_khz flag is set. The array is + * zero-terminated. */ int *freqs; + unsigned int freq_in_khz:1; + /** * filter_ssids - Filter for reporting SSIDs * @@ -5750,7 +5760,8 @@ enum wpa_event_type { * struct freq_survey - Channel survey info * * @ifidx: Interface index in which this survey was observed - * @freq: Center of frequency of the surveyed channel + * @freq: Center of frequency (MHz) of the surveyed channel + * @freq_khz: Center of frequency (kHz) of the surveyed channel * @nf: Channel noise floor in dBm * @channel_time: Amount of time in ms the radio spent on the channel * @channel_time_busy: Amount of time in ms the radio detected some signal @@ -5764,6 +5775,7 @@ enum wpa_event_type { struct freq_survey { u32 ifidx; unsigned int freq; + unsigned int freq_khz; s8 nf; u64 channel_time; u64 channel_time_busy; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 5e7a30c98..4d9ac9cab 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -9996,6 +9996,9 @@ static void add_survey(struct nlattr **sinfo, u32 ifidx, survey->ifidx = ifidx; survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); + if (sinfo[NL80211_SURVEY_INFO_FREQUENCY_OFFSET]) + survey->freq_khz = MHZ_TO_KHZ(survey->freq) + + nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY_OFFSET]); survey->filled = 0; if (sinfo[NL80211_SURVEY_INFO_NOISE]) { @@ -10028,8 +10031,9 @@ static void add_survey(struct nlattr **sinfo, u32 ifidx, survey->filled |= SURVEY_HAS_CHAN_TIME_TX; } - wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d MHz noise=%d channel_time=%ld busy_time=%ld tx_time=%ld rx_time=%ld filled=%04x)", - survey->freq, + wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d %s noise=%d channel_time=%ld busy_time=%ld tx_time=%ld rx_time=%ld filled=%04x)", + survey->freq_khz ? survey->freq_khz : survey->freq, + KHZ_PRINT_FREQ_UNITS(survey->freq_khz), survey->nf, (unsigned long int) survey->channel_time, (unsigned long int) survey->channel_time_busy, diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 81eeae358..4df91373c 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -2091,6 +2091,8 @@ static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) mode->vht_mcs_set[4] = 0xff; mode->vht_mcs_set[5] = 0xff; + mode->band = nl_band->nla_type; + *(phy_info->num_modes) += 1; phy_info->last_mode = nl_band->nla_type; phy_info->last_chan_idx = 0; diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index 461d688a4..0369095df 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -229,15 +229,20 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd, params->extra_ies)) goto fail; } - if (params->freqs) { struct nlattr *freqs; - freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); + + if (params->freq_in_khz) + freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQ_KHZ); + else + freqs = nla_nest_start(msg, + NL80211_ATTR_SCAN_FREQUENCIES); if (freqs == NULL) goto fail; for (i = 0; params->freqs[i]; i++) { wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " - "MHz", params->freqs[i]); + "%s", params->freqs[i], + KHZ_PRINT_FREQ_UNITS(params->freq_in_khz)); if (nla_put_u32(msg, i + 1, params->freqs[i])) goto fail; } @@ -719,6 +724,7 @@ nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv, static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC }, [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_BSS_FREQUENCY_OFFSET] = {.type = NLA_U32 }, [NL80211_BSS_TSF] = { .type = NLA_U64 }, [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 }, [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 }, @@ -772,6 +778,8 @@ nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv, ETH_ALEN); if (bss[NL80211_BSS_FREQUENCY]) r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + if (bss[NL80211_BSS_FREQUENCY_OFFSET]) + r->freq_offset = nla_get_u32(bss[NL80211_BSS_FREQUENCY_OFFSET]); if (bss[NL80211_BSS_BEACON_INTERVAL]) r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]); if (bss[NL80211_BSS_CAPABILITY]) diff --git a/src/drivers/linux_wext.h b/src/drivers/linux_wext.h index e7c7001e1..1cb890974 100644 --- a/src/drivers/linux_wext.h +++ b/src/drivers/linux_wext.h @@ -26,6 +26,7 @@ typedef int32_t __s32; typedef uint16_t __u16; typedef int16_t __s16; typedef uint8_t __u8; +typedef int8_t __s8; #ifndef __user #define __user #endif /* __user */ diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index 6bb6afb10..8b4f0f12c 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -2998,6 +2998,8 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) goto failed; } + params->freq_in_khz = src->freq_in_khz; + if (src->filter_ssids) { params->filter_ssids = os_memdup(src->filter_ssids, sizeof(*params->filter_ssids) * -- 2.25.1 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap