Cumulative patch from commit ea18024d78bde140fb8f74d216d757816bfd6e9b

ea18024 dbus: Add PropertiesChanged signal to Peer object
36716ee P2P: Add a utility function to run a method on every known peer
bf03566 dbus: Remove GroupMember object type and use Peer instead
17a37d7 dbus: Add a Groups property to a Peer object on which it belongs
6f04642 P2P: Add utility functions to get GO/client interface
c6386e5 P2P Add a utility to run a callback on all available groups
8e76f48 P2P: Add a utility function to get the group configuration
37d8a27 TDLS: Clean up add/set peer operations
bcd2baa TDLS: Tear down connection on malformed Setup Confirm
8190540 TDLS: Abort local setup when failing to add STA
1dce7a2 TDLS: Update peer STA as soon as full peer info is available
819c943 TDLS: Remove peer from global peer-list on free
5841958 hostapd: Use channel switch fallback on error
8974620 hostapd: Perform multi-BSS CSA for DFS properly
ccac7c6 hostapd: Make chan_switch command per-interface not per-BSS
6782b68 hostapd: Move CSA parameters to hostapd_data
1de809e eapol_test: Fix -R option to not replace -s option value
3c5d34e Change channel before IBSS associations
ebffdbc nl80211: Refactor mode switch logic

Change-Id: I1cbdc4dce586ec69f693b3b04eb340a5332f6b40
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 73dffe2..b3b6149 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -265,18 +265,18 @@
 {
 	u8 chan;
 
-	if (!hapd->iface->cs_freq_params.freq)
+	if (!hapd->cs_freq_params.freq)
 		return eid;
 
-	if (ieee80211_freq_to_chan(hapd->iface->cs_freq_params.freq, &chan) ==
+	if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) ==
 	    NUM_HOSTAPD_MODES)
 		return eid;
 
 	*eid++ = WLAN_EID_CHANNEL_SWITCH;
 	*eid++ = 3;
-	*eid++ = hapd->iface->cs_block_tx;
+	*eid++ = hapd->cs_block_tx;
 	*eid++ = chan;
-	*eid++ = hapd->iface->cs_count;
+	*eid++ = hapd->cs_count;
 
 	return eid;
 }
@@ -286,12 +286,12 @@
 {
 	u8 sec_ch;
 
-	if (!hapd->iface->cs_freq_params.sec_channel_offset)
+	if (!hapd->cs_freq_params.sec_channel_offset)
 		return eid;
 
-	if (hapd->iface->cs_freq_params.sec_channel_offset == -1)
+	if (hapd->cs_freq_params.sec_channel_offset == -1)
 		sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW;
-	else if (hapd->iface->cs_freq_params.sec_channel_offset == 1)
+	else if (hapd->cs_freq_params.sec_channel_offset == 1)
 		sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE;
 	else
 		return eid;
@@ -409,7 +409,7 @@
 	pos = hostapd_eid_roaming_consortium(hapd, pos);
 
 	pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp,
-				    &hapd->iface->cs_c_off_proberesp);
+				    &hapd->cs_c_off_proberesp);
 #ifdef CONFIG_IEEE80211AC
 	pos = hostapd_eid_vht_capabilities(hapd, pos);
 	pos = hostapd_eid_vht_operation(hapd, pos);
@@ -824,7 +824,7 @@
 	tailpos = hostapd_eid_adv_proto(hapd, tailpos);
 	tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
 	tailpos = hostapd_add_csa_elems(hapd, tailpos, tail,
-					&hapd->iface->cs_c_off_beacon);
+					&hapd->cs_c_off_beacon);
 #ifdef CONFIG_IEEE80211AC
 	tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
 	tailpos = hostapd_eid_vht_operation(hapd, tailpos);
@@ -957,7 +957,7 @@
 	struct wpabuf *beacon, *proberesp, *assocresp;
 	int res, ret = -1;
 
-	if (hapd->iface->csa_in_progress) {
+	if (hapd->csa_in_progress) {
 		wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period");
 		return -1;
 	}
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index c30f6d6..a11b2cf 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -756,6 +756,16 @@
 }
 
 
+static int hostapd_csa_in_progress(struct hostapd_iface *iface)
+{
+	unsigned int i;
+	for (i = 0; i < iface->num_bss; i++)
+		if (iface->bss[i]->csa_in_progress)
+			return 1;
+	return 0;
+}
+
+
 static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
 {
 	struct hostapd_channel_data *channel;
@@ -764,15 +774,15 @@
 	u8 vht_oper_centr_freq_seg1_idx;
 	int skip_radar = 1;
 	struct csa_settings csa_settings;
-	struct hostapd_data *hapd = iface->bss[0];
+	unsigned int i;
 	int err = 1;
 
 	wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
 		   __func__, iface->cac_started ? "yes" : "no",
-		   iface->csa_in_progress ? "yes" : "no");
+		   hostapd_csa_in_progress(iface) ? "yes" : "no");
 
 	/* Check if CSA in progress */
