From 1a471ff0810ac660115e4175e44e1c37875c3519 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Mon, 27 Jul 2015 22:24:19 +0300 Subject: [PATCH] P2P: Move a GO from its operating frequency 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 GO Negotiation/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 --- wpa_supplicant/config.c | 2 + wpa_supplicant/config.h | 28 ++++ wpa_supplicant/config_file.c | 3 + wpa_supplicant/p2p_supplicant.c | 219 +++++++++++++++++++++++++++++++- 4 files changed, 251 insertions(+), 1 deletion(-) diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 4841646f3..b1adab77b 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -3522,6 +3522,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->p2p_go_ctwindow = DEFAULT_P2P_GO_CTWINDOW; @@ -4176,6 +4177,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 0bc8fecc9..d42788348 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -738,6 +738,34 @@ struct wpa_config { */ int p2p_group_idle; + /** + * p2p_go_freq_change_policy - 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 more than one 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 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 * diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 5bab821a6..2af187dd4 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -1133,6 +1133,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) config->p2p_ignore_shared_freq); if (config->p2p_cli_probe) fprintf(f, "p2p_cli_probe=%d\n", config->p2p_cli_probe); + 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 04db85697..9c256a5b4 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -47,6 +47,12 @@ #define P2P_AUTO_PD_SCAN_ATTEMPTS 5 +/** + * Defines time interval in seconds when a GO needs to evacuate a frequency that + * it is currently using, but is no longer valid for P2P use cases. + */ +#define P2P_GO_FREQ_CHANGE_TIME 5 + #ifndef P2P_MAX_CLIENT_IDLE /* * How many seconds to try to reconnect to the GO when connection in P2P client @@ -94,7 +100,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 }; @@ -128,6 +135,7 @@ static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, enum wpa_driver_if_type type); static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s, int already_deleted); +static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx); /* @@ -876,6 +884,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 @@ -1866,6 +1875,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; @@ -2596,6 +2606,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 GO Negotiation. + */ +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. */ @@ -8249,6 +8281,181 @@ 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->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) { + wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled"); + return -1; + } + + /* TODO: Add CSA support */ + wpa_dbg(wpa_s, MSG_DEBUG, "Moving 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, "P2P: Move GO from freq=%d MHz", + current_ssid->frequency); + + /* Stop the AP functionality */ + /* TODO: Should do this in a way that does not indicated to possible + * P2P Clients in the group that the group is terminated. */ + wpa_supplicant_ap_deinit(wpa_s); + + /* Reselect the GO frequency */ + if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, NULL)) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq"); + wpas_p2p_group_delete(wpa_s, + P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL); + return; + } + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 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, + * 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 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, + "P2P: Freq=%d MHz no 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, + "P2P: 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 || policy_move) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Cancel a GO move from freq=%d MHz", freq); + eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL); + } + + if (!invalid_freq && (!policy_move || flags != 0)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: 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, "P2P: Move GO from freq=%d MHz in %d secs", + freq, timeout); + eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL); + eloop_register_timeout(timeout, 0, wpas_p2p_move_go, wpa_s, NULL); +} + + +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 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; @@ -8274,6 +8481,16 @@ 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 a channel that is no longer valid for P2P use. It is also + * possible that due to policy consideration, it would be preferable to + * move the group to a frequency already used by other station + * interfaces. + */ + wpas_p2p_consider_moving_gos(wpa_s, freqs, num); + os_free(freqs); }