[PATCH] ACS: cover not-offloaded-ACS with hw_mode=any

[email protected]
Newsgroups gmane.linux.drivers.hostap
Message-ID <[email protected]>
From: Neo Jou <[email protected]>

This patch is to extend the existing hw_mode=any to cover
the not-offloaded-ACS case.

We DONOT duplicate for the mode HOSTAPD_MODE_IEEE80211ANY in
wpa_driver_nl80211_postprocess_modes(), so hostapd_select_hw_mode()
cannot find suitable hw_mode and iface->current_mode is NULL. Thus,
ACS code combine channels from multiple hw_mode lists, to determine
which channel needs to be surveyed. Thus, there are some patches in
src/ap/acs.c to handle the case that iface->current_mode is NULL,
and to combine channels.

Also in hostapd_is_usable_chans() called by hostapd_check_chans(),
it updates the hw_mode, based on the frequency channel ACS picked.

By this way, "hw_mode=any" can be used for not-offloaded-ACS case.

Signed-off-by: Neo Jou <[email protected]>
---
 src/ap/acs.c           | 327 +++++++++++++++++++++++++++++++++++++------------
 src/ap/drv_callbacks.c |  23 +++-
 src/ap/hostapd.h       |   1 +
 src/ap/hw_features.c   |  50 +++++++-
 4 files changed, 311 insertions(+), 90 deletions(-)

diff --git a/src/ap/acs.c b/src/ap/acs.c
index 232afa8..2ffa2ff 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -261,13 +261,15 @@ static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
 }
 
 
-void acs_cleanup(struct hostapd_iface *iface)
+static inline
+void acs_cleanup_by_mode(struct hostapd_iface *iface,
+			 struct hostapd_hw_modes *mode)
 {
 	int i;
 	struct hostapd_channel_data *chan;
 
-	for (i = 0; i < iface->current_mode->num_channels; i++) {
-		chan = &iface->current_mode->channels[i];
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
 
 		if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED)
 			acs_clean_chan_surveys(chan);
@@ -276,12 +278,31 @@ void acs_cleanup(struct hostapd_iface *iface)
 		chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
 		chan->min_nf = 0;
 	}
+}
+
+void acs_cleanup(struct hostapd_iface *iface)
+{
+	if (iface->current_mode == NULL) {
+		int i;
+		struct hostapd_hw_modes *mode;
+
+		for (i = 0; i < iface->num_hw_features; i++) {
+			mode = &iface->hw_features[i];
+
+			if (iface->hw_modes_has_11g &&
+			    (mode->mode == HOSTAPD_MODE_IEEE80211B))
+				continue;
+
+			acs_cleanup_by_mode(iface, mode);
+		}
+	} else {
+		acs_cleanup_by_mode(iface, iface->current_mode);
+	}
 
 	iface->chans_surveyed = 0;
 	iface->acs_num_completed_scans = 0;
 }
 
-
 static void acs_fail(struct hostapd_iface *iface)
 {
 	wpa_printf(MSG_ERROR, "ACS: Failed to start");
@@ -453,19 +474,45 @@ static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
 }
 
 
-static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
+static inline
+int acs_surveys_are_sufficient_by_mode(struct hostapd_iface *iface,
+				       struct hostapd_hw_modes *mode)
 {
 	int i;
 	struct hostapd_channel_data *chan;
 	int valid = 0;
 
-	for (i = 0; i < iface->current_mode->num_channels; i++) {
-		chan = &iface->current_mode->channels[i];
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
 		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
 		    acs_survey_list_is_sufficient(chan))
 			valid++;
 	}
 
+	return valid;
+}
+
+static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
+{
+	int valid = 0;
+
+	if (iface->current_mode == NULL) {
+		int i;
+		struct hostapd_hw_modes *mode;
+
+		for (i = 0; i < iface->num_hw_features; i++) {
+			mode = &iface->hw_features[i];
+
+			if (iface->hw_modes_has_11g &&
+			    (mode->mode == HOSTAPD_MODE_IEEE80211B))
+				continue;
+
+			valid = acs_surveys_are_sufficient_by_mode(iface, mode);
+		}
+	} else {
+		valid = acs_surveys_are_sufficient_by_mode(iface, iface->current_mode);
+	}
+
 	/* We need at least survey data for one channel */
 	return !!valid;
 }