-	if (iface->csa_in_progress)
+	if (hostapd_csa_in_progress(iface))
 		return 0;
 
 	/* Check if active CAC */
@@ -843,7 +853,12 @@
 		return err;
 	}
 
-	err = hostapd_switch_channel(hapd, &csa_settings);
+	for (i = 0; i < iface->num_bss; i++) {
+		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+		if (err)
+			break;
+	}
+
 	if (err) {
 		wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
 			   err);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index fb095ef..93804de 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -489,9 +489,10 @@
 	hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx;
 	hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx;
 
-	if (hapd->iface->csa_in_progress &&
-	    freq == hapd->iface->cs_freq_params.freq) {
+	if (hapd->csa_in_progress &&
+	    freq == hapd->cs_freq_params.freq) {
 		hostapd_cleanup_cs_params(hapd);
+		ieee802_11_set_beacon(hapd);
 
 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED "freq=%d",
 			freq);
@@ -884,6 +885,20 @@
 
 #ifdef NEED_AP_MLME
 
+static void hostapd_event_iface_unavailable(struct hostapd_data *hapd)
+{
+	wpa_printf(MSG_DEBUG, "Interface %s is unavailable -- stopped",
+		   hapd->conf->iface);
+
+	if (hapd->csa_in_progress) {
+		wpa_printf(MSG_INFO, "CSA failed (%s was stopped)",
+			   hapd->conf->iface);
+		hostapd_switch_channel_fallback(hapd->iface,
+						&hapd->cs_freq_params);
+	}
+}
+
+
 static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd,
 					     struct dfs_event *radar)
 {
@@ -1071,6 +1086,9 @@
 		hostapd_event_get_survey(hapd, &data->survey_results);
 		break;
 #ifdef NEED_AP_MLME
+	case EVENT_INTERFACE_UNAVAILABLE:
+		hostapd_event_iface_unavailable(hapd);
+		break;
 	case EVENT_DFS_RADAR_DETECTED:
 		if (!data)
 			break;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 4e09fa3..55b7ced 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -2175,13 +2175,12 @@
 }
 
 
