[PATCH 6/6] S1G: Add S1G support to ACS
Bassem Dawood
bassem at morsemicro.com
Tue Oct 31 22:34:31 PDT 2023
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
More information about the Hostap
mailing list