Cumulative patch from commit fa56315cfc9ebaff2b210ed99d43dda9b16bdf56

fa56315 eap_proxy: Add context data pointer to the get_imsi call
07041c6 eap_proxy: Confirm eap_proxy initialization before reading SIM info
47d986e P2P: Check Action frame payload match before accepted TX status
d259249 Fix ENABLE_NETWORK not to reconnect in disconnected state
677cf19 hostapd: Select any supported channel if ACS fails
20f9cb1 hostapd: Allow ACS to deal with partial survey data
3645fd5 hostapd: Propagate ACS errors to iface setup
0e1d0b3 hostapd: Don't get stuck after failed ACS
af8a827 Make frequency range list routines more general
941dae0 P2P: Add more user friendly debug print of channel lists

Change-Id: I942dbbc0cb92f4e09626ec6ea17f6ea583c17f1a
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/acs.c b/src/ap/acs.c
index d5e3f59..8536e48 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -352,16 +352,6 @@
 }
 
 
-static int acs_usable_chan(struct hostapd_channel_data *chan)
-{
-	if (dl_list_empty(&chan->survey_list))
-		return 0;
-	if (chan->flag & HOSTAPD_CHAN_DISABLED)
-		return 0;
-	return 1;
-}
-
-
 static int acs_usable_ht40_chan(struct hostapd_channel_data *chan)
 {
 	const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149,
@@ -398,28 +388,54 @@
 }
 
 
+static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
+{
+	struct freq_survey *survey;
+
+	dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+	{
+		if (!acs_survey_is_sufficient(survey)) {
+			wpa_printf(MSG_ERROR, "ACS: Channel %d has insufficient survey data",
+				   chan->chan);
+			return 0;
+		}
+	}
+
+	return 1;
+
+}
+
+
 static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
 {
 	int i;
 	struct hostapd_channel_data *chan;
-	struct freq_survey *survey;
+	int valid = 0;
 
 	for (i = 0; i < iface->current_mode->num_channels; i++) {
 		chan = &iface->current_mode->channels[i];
 		if (chan->flag & HOSTAPD_CHAN_DISABLED)
 			continue;
 
-		dl_list_for_each(survey, &chan->survey_list,
-				 struct freq_survey, list)
-		{
-			if (!acs_survey_is_sufficient(survey)) {
-				wpa_printf(MSG_ERROR, "ACS: Channel %d has insufficient survey data",
-					   chan->chan);
-				return 0;
-			}
-		}
+		if (!acs_survey_list_is_sufficient(chan))
+			continue;
+
+		valid++;
 	}
 
+	/* We need at least survey data for one channel */
+	return !!valid;
+}
+
+
+static int acs_usable_chan(struct hostapd_channel_data *chan)
+{
+	if (dl_list_empty(&chan->survey_list))
+		return 0;
+	if (chan->flag & HOSTAPD_CHAN_DISABLED)
+		return 0;
+	if (!acs_survey_list_is_sufficient(chan))
+		return 0;
 	return 1;
 }
 