-static int hostapd_build_beacon_data(struct hostapd_iface *iface,
+static int hostapd_build_beacon_data(struct hostapd_data *hapd,
 				     struct beacon_data *beacon)
 {
 	struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra;
 	struct wpa_driver_ap_params params;
 	int ret;
-	struct hostapd_data *hapd = iface->bss[0];
 
 	os_memset(beacon, 0, sizeof(*beacon));
 	ret = ieee802_11_build_ap_params(hapd, &params);
@@ -2281,13 +2280,13 @@
 
 	if (!params->channel) {
 		/* check if the new channel is supported by hw */
-		channel = hostapd_hw_get_channel(hapd, params->freq);
-		if (!channel)
-			return -1;
-	} else {
-		channel = params->channel;
+		params->channel = hostapd_hw_get_channel(hapd, params->freq);
 	}
 
+	channel = params->channel;
+	if (!channel)
+		return -1;
+
 	/* if a pointer to old_params is provided we save previous state */
 	if (old_params) {
 		old_params->channel = conf->channel;
@@ -2305,14 +2304,15 @@
 }
 
 
-static int hostapd_fill_csa_settings(struct hostapd_iface *iface,
+static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
 				     struct csa_settings *settings)
 {
+	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_freq_params old_freq;
 	int ret;
 
 	os_memset(&old_freq, 0, sizeof(old_freq));
-	if (!iface || !iface->freq || iface->csa_in_progress)
+	if (!iface || !iface->freq || hapd->csa_in_progress)
 		return -1;
 
 	ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
@@ -2321,7 +2321,7 @@
 	if (ret)
 		return ret;
 
-	ret = hostapd_build_beacon_data(iface, &settings->beacon_after);
+	ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
 
 	/* change back the configuration */
 	hostapd_change_config_freq(iface->bss[0], iface->conf,
@@ -2331,18 +2331,18 @@
 		return ret;
 
 	/* set channel switch parameters for csa ie */
-	iface->cs_freq_params = settings->freq_params;
-	iface->cs_count = settings->cs_count;
-	iface->cs_block_tx = settings->block_tx;
+	hapd->cs_freq_params = settings->freq_params;
+	hapd->cs_count = settings->cs_count;
+	hapd->cs_block_tx = settings->block_tx;
 
-	ret = hostapd_build_beacon_data(iface, &settings->beacon_csa);
+	ret = hostapd_build_beacon_data(hapd, &settings->beacon_csa);
 	if (ret) {
 		free_beacon_data(&settings->beacon_after);
 		return ret;
 	}
 
-	settings->counter_offset_beacon = iface->cs_c_off_beacon;
-	settings->counter_offset_presp = iface->cs_c_off_proberesp;
+	settings->counter_offset_beacon = hapd->cs_c_off_beacon;
+	settings->counter_offset_presp = hapd->cs_c_off_proberesp;
 
 	return 0;
 }
@@ -2350,13 +2350,12 @@
 
 void hostapd_cleanup_cs_params(struct hostapd_data *hapd)
 {
-	os_memset(&hapd->iface->cs_freq_params, 0,
-		  sizeof(hapd->iface->cs_freq_params));
-	hapd->iface->cs_count = 0;
-	hapd->iface->cs_block_tx = 0;
-	hapd->iface->cs_c_off_beacon = 0;
-	hapd->iface->cs_c_off_proberesp = 0;
-	hapd->iface->csa_in_progress = 0;
+	os_memset(&hapd->cs_freq_params, 0, sizeof(hapd->cs_freq_params));
+	hapd->cs_count = 0;
+	hapd->cs_block_tx = 0;
+	hapd->cs_c_off_beacon = 0;
+	hapd->cs_c_off_proberesp = 0;
+	hapd->csa_in_progress = 0;
 }
 
 
@@ -2364,7 +2363,7 @@
 			   struct csa_settings *settings)
 {
 	int ret;
-	ret = hostapd_fill_csa_settings(hapd->iface, settings);
+	ret = hostapd_fill_csa_settings(hapd, settings);
 	if (ret)
 		return ret;
 
@@ -2378,8 +2377,64 @@
 		return ret;
 	}
 
-	hapd->iface->csa_in_progress = 1;
+	hapd->csa_in_progress = 1;
 	return 0;
 }
 
+
+void
+hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+				const struct hostapd_freq_params *freq_params)
+{
+	int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT;
+	unsigned int i;
+
+	wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
+
+	if (freq_params->center_freq1)
+		vht_seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
+	if (freq_params->center_freq2)
+		vht_seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5;
+
+	switch (freq_params->bandwidth) {
+	case 0:
+	case 20:
+	case 40:
+		vht_bw = VHT_CHANWIDTH_USE_HT;
+		break;
+	case 80:
+		if (freq_params->center_freq2)
+			vht_bw = VHT_CHANWIDTH_80P80MHZ;
+		else
+			vht_bw = VHT_CHANWIDTH_80MHZ;
+		break;
+	case 160:
+		vht_bw = VHT_CHANWIDTH_160MHZ;
+		break;
+	default:
+		wpa_printf(MSG_WARNING, "Unknown CSA bandwidth: %d",
+			   freq_params->bandwidth);
+		break;
+	}
+
+	iface->freq = freq_params->freq;
+	iface->conf->channel = freq_params->channel;
+	iface->conf->secondary_channel = freq_params->sec_channel_offset;
+	iface->conf->vht_oper_centr_freq_seg0_idx = vht_seg0_idx;
+	iface->conf->vht_oper_centr_freq_seg1_idx = vht_seg1_idx;
+	iface->conf->vht_oper_chwidth = vht_bw;
+	iface->conf->ieee80211n = freq_params->ht_enabled;
+	iface->conf->ieee80211ac = freq_params->vht_enabled;
+
+	/*
+	 * cs_params must not be cleared earlier because the freq_params
+	 * argument may actually point to one of these.
+	 */
+	for (i = 0; i < iface->num_bss; i++)
+		hostapd_cleanup_cs_params(iface->bss[i]);
+
+	hostapd_disable_iface(iface);
+	hostapd_enable_iface(iface);
+}
+
 #endif /* NEED_AP_MLME */
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index bd85c54..3c8727b 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -210,6 +210,14 @@
 			   size_t psk_len);
 	void *new_psk_cb_ctx;
 
+	/* channel switch parameters */
+	struct hostapd_freq_params cs_freq_params;
+	u8 cs_count;
+	int cs_block_tx;
+	unsigned int cs_c_off_beacon;
+	unsigned int cs_c_off_proberesp;
+	int csa_in_progress;
+
 #ifdef CONFIG_P2P
 	struct p2p_data *p2p;
 	struct p2p_group *p2p_group;
@@ -343,14 +351,6 @@
 	/* lowest observed noise floor in dBm */
 	s8 lowest_nf;
 
-	/* channel switch parameters */
-	struct hostapd_freq_params cs_freq_params;
-	u8 cs_count;
-	int cs_block_tx;
-	unsigned int cs_c_off_beacon;
-	unsigned int cs_c_off_proberesp;
-	int csa_in_progress;
-
 	unsigned int dfs_cac_ms;
 	struct os_reltime dfs_cac_start;
 
@@ -397,6 +397,9 @@
 const char * hostapd_state_text(enum hostapd_iface_state s);
 int hostapd_switch_channel(struct hostapd_data *hapd,
 			   struct csa_settings *settings);
+void
+hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+				const struct hostapd_freq_params *freq_params);
 void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
 
 /* utils.c */