[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(&params, 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