[PATCH 10/24] P2P: Move a GO from its operating frequency

Ilan Peer ilan.peer
Mon May 19 00:06:07 PDT 2014


Upon any change in the currently used channels evaluate if a GO should
move to a different operating frequency, where the possible scenarios:

1. The frequency that the GO is currently using is no longer valid,
   due to regulatory reasons, and thus the GO must be moved to some
   other frequency.
2. Due to Multi Concurrent Channel (MCC) policy considerations, it would
   be preferable, based on configuration settings, to prefer Same Channel
   Mode (SCM) over concurrent operation in multiple channels. The supported
   policies:

   - prefer SCM: prefer moving the GO to a frequency used by some other
     interface.
   - prefer SCM if Peer supports: prefer moving the GO to a frequency used
     by some other station interface iff the other station interface is using
     a frequency that is common between the local and the peer device
     (based on the GoN/Invitation signaling).
   - Stay on the current frequency.

Currently, the GO transition to another frequency is handled by a complete
tear down and re-setup of the GO. Still need to add CSA flow to the
considerations.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 wpa_supplicant/config.c         |    2 +
 wpa_supplicant/config.h         |   29 ++++++
 wpa_supplicant/config_file.c    |    3 +
 wpa_supplicant/p2p_supplicant.c |  209 ++++++++++++++++++++++++++++++++++++++-
 4 files changed, 242 insertions(+), 1 deletion(-)

diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index ec2eefa..8532b7d 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -3255,6 +3255,7 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
 	config->fast_reauth = DEFAULT_FAST_REAUTH;
 	config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
 	config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
+	config->p2p_go_freq_change_policy = DEFAULT_P2P_GO_FREQ_MOVE;
 	config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
 	config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN;
 	config->bss_max_count = DEFAULT_BSS_MAX_COUNT;
