Patchwork [10/23] P2P: Move a GO from its operating frequency

login
register
mail settings
Submitter Ilan Peer
Date July 7, 2014, 11:21 a.m.
Message ID <1404732076-32252-11-git-send-email-ilan.peer@intel.com>
Download mbox | patch
Permalink /patch/367513/
State New
Headers show

Comments

Ilan Peer - July 7, 2014, 11:21 a.m.
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.

Change-Id: I6fb713d19cc15f2da26418899c03121a4a0c8091
Signed-off-by: Ilan Peer <ilan.peer@intel.com>
---
 wpa_supplicant/config.c         |    2 +
 wpa_supplicant/config.h         |   29 ++++++
 wpa_supplicant/config_file.c    |    3 +
 wpa_supplicant/p2p_supplicant.c |  216 ++++++++++++++++++++++++++++++++++++++-
 4 files changed, 248 insertions(+), 2 deletions(-)

Patch

diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 8cd4a2f..d3bd8df 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -3274,6 +3274,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;
@@ -3855,6 +3856,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 },
 	{ INT_RANGE(p2p_passphrase_len, 8, 63),
 	  CFG_CHANGED_P2P_PASSPHRASE_LEN },
 	{ FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 52add9d..7316ad4 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -717,6 +717,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
+
+	/**
 	 * p2p_passphrase_len - Passphrase length (8..63) for P2P GO
 	 *
 	 * This parameter controls the length of the random passphrase that is
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 98855d8..b77e9fa 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -1057,6 +1057,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 895683c..5b4b59c 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
@@ -93,7 +99,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
 };
 
 
@@ -123,7 +130,7 @@  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_group_deinit(struct wpa_supplicant *wpa_s);
-
+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
@@ -504,6 +511,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
@@ -1426,6 +1434,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;
@@ -2959,6 +2968,28 @@  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.
  */
@@ -7992,6 +8023,178 @@  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_msg_global(wpa_s, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+
+	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 && !wpas_p2p_disallowed_freq(wpa_s->global, freq))
+		timeout = P2P_GO_FREQ_CHANGE_TIME;
+	else
+		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;
@@ -8022,6 +8225,15 @@  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);
 }