@@ -456,7 +472,7 @@
 	for (i = 0; i < iface->current_mode->num_channels; i++) {
 		chan = &iface->current_mode->channels[i];
 
-		if (!acs_usable_chan(chan))
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
 			continue;
 
 		if (chan->freq == freq)
@@ -476,7 +492,8 @@
 static struct hostapd_channel_data *
 acs_find_ideal_chan(struct hostapd_iface *iface)
 {
-	struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL;
+	struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL,
+		*rand_chan = NULL;
 	long double factor, ideal_factor = 0;
 	int i, j;
 	int n_chans = 1;
@@ -508,9 +525,10 @@
 	for (i = 0; i < iface->current_mode->num_channels; i++) {
 		chan = &iface->current_mode->channels[i];
 
-		if (!acs_usable_chan(chan))
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
 			continue;
 
+
 		/* HT40 on 5 GHz has a limited set of primary channels as per
 		 * 11n Annex J */
 		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
@@ -522,14 +540,17 @@
 			continue;
 		}
 
-		factor = chan->interference_factor;
+		factor = 0;
+		if (acs_usable_chan(chan))
+			factor = chan->interference_factor;
 
 		for (j = 1; j < n_chans; j++) {
 			adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
 			if (!adj_chan)
 				break;
 
-			factor += adj_chan->interference_factor;
+			if (acs_usable_chan(adj_chan))
+				factor += adj_chan->interference_factor;
 		}
 
 		if (j != n_chans) {
@@ -548,22 +569,22 @@
 
 				adj_chan = acs_find_chan(iface, chan->freq +
 							 (j * 20) - 5);
-				if (adj_chan)
+				if (adj_chan && acs_usable_chan(adj_chan))
 					factor += adj_chan->interference_factor;
 
 				adj_chan = acs_find_chan(iface, chan->freq +
 							 (j * 20) - 10);
-				if (adj_chan)
+				if (adj_chan && acs_usable_chan(adj_chan))
 					factor += adj_chan->interference_factor;
 
 				adj_chan = acs_find_chan(iface, chan->freq +
 							 (j * 20) + 5);
-				if (adj_chan)
+				if (adj_chan && acs_usable_chan(adj_chan))
 					factor += adj_chan->interference_factor;
 
 				adj_chan = acs_find_chan(iface, chan->freq +
 							 (j * 20) + 10);
-				if (adj_chan)
+				if (adj_chan && acs_usable_chan(adj_chan))
 					factor += adj_chan->interference_factor;
 			}
 		}
@@ -571,17 +592,24 @@
 		wpa_printf(MSG_DEBUG, "ACS:  * channel %d: total interference = %Lg",
 			   chan->chan, factor);
 
-		if (!ideal_chan || factor < ideal_factor) {
+		if (acs_usable_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 (ideal_chan)
+	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);
+		return ideal_chan;
+	}
 
-	return ideal_chan;
+	return rand_chan;
 }
 
 
@@ -655,6 +683,7 @@
 	ideal_chan = acs_find_ideal_chan(iface);
 	if (!ideal_chan) {
 		wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
+		err = -1;
 		goto fail;
 	}
 
@@ -663,24 +692,20 @@
 	if (iface->conf->ieee80211ac)
 		acs_adjust_vht_center_freq(iface);
 
+	err = 0;
+fail:
 	/*
 	 * hostapd_setup_interface_complete() will return -1 on failure,
 	 * 0 on success and 0 is HOSTAPD_CHAN_VALID :)
 	 */
-	switch (hostapd_acs_completed(iface)) {
-	case HOSTAPD_CHAN_VALID:
+	if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) {
 		acs_cleanup(iface);
 		return;
-	case HOSTAPD_CHAN_INVALID:
-	case HOSTAPD_CHAN_ACS:
-	default:
-		/* This can possibly happen if channel parameters (secondary
-		 * channel, center frequencies) are misconfigured */
-		wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
-		goto fail;
 	}
 
-fail:
+	/* This can possibly happen if channel parameters (secondary
+	 * channel, center frequencies) are misconfigured */
+	wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
 	acs_fail(iface);
 }
 
diff --git a/src/ap/acs.h b/src/ap/acs.h
index 0d1d0f1..a41f17f 100644
--- a/src/ap/acs.h
+++ b/src/ap/acs.h
@@ -13,7 +13,7 @@
 #ifdef CONFIG_ACS
 
 enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
-int hostapd_acs_completed(struct hostapd_iface *iface);
+int hostapd_acs_completed(struct hostapd_iface *iface, int err);
 
 #else /* CONFIG_ACS */
 
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 9e5becc..609ed53 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -758,9 +758,12 @@
 }
 
 
-int hostapd_acs_completed(struct hostapd_iface *iface)
+int hostapd_acs_completed(struct hostapd_iface *iface, int err)
 {
-	int ret;
+	int ret = -1;
+
+	if (err)
+		goto out;
 
 	switch (hostapd_check_chans(iface)) {
 	case HOSTAPD_CHAN_VALID:
@@ -768,23 +771,25 @@
 	case HOSTAPD_CHAN_ACS:
 		wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
 		hostapd_notify_bad_chans(iface);
-		return -1;
+		goto out;
 	case HOSTAPD_CHAN_INVALID:
 	default:
 		wpa_printf(MSG_ERROR, "ACS picked unusable channels");
 		hostapd_notify_bad_chans(iface);
-		return -1;
+		goto out;
 	}
 
 	ret = hostapd_check_ht_capab(iface);
 	if (ret < 0)
-		return -1;
+		goto out;
 	if (ret == 1) {
 		wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
 		return 0;
 	}
 
-	return hostapd_setup_interface_complete(iface, 0);
+	ret = 0;
+out:
+	return hostapd_setup_interface_complete(iface, ret);
 }