@@ -3835,6 +3836,7 @@ static const struct global_parse_data global_fields[] = {
 	{ INT_RANGE(persistent_reconnect, 0, 1), 0 },
 	{ INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
 	{ INT(p2p_group_idle), 0 },
+	{ INT_RANGE(p2p_go_freq_change_policy, 0, P2P_GO_FREQ_MOVE_MAX), 0 },
 	{ FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
 	{ FUNC(p2p_no_go_freq), CFG_CHANGED_P2P_PREF_CHAN },
 	{ INT_RANGE(p2p_add_cli_chan, 0, 1), 0 },
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 95e85d4..9688a32 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -715,6 +715,35 @@ struct wpa_config {
 	int p2p_group_idle;
 
 	/**
+	 * p2p_go_freq_change_policy - Defines the go frequency change policy
+	 *
+	 * This controls the behavior of the GO when there is a change in the
+	 * map of the currently used frequencies in case that more than 1
+	 * channel is supported.
+	 *
+	 * @P2P_GO_FREQ_MOVE_SCM: prefer working in a single channel mode if
+	 * possible. In case the GO is the only interface using
+	 * its frequency and there are other station interfaces on other
+	 * frequencies, the GO will migrate to one of these frequencies.
+	 *
+	 * @P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS: same as P2P_GO_FREQ_MOVE_SCM,
+	 * but a transition is possible only in case that one of the other used
+	 * frequencies is one of the frequencies in the intersection of the
+	 * frequency list of the local device and the peer device.
+	 *
+	 * @P2P_GO_FREQ_MOVE_STAY: prefer to stay on the current frequency.
+	 */
+	enum {
+		P2P_GO_FREQ_MOVE_SCM = 0,
+		P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS = 1,
+		P2P_GO_FREQ_MOVE_STAY = 2,
+		P2P_GO_FREQ_MOVE_MAX = P2P_GO_FREQ_MOVE_STAY,
+
+	} p2p_go_freq_change_policy;
+
+#define DEFAULT_P2P_GO_FREQ_MOVE P2P_GO_FREQ_MOVE_STAY
+
+	/**
 	 * bss_max_count - Maximum number of BSS entries to keep in memory
 	 */
 	unsigned int bss_max_count;
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 766f3ec..61c8a2a 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1050,6 +1050,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
 	if (config->p2p_ignore_shared_freq)
 		fprintf(f, "p2p_ignore_shared_freq=%u\n",
 			config->p2p_ignore_shared_freq);
+	if (config->p2p_go_freq_change_policy != DEFAULT_P2P_GO_FREQ_MOVE)
+		fprintf(f, "p2p_go_freq_change_policy=%u\n",
+			config->p2p_go_freq_change_policy);
 #endif /* CONFIG_P2P */
 	if (config->country[0] && config->country[1]) {
 		fprintf(f, "country=%c%c\n",
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index bf416ea..5954afe 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -46,6 +46,12 @@
 
 #define P2P_AUTO_PD_SCAN_ATTEMPTS 5
 
+/**
+ * Defines time interval when a GO needs to evacuate a frequency that it is
+ * currently using, but is not longer valid for P2P use cases.
+ */
+#define P2P_GO_FREQ_CHANGE_TIME 30
+
 #ifndef P2P_MAX_CLIENT_IDLE
 /*
  * How many seconds to try to reconnect to the GO when connection in P2P client
@@ -97,7 +103,8 @@ enum p2p_group_removal_reason {
 	P2P_GROUP_REMOVAL_UNAVAILABLE,
 	P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
 	P2P_GROUP_REMOVAL_PSK_FAILURE,
-	P2P_GROUP_REMOVAL_FREQ_CONFLICT
+	P2P_GROUP_REMOVAL_FREQ_CONFLICT,
+	P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL
 };
 
 
@@ -121,12 +128,14 @@ static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
 					     void *timeout_ctx);
 static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx);
+
 static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
 					int group_added);
 static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
 static void wpas_stop_listen(void *ctx);
 static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx);
 
+static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx);
 
 /*
  * Get the number of concurrent channels that the HW can operate, but that are
@@ -506,6 +515,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
 	}
 
 	wpa_s->p2p_in_invitation = 0;
+	eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
 
 	/*
 	 * Make sure wait for the first client does not remain active after the
@@ -1427,6 +1437,7 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
 	d->num_sec_device_types = s->num_sec_device_types;
 
 	d->p2p_group_idle = s->p2p_group_idle;
+	d->p2p_go_freq_change_policy = s->p2p_go_freq_change_policy;
 	d->p2p_intra_bss = s->p2p_intra_bss;
 	d->persistent_reconnect = s->persistent_reconnect;
 	d->max_num_sta = s->max_num_sta;
@@ -2953,6 +2964,27 @@ static int freq_included(const struct p2p_channels *channels, unsigned int freq)
 
 }
 
+/*
+ * Check if the given frequency is one of the possible operating frequencies
+ * set after the completion of the GoN.
+ */
+static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq)
+{
+	unsigned int i;
+
+	p2p_go_dump_common_freqs(wpa_s);
+
+	/* assume no restrictions */
+	if (!wpa_s->p2p_group_common_freqs_num)
+		return 1;
+
+	for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+		if (wpa_s->p2p_group_common_freqs[i] == freq)
+			return 1;
+	}
+	return 0;
+}
+
 /**
  * Pick the best frequency to use from all the currently used frequencies.
  * The function assumes that all the freqs in freqs_data are valid P2P
@@ -7939,6 +7971,172 @@ static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
 }
 
 
+static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->csa_supported) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled");
+		return -1;
+	}
+
+	/* TODO: add csa support */
+	wpa_dbg(wpa_s, MSG_DEBUG, "Move GO with CSA is not implemented");
+	return -1;
+}
+
+static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s)
+{
+	struct p2p_go_neg_results params;
+	struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Move GO from freq=%dMHz",
+		wpa_s->current_ssid->frequency);
+
+	/* Stop the AP functionality  */
+	wpa_supplicant_ap_deinit(wpa_s);
+
+	/* Reselect the GO frequency */
+	if (wpas_p2p_init_go_params(wpa_s, &params, 0, 0, 0, NULL)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Failed to reselect freq.");
+		wpas_p2p_group_delete(wpa_s,
+				      P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
+		return;
+	}
+	wpa_dbg(wpa_s, MSG_DEBUG, "New freq selected for the GO (%u MHz)",
+		params.freq);
+
+	if (params.freq &&
+	    !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: selected freq (%u MHz) is not valid for P2P",
+			   params.freq);
+		wpas_p2p_group_delete(wpa_s,
+				      P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
+		return;
+	}
+
+	/* Update the frequency */
+	current_ssid->frequency = params.freq;
+	wpa_s->connect_without_scan = current_ssid;
+	wpa_s->reassociate = 1;
+	wpa_s->disconnected = 0;
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (!wpa_s->ap_iface || !wpa_s->current_ssid)
+		return;
+
+	/*
+	 * first try a channel switch flow, if it is not supported or fails,
+	 * perform take down the GO and bring it up again
+	 */
+	if (wpas_p2p_move_go_csa(wpa_s) < 0)
+		wpas_p2p_move_go_no_csa(wpa_s);
+}
+
+/*
+ * Consider moving a GO from its currently used frequency:
+ * 1. It is possible that due to regulatory consideration the frequency
+ *    can no longer be used and there is a need to evacuate the GO.
+ * 2. It is possible that due to MCC considerations, it would be preferable
+ *    to move the GO to a channel that is currently used by some other
+ *    station interface.
+ *
+ * In case that a frequency that became invalid is once again valid,
+ * cancel a previously initiated GO frequency change.
+ */
+static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s,
+					    struct wpa_used_freq_data *freqs,
+					    unsigned int num)
+{
+	unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0;
+	unsigned int timeout;
+	int freq;
+
+	freq = wpa_s->current_ssid->frequency;
+	for (i = 0, invalid_freq = 0; i < num; i++) {
+		if (freqs[i].freq == freq) {
+			flags = freqs[i].flags;
+
+			/* The channel is invalid, must change it */
+			if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"Freq=%dMHz not longer valid for GO",
+					freq);
+				invalid_freq = 1;
+			}
+		} else if (freqs[i].flags == 0) {
+			/* freq is not used by any other station interface */
+			continue;
+		} else if (!p2p_supported_freq(wpa_s->global->p2p,
+					       freqs[i].freq)) {
+			/* freq is not valid for P2P use cases  */
+			continue;
+		} else if (wpa_s->conf->p2p_go_freq_change_policy ==
+			   P2P_GO_FREQ_MOVE_SCM) {
+			policy_move = 1;
+		} else if ((wpa_s->conf->p2p_go_freq_change_policy ==
+			    P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS) &&
+			   wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
+			policy_move = 1;
+		}
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"GO move: invalid_freq=%u, policy_move=%u, flags=0x%X",
+		invalid_freq, policy_move, flags);
+
+	/* The channel is valid, or we are going to have a policy move, so
+	 * cancel timeout */
+	if (invalid_freq == 0 || policy_move == 1) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Cancel a GO move from freq=%dMHz",
+			freq);
+		eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+	}
+
+	if (invalid_freq == 0 && (policy_move == 0 || flags != 0)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Not initiating a GO frequency change");
+		return;
+	}
+
+	if (invalid_freq)
+		timeout = P2P_GO_FREQ_CHANGE_TIME;
+	if (policy_move)
+		timeout = 0;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "Move GO from freq=%dMHz in %d secs",
+		freq, timeout);
+	eloop_register_timeout(timeout, 0, wpas_p2p_move_go, wpa_s, NULL);
+	return;
+}
+
+static void wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
+					 struct wpa_used_freq_data *freqs,
+					 unsigned int num)
+{
+	struct wpa_supplicant *ifs;
+
+	/*
+	 * Travers all the radio interfaces, and for each GO interface, check
+	 * if there is a need to  move the GO from the frequency it is using,
+	 * or in case that the frequency is valid again, cancel the
+	 * evacuation flow
+	 */
+	dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+			 radio_list) {
+		if (ifs->current_ssid == NULL ||
+		    ifs->current_ssid->mode != WPAS_MODE_P2P_GO)
+			continue;
+
+		wpas_p2p_consider_moving_one_go(ifs, freqs, num);
+	}
+}
+
+
 void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_used_freq_data *freqs = NULL;
@@ -7969,5 +8167,14 @@ void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
 	num = get_shared_radio_freqs_data(wpa_s, freqs, num);
 
 	wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
+
+	/*
+	 * The used frequencies map changed, so it is possible that a GO is
+	 * using an channel that is no longer valid for P2P use, or it is also
+	 * possible that due to policy consideration, it would preferable to
+	 * move it to a frequency already used by other station interfaces
+	 */
+	wpas_p2p_consider_moving_gos(wpa_s, freqs, num);
+
 	os_free(freqs);
 }
-- 
1.7.10.4




More information about the Hostap mailing list