@@ -489,14 +536,15 @@ static int is_in_chanlist(struct hostapd_iface *iface,
 }
 
 
-static void acs_survey_all_chans_intereference_factor(
-	struct hostapd_iface *iface)
+static void acs_survey_all_chans_intereference_factor_by_mode(
+	struct hostapd_iface *iface,
+	struct hostapd_hw_modes *mode)
 {
 	int i;
 	struct hostapd_channel_data *chan;
 
-	for (i = 0; i < iface->current_mode->num_channels; i++) {
-		chan = &iface->current_mode->channels[i];
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
 
 		if (!acs_usable_chan(chan))
 			continue;
@@ -514,15 +562,40 @@ static void acs_survey_all_chans_intereference_factor(
 	}
 }
 
+static void acs_survey_all_chans_intereference_factor(
+	struct hostapd_iface *iface)
+{
+	if (iface->current_mode == NULL) {
+		int i;
+		struct hostapd_hw_modes *mode;
 
-static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface,
-						  int freq)
+		for (i = 0; i < iface->num_hw_features; i++) {
+			mode = &iface->hw_features[i];
+
+			if (iface->hw_modes_has_11g &&
+			    (mode->mode == HOSTAPD_MODE_IEEE80211B))
+				continue;
+
+			acs_survey_all_chans_intereference_factor_by_mode(
+				iface, mode);
+
+		}
+	} else
+		acs_survey_all_chans_intereference_factor_by_mode(
+			iface, iface->current_mode);
+}
+
+
+static inline struct hostapd_channel_data *
+acs_find_chan_by_mode(struct hostapd_iface *iface,
+	int freq,
+	struct hostapd_hw_modes *mode)
 {
 	struct hostapd_channel_data *chan;
 	int i;
 
-	for (i = 0; i < iface->current_mode->num_channels; i++) {
-		chan = &iface->current_mode->channels[i];
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
 
 		if (chan->flag & HOSTAPD_CHAN_DISABLED)
 			continue;
@@ -534,6 +607,30 @@ static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface,
 	return NULL;
 }
 
+static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface,
+						  int freq)
+{
+	struct hostapd_channel_data *chan = NULL;
+
+	if (iface->current_mode == NULL) {
+		int i;
+		struct hostapd_hw_modes *mode;
+
+		for (i = 0; i < iface->num_hw_features; i++) {
+			mode = &iface->hw_features[i];
+
+			if (iface->hw_modes_has_11g &&
+			    (mode->mode == HOSTAPD_MODE_IEEE80211B))
+				continue;
+
+			chan = acs_find_chan_by_mode(iface, freq, mode);
+		}
+	} else {
+		chan = acs_find_chan_by_mode(iface, freq, iface->current_mode);
+	}
+
+	return chan;
+}
 
 static int is_24ghz_mode(enum hostapd_hw_mode mode)
 {
@@ -565,58 +662,25 @@ static int is_common_24ghz_chan(int chan)
 #define ACS_24GHZ_PREFER_1_6_11 0.8
 #endif /* ACS_24GHZ_PREFER_1_6_11 */
 
-/*
- * At this point it's assumed chan->interface_factor has been computed.
- * This function should be reusable regardless of interference computation
- * option (survey, BSS, spectral, ...). chan->interference factor must be
- * summable (i.e., must be always greater than zero).
- */
-static struct hostapd_channel_data *
-acs_find_ideal_chan(struct hostapd_iface *iface)
+static void
+acs_find_ideal_chan_by_mode(struct hostapd_iface *iface,
+			    struct hostapd_hw_modes *mode,
+			    int n_chans,
+			    u32 bw,
+			    struct hostapd_channel_data **rand_chan,
+			    struct hostapd_channel_data **ideal_chan,
+			    long double *ideal_factor)
 {
-	struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL,
-		*rand_chan = NULL;
-	long double factor, ideal_factor = 0;
+	struct hostapd_channel_data *chan, *adj_chan = NULL;
+	long double factor;
 	int i, j;
-	int n_chans = 1;
-	u32 bw;
 	unsigned int k;
 
-	/* TODO: HT40- support */
-
-	if (iface->conf->ieee80211n &&
-	    iface->conf->secondary_channel == -1) {
-		wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
-		return NULL;
-	}
-
-	if (iface->conf->ieee80211n &&
-	    iface->conf->secondary_channel)
-		n_chans = 2;
-
-	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
-		switch (hostapd_get_oper_chwidth(iface->conf)) {
-		case CHANWIDTH_80MHZ:
-			n_chans = 4;
-			break;
-		case CHANWIDTH_160MHZ:
-			n_chans = 8;
-			break;
-		}
-	}
-
-	bw = num_chan_to_bw(n_chans);
-
-	/* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */
-
-	wpa_printf(MSG_DEBUG,
-		   "ACS: Survey analysis for selected bandwidth %d MHz", bw);
-
-	for (i = 0; i < iface->current_mode->num_channels; i++) {
+	for (i = 0; i < mode->num_channels; i++) {
 		double total_weight;
 		struct acs_bias *bias, tmp_bias;
 
-		chan = &iface->current_mode->channels[i];
+		chan = &mode->channels[i];
 
 		/* Since in the current ACS implementation the first channel is
 		 * always a primary channel, skip channels not available as
@@ -637,7 +701,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
 
 		/* HT40 on 5 GHz has a limited set of primary channels as per
 		 * 11n Annex J */
-		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
 		    iface->conf->ieee80211n &&
 		    iface->conf->secondary_channel &&
 		    !acs_usable_ht40_chan(chan)) {
@@ -646,7 +710,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
 			continue;
 		}
 
-		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+		if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
 		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
 			if (hostapd_get_oper_chwidth(iface->conf) ==
 			    CHANWIDTH_80MHZ &&
@@ -698,7 +762,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
 
 		/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
 		 * channel interference factor. */
-		if (is_24ghz_mode(iface->current_mode->mode)) {
+		if (is_24ghz_mode(mode->mode)) {
 			for (j = 0; j < n_chans; j++) {
 				adj_chan = acs_find_chan(iface, chan->freq +
 							 (j * 20) - 5);
@@ -744,7 +808,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
 					break;
 				bias = NULL;
 			}
-		} else if (is_24ghz_mode(iface->current_mode->mode) &&
+		} else if (is_24ghz_mode(mode->mode) &&
 			   is_common_24ghz_chan(chan->chan)) {
 			tmp_bias.channel = chan->chan;
 			tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11;
@@ -763,14 +827,78 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
 		}
 
 		if (acs_usable_chan(chan) &&
-		    (!ideal_chan || factor < ideal_factor)) {
-			ideal_factor = factor;
-			ideal_chan = chan;
+		    (!*ideal_chan || factor < *ideal_factor)) {
+			*ideal_factor = factor;
+			*ideal_chan = chan;
 		}
 
 		/* This channel would at least be usable */
-		if (!rand_chan)
-			rand_chan = chan;
+		if (!(*rand_chan))
+			*rand_chan = chan;
+	}
+}
+
+
+
+/*
+ * At this point it's assumed chan->interface_factor has been computed.
+ * This function should be reusable regardless of interference computation
+ * option (survey, BSS, spectral, ...). chan->interference factor must be
+ * summable (i.e., must be always greater than zero).
+ */
+static struct hostapd_channel_data *
+acs_find_ideal_chan(struct hostapd_iface *iface)
+{
+	struct hostapd_channel_data *ideal_chan = NULL,
+		*rand_chan = NULL;
+	long double ideal_factor = 0;
+	int i;
+	int n_chans = 1;
+	u32 bw;
+	struct hostapd_hw_modes *mode;
+
+	/* TODO: HT40- support */
+
+	if (iface->conf->ieee80211n &&
+	    iface->conf->secondary_channel == -1) {
+		wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
+		return NULL;
+	}
+
+	if (iface->conf->ieee80211n &&
+	    iface->conf->secondary_channel)
+		n_chans = 2;
+
+	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+		switch (hostapd_get_oper_chwidth(iface->conf)) {
+		case CHANWIDTH_80MHZ:
+			n_chans = 4;
+			break;
+		case CHANWIDTH_160MHZ:
+			n_chans = 8;
+			break;
+		}
+	}
+
+	bw = num_chan_to_bw(n_chans);
+
+	/* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */
+
+	wpa_printf(MSG_DEBUG,
+		   "ACS: Survey analysis for selected bandwidth %d MHz", bw);
+
+	if (iface->current_mode == NULL) {
+		for (i = 0; i < iface->num_hw_features; i++) {
+			mode = &iface->hw_features[i];
+
+			if (iface->hw_modes_has_11g &&
+			    (mode->mode == HOSTAPD_MODE_IEEE80211B))
+				continue;
+
+			acs_find_ideal_chan_by_mode(iface, mode, n_chans, bw, &rand_chan, &ideal_chan, &ideal_factor);
+		}
+	} else {
+		acs_find_ideal_chan_by_mode(iface, iface->current_mode, n_chans, bw, &rand_chan, &ideal_chan, &ideal_factor);
 	}
 
 	if (ideal_chan) {
@@ -917,30 +1045,68 @@ fail:
 	acs_fail(iface);
 }
 
+static int *acs_request_scan_add_freq_by_mode(struct hostapd_iface *iface,
+					struct hostapd_hw_modes *mode,
+					int *freq)
+{
+	int i;
+	struct hostapd_channel_data *chan;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+
+		if (!is_in_chanlist(iface, chan))
+			continue;
+
+		*freq++ = chan->freq;
+	}
+
+	return freq;
+}
+
 
 static int acs_request_scan(struct hostapd_iface *iface)
 {
 	struct wpa_driver_scan_params params;
-	struct hostapd_channel_data *chan;
 	int i, *freq;
+	int num_channels;
+	struct hostapd_hw_modes *mode;
 
 	os_memset(&params, 0, sizeof(params));
-	params.freqs = os_calloc(iface->current_mode->num_channels + 1,
-				 sizeof(params.freqs[0]));
+	if (iface->current_mode == NULL) {
+		num_channels = 0;
+		for (i = 0; i < iface->num_hw_features; i++) {
+			mode = &iface->hw_features[i];
+			num_channels += mode->num_channels;
+		}
+	} else {
+		num_channels = iface->current_mode->num_channels;
+	}
+
+	params.freqs = os_calloc(num_channels + 1,
+			 	sizeof(params.freqs[0]));
 	if (params.freqs == NULL)
 		return -1;
 
 	freq = params.freqs;
-	for (i = 0; i < iface->current_mode->num_channels; i++) {
-		chan = &iface->current_mode->channels[i];
-		if (chan->flag & HOSTAPD_CHAN_DISABLED)
-			continue;
 
-		if (!is_in_chanlist(iface, chan))
-			continue;
+	if (iface->current_mode == NULL) {
+		for (i = 0; i < iface->num_hw_features; i++) {
+			mode = &iface->hw_features[i];
 
-		*freq++ = chan->freq;
+			if (iface->hw_modes_has_11g &&
+			    (mode->mode == HOSTAPD_MODE_IEEE80211B))
+				continue;
+
+			freq = acs_request_scan_add_freq_by_mode(iface, mode, freq);
+		}
+	} else {
+		freq = acs_request_scan_add_freq_by_mode(iface, iface->current_mode, freq);
 	}
+
 	*freq = 0;
 
 	if (params.freqs == freq) {
@@ -978,7 +1144,8 @@ enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
 		return HOSTAPD_CHAN_ACS;
 	}
 
-	if (!iface->current_mode)
+	if ((!iface->current_mode) &&
+	    (iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY))
 		return HOSTAPD_CHAN_INVALID;
 
 	acs_cleanup(iface);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 8a4b0ef..4b3e4ea 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -1429,13 +1429,26 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
 static struct hostapd_channel_data * hostapd_get_mode_channel(
 	struct hostapd_iface *iface, unsigned int freq)
 {
-	int i;
+	int i, j;
 	struct hostapd_channel_data *chan;
+	struct hostapd_hw_modes *mode;
 
-	for (i = 0; i < iface->current_mode->num_channels; i++) {
-		chan = &iface->current_mode->channels[i];
-		if ((unsigned int) chan->freq == freq)
-			return chan;
+	if (iface->current_mode == NULL) {
+		for (i = 0; i < iface->num_hw_features; i++) {
+			mode = &iface->hw_features[i];
+
+			for (j = 0; j < mode->num_channels; j++) {
+				chan = &mode->channels[j];
+				if ((unsigned int) chan->freq == freq)
+					return chan;
+			}
+		}
+	} else {
+		for (i = 0; i < iface->current_mode->num_channels; i++) {
+			chan = &iface->current_mode->channels[i];
+			if ((unsigned int) chan->freq == freq)
+				return chan;
+		}
 	}
 
 	return NULL;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 2358d16..2b4d656 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -480,6 +480,7 @@ struct hostapd_iface {
 
 	struct hostapd_hw_modes *hw_features;
 	int num_hw_features;
+	Boolean hw_modes_has_11g;
 	struct hostapd_hw_modes *current_mode;
 	/* Rates that are currently used (i.e., filtered copy of
 	 * current_mode->channels */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index ba10752..7ce1f74 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -859,8 +859,37 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface)
 	int secondary_freq;
 	struct hostapd_channel_data *pri_chan;
 
-	if (!iface->current_mode)
-		return 0;
+	if (!iface->current_mode) {
+		if (iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211ANY) {
+			int i;
+			enum hostapd_hw_mode target_mode;
+
+			if (iface->freq < 4000)
+				if (iface->hw_modes_has_11g)
+					target_mode = HOSTAPD_MODE_IEEE80211G;
+				else
+					target_mode = HOSTAPD_MODE_IEEE80211B;
+			else if (iface->freq > 50000)
+				target_mode = HOSTAPD_MODE_IEEE80211AD;
+			else
+				target_mode = HOSTAPD_MODE_IEEE80211A;
+
+			for (i = 0; i < iface->num_hw_features; i++) {
+				struct hostapd_hw_modes *mode = &iface->hw_features[i];
+				if (mode->mode == target_mode) {
+					iface->current_mode = mode;
+					break;
+				}
+			}
+
+			if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211ANY) {
+				wpa_printf(MSG_ERROR, "ACS: cannot decide mode");
+				return 0;
+			}
+		} else 
+			return 0;
+	}
+	
 	pri_chan = hw_get_channel_freq(iface->current_mode->mode,
 				       iface->freq, NULL,
 				       iface->hw_features,
@@ -1031,10 +1060,21 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
 		}
 	}
 
+	/* set if there is 11g already */
+	iface->hw_modes_has_11g = FALSE;
+	for (i = 0; i < iface->num_hw_features; i++) {
+		struct hostapd_hw_modes *mode = &iface->hw_features[i];
+
+		if (mode->mode == HOSTAPD_MODE_IEEE80211G) {
+			iface->hw_modes_has_11g = TRUE;
+			break;
+		}
+	}
+
 	if (iface->current_mode == NULL) {
-		if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) ||
-		    !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY))
-		{
+		if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) ||
+		     !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) &&
+		    (iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY)) {
 			wpa_printf(MSG_ERROR,
 				   "Hardware does not support configured mode");
 			hostapd_logger(iface->bss[0], NULL,
-- 
2.7.4
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.