P2P: Do not perform P2P GO CS in some cases
[mech_eap.git] / wpa_supplicant / p2p_supplicant.c
index e41c2bf..e849b91 100644 (file)
 
 #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
 
 #define P2P_MGMT_DEVICE_PREFIX         "p2p-dev-"
 
+/*
+ * How many seconds to wait to re-attempt to move GOs, in case previous attempt
+ * was not possible.
+ */
+#define P2P_RECONSIDER_GO_MOVE_DELAY 30
+
 enum p2p_group_removal_reason {
        P2P_GROUP_REMOVAL_UNKNOWN,
        P2P_GROUP_REMOVAL_SILENT,
@@ -94,7 +106,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 +141,16 @@ 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_optimize_listen_channel(struct wpa_supplicant *wpa_s,
+                                            struct wpa_used_freq_data *freqs,
+                                            unsigned int num);
+static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx);
+static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq);
+static void
+wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
+                            struct wpa_used_freq_data *freqs, unsigned int num,
+                            enum wpas_p2p_channel_update_trig trig);
+static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx);
 
 
 /*
@@ -872,9 +895,12 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
                wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation "
                           "timeout");
                wpa_s->p2p_in_provisioning = 0;
+               wpas_p2p_group_formation_failed(wpa_s, 1);
        }
 
        wpa_s->p2p_in_invitation = 0;
+       eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+       eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL);
 
        /*
         * Make sure wait for the first client does not remain active after the
@@ -1865,6 +1891,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;
@@ -2586,12 +2613,62 @@ static void wpas_prov_disc_fail(void *ctx, const u8 *peer,
 }
 
 
-static int freq_included(const struct p2p_channels *channels, unsigned int freq)
+static int freq_included(struct wpa_supplicant *wpa_s,
+                        const struct p2p_channels *channels,
+                        unsigned int freq)
 {
-       if (channels == NULL)
-               return 1; /* Assume no restrictions */
-       return p2p_channels_includes_freq(channels, freq);
+       if ((channels == NULL || p2p_channels_includes_freq(channels, freq)) &&
+           wpas_p2p_go_is_peer_freq(wpa_s, freq))
+               return 1;
+       return 0;
+}
+
+
+static void wpas_p2p_go_update_common_freqs(struct wpa_supplicant *wpa_s)
+{
+       unsigned int num = P2P_MAX_CHANNELS;
+       int *common_freqs;
+       int ret;
 
+       p2p_go_dump_common_freqs(wpa_s);
+       common_freqs = os_calloc(num, sizeof(int));
+       if (!common_freqs)
+               return;
+
+       ret = p2p_group_get_common_freqs(wpa_s->p2p_group, common_freqs, &num);
+       if (ret < 0) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "P2P: Failed to get group common freqs");
+               os_free(common_freqs);
+               return;
+       }
+
+       os_free(wpa_s->p2p_group_common_freqs);
+       wpa_s->p2p_group_common_freqs = common_freqs;
+       wpa_s->p2p_group_common_freqs_num = num;
+       p2p_go_dump_common_freqs(wpa_s);
+}
+
+
+/*
+ * 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;
 }
 
 
@@ -2767,7 +2844,7 @@ accept_inv:
                                   "running a GO but we are capable of MCC, "
                                   "figure out the best channel to use");
                        *force_freq = 0;
-               } else if (!freq_included(channels, *force_freq)) {
+               } else if (!freq_included(wpa_s, channels, *force_freq)) {
                        /* We are the GO, and *force_freq is not in the
                         * intersection */
                        wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
@@ -2974,10 +3051,10 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
        os_sleep(0, 50000);
 
        if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO &&
-           freq_included(channels, neg_freq))
+           freq_included(wpa_s, channels, neg_freq))
                freq = neg_freq;
        else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO &&
-                freq_included(channels, peer_oper_freq))
+                freq_included(wpa_s, channels, peer_oper_freq))
                freq = peer_oper_freq;
        else
                freq = 0;
@@ -5293,30 +5370,45 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
 }
 
 
-static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s,
-                                       struct p2p_go_neg_results *params,
-                                       const struct p2p_channels *channels)
+static int wpas_p2p_supported_freq_go(struct wpa_supplicant *wpa_s,
+                                     const struct p2p_channels *channels,
+                                     int freq)
+{
+       if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) &&
+           p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
+           freq_included(wpa_s, channels, freq))
+               return 1;
+       return 0;
+}
+
+
+static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s,
+                                           struct p2p_go_neg_results *params,
+                                           const struct p2p_channels *channels)
 {
        unsigned int i, r;
 
        /* first try some random selection of the social channels */
        if (os_get_random((u8 *) &r, sizeof(r)) < 0)
-               return -1;
+               return;
 
        for (i = 0; i < 3; i++) {
                params->freq = 2412 + ((r + i) % 3) * 25;
-               if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-                   freq_included(channels, params->freq) &&
-                   p2p_supported_freq(wpa_s->global->p2p, params->freq))
+               if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
                        goto out;
        }
 
-       /* try all channels in reg. class 81 */
+       /* try all other channels in operating class 81 */
        for (i = 0; i < 11; i++) {
                params->freq = 2412 + i * 5;
-               if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-                   freq_included(channels, params->freq) &&
-                   p2p_supported_freq(wpa_s->global->p2p, params->freq))
+
+               /* skip social channels; covered in the previous loop */
+               if (params->freq == 2412 ||
+                   params->freq == 2437 ||
+                   params->freq == 2462)
+                       continue;
+
+               if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
                        goto out;
        }
 
@@ -5324,7 +5416,7 @@ static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s,
        for (i = 0; i < 4; i++) {
                params->freq = 5180 + i * 20;
                if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-                   freq_included(channels, params->freq) &&
+                   freq_included(wpa_s, channels, params->freq) &&
                    p2p_supported_freq(wpa_s->global->p2p, params->freq))
                        goto out;
        }
@@ -5333,7 +5425,7 @@ static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s,
        for (i = 0; i < 4; i++) {
                params->freq = 5745 + i * 20;
                if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-                   freq_included(channels, params->freq) &&
+                   freq_included(wpa_s, channels, params->freq) &&
                    p2p_supported_freq(wpa_s->global->p2p, params->freq))
                        goto out;
        }
@@ -5341,7 +5433,7 @@ static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s,
        /* try social channel class 180 channel 2 */
        params->freq = 58320 + 1 * 2160;
        if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-           freq_included(channels, params->freq) &&
+           freq_included(wpa_s, channels, params->freq) &&
            p2p_supported_freq(wpa_s->global->p2p, params->freq))
                goto out;
 
@@ -5349,17 +5441,17 @@ static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s,
        for (i = 0; i < 4; i++) {
                params->freq = 58320 + i * 2160;
                if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-                   freq_included(channels, params->freq) &&
+                   freq_included(wpa_s, channels, params->freq) &&
                    p2p_supported_freq(wpa_s->global->p2p, params->freq))
                        goto out;
        }
 
+       params->freq = 0;
        wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed");
-       return -1;
+       return;
 out:
        wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)",
                   params->freq);
-       return 0;
 }
 
 
@@ -5369,130 +5461,186 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
                                   const struct p2p_channels *channels)
 {
        struct wpa_used_freq_data *freqs;
-       unsigned int pref_freq, cand_freq;
+       unsigned int cand;
        unsigned int num, i;
 
        os_memset(params, 0, sizeof(*params));
        params->role_go = 1;
        params->ht40 = ht40;
        params->vht = vht;
+
+       if (wpa_s->p2p_group_common_freqs_num)
+               wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO",
+                          __func__);
+
+       freqs = os_calloc(wpa_s->num_multichan_concurrent,
+                         sizeof(struct wpa_used_freq_data));
+       if (!freqs)
+               return -1;
+
+       num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
+                                       wpa_s->num_multichan_concurrent);
+
+       /* try using the forced freq */
        if (freq) {
-               if (!freq_included(channels, freq)) {
-                       wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
-                                  "accepted", freq);
-                       return -1;
+               if (!wpas_p2p_supported_freq_go(wpa_s, channels, freq)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: Forced GO freq %d MHz not accepted",
+                                  freq);
+                       goto fail;
                }
-               wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced "
-                          "frequency %d MHz", freq);
+
+               for (i = 0; i < num; i++) {
+                       if (freqs[i].freq == freq) {
+                               wpa_printf(MSG_DEBUG,
+                                          "P2P: forced freq (%d MHz) is also shared",
+                                          freq);
+                               params->freq = freq;
+                               goto success;
+                       }
+               }
+
+               if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: Cannot force GO on freq (%d MHz) as all the channels are in use",
+                                  freq);
+                       goto fail;
+               }
+
+               wpa_printf(MSG_DEBUG,
+                          "P2P: force GO freq (%d MHz) on a free channel",
+                          freq);
                params->freq = freq;
-       } else if (wpa_s->conf->p2p_oper_reg_class == 81 &&
-                  wpa_s->conf->p2p_oper_channel >= 1 &&
-                  wpa_s->conf->p2p_oper_channel <= 11 &&
-                  freq_included(channels,
-                                2407 + 5 * wpa_s->conf->p2p_oper_channel)) {
+               goto success;
+       }
+
+       /* consider using one of the shared frequencies */
+       if (num) {
+               cand = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+               if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: Use shared freq (%d MHz) for GO",
+                                  freq);
+                       params->freq = cand;
+                       goto success;
+               }
+
+               /* try using one of the shared freqs */
+               for (i = 0; i < num; i++) {
+                       if (wpas_p2p_supported_freq_go(wpa_s, channels,
+                                                      freqs[i].freq)) {
+                               wpa_printf(MSG_DEBUG,
+                                          "P2P: Use shared freq (%d MHz) for GO",
+                                          freq);
+                               params->freq = freqs[i].freq;
+                               goto success;
+                       }
+               }
+       }
+
+       if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+               wpa_printf(MSG_DEBUG,
+                          "P2P: Cannot force GO on any of the channels we are already using");
+               goto fail;
+       }
+
+       /* try using the setting from the configuration file */
+       if (wpa_s->conf->p2p_oper_reg_class == 81 &&
+           wpa_s->conf->p2p_oper_channel >= 1 &&
+           wpa_s->conf->p2p_oper_channel <= 11 &&
+           wpas_p2p_supported_freq_go(
+                   wpa_s, channels,
+                   2407 + 5 * wpa_s->conf->p2p_oper_channel)) {
                params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
                           "frequency %d MHz", params->freq);
-       } else if ((wpa_s->conf->p2p_oper_reg_class == 115 ||
-                   wpa_s->conf->p2p_oper_reg_class == 116 ||
-                   wpa_s->conf->p2p_oper_reg_class == 117 ||
-                   wpa_s->conf->p2p_oper_reg_class == 124 ||
-                   wpa_s->conf->p2p_oper_reg_class == 125 ||
-                   wpa_s->conf->p2p_oper_reg_class == 126 ||
-                   wpa_s->conf->p2p_oper_reg_class == 127) &&
-                  freq_included(channels,
-                                5000 + 5 * wpa_s->conf->p2p_oper_channel)) {
+               goto success;
+       }
+
+       if ((wpa_s->conf->p2p_oper_reg_class == 115 ||
+            wpa_s->conf->p2p_oper_reg_class == 116 ||
+            wpa_s->conf->p2p_oper_reg_class == 117 ||
+            wpa_s->conf->p2p_oper_reg_class == 124 ||
+            wpa_s->conf->p2p_oper_reg_class == 125 ||
+            wpa_s->conf->p2p_oper_reg_class == 126 ||
+            wpa_s->conf->p2p_oper_reg_class == 127) &&
+           wpas_p2p_supported_freq_go(wpa_s, channels,
+                                      5000 +
+                                      5 * wpa_s->conf->p2p_oper_channel)) {
                params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
                           "frequency %d MHz", params->freq);
-       } else if (wpa_s->conf->p2p_oper_channel == 0 &&
-                  wpa_s->best_overall_freq > 0 &&
-                  p2p_supported_freq_go(wpa_s->global->p2p,
-                                        wpa_s->best_overall_freq) &&
-                  freq_included(channels, wpa_s->best_overall_freq)) {
+               goto success;
+       }
+
+       /* Try using best channels */
+       if (wpa_s->conf->p2p_oper_channel == 0 &&
+           wpa_s->best_overall_freq > 0 &&
+           wpas_p2p_supported_freq_go(wpa_s, channels,
+                                      wpa_s->best_overall_freq)) {
                params->freq = wpa_s->best_overall_freq;
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
                           "channel %d MHz", params->freq);
-       } else if (wpa_s->conf->p2p_oper_channel == 0 &&
-                  wpa_s->best_24_freq > 0 &&
-                  p2p_supported_freq_go(wpa_s->global->p2p,
-                                        wpa_s->best_24_freq) &&
-                  freq_included(channels, wpa_s->best_24_freq)) {
+               goto success;
+       }
+
+       if (wpa_s->conf->p2p_oper_channel == 0 &&
+           wpa_s->best_24_freq > 0 &&
+           wpas_p2p_supported_freq_go(wpa_s, channels,
+                                      wpa_s->best_24_freq)) {
                params->freq = wpa_s->best_24_freq;
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
                           "channel %d MHz", params->freq);
-       } else if (wpa_s->conf->p2p_oper_channel == 0 &&
-                  wpa_s->best_5_freq > 0 &&
-                  p2p_supported_freq_go(wpa_s->global->p2p,
-                                        wpa_s->best_5_freq) &&
-                  freq_included(channels, wpa_s->best_5_freq)) {
+               goto success;
+       }
+
+       if (wpa_s->conf->p2p_oper_channel == 0 &&
+           wpa_s->best_5_freq > 0 &&
+           wpas_p2p_supported_freq_go(wpa_s, channels,
+                                      wpa_s->best_5_freq)) {
                params->freq = wpa_s->best_5_freq;
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
                           "channel %d MHz", params->freq);
-       } else if ((pref_freq = p2p_get_pref_freq(wpa_s->global->p2p,
-                                                 channels))) {
-               params->freq = pref_freq;
+               goto success;
+       }
+
+       /* try using preferred channels */
+       cand = p2p_get_pref_freq(wpa_s->global->p2p, channels);
+       if (cand && wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+               params->freq = cand;
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
                           "channels", params->freq);
-       } else {
-               /* no preference, select some channel */
-               if (wpas_p2p_select_freq_no_pref(wpa_s, params, channels) < 0)
-                       return -1;
+               goto success;
        }
 
-       freqs = os_calloc(wpa_s->num_multichan_concurrent,
-                         sizeof(struct wpa_used_freq_data));
-       if (!freqs)
-               return -1;
-
-       num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
-                                       wpa_s->num_multichan_concurrent);
-
-       cand_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
-
-       /* First try the best used frequency if possible */
-       if (!freq && cand_freq > 0 && freq_included(channels, cand_freq)) {
-               params->freq = cand_freq;
-       } else if (!freq) {
-               /* Try any of the used frequencies */
-               for (i = 0; i < num; i++) {
-                       if (freq_included(channels, freqs[i].freq)) {
-                               wpa_printf(MSG_DEBUG, "P2P: Force GO on a channel we are already using (%u MHz)",
-                                          freqs[i].freq);
-                               params->freq = freqs[i].freq;
-                               break;
+       /* Try using one of the group common freqs */
+       if (wpa_s->p2p_group_common_freqs) {
+               for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+                       cand = wpa_s->p2p_group_common_freqs[i];
+                       if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+                               params->freq = cand;
+                               wpa_printf(MSG_DEBUG,
+                                          "P2P: Use freq %d MHz common with the peer",
+                                          params->freq);
+                               goto success;
                        }
                }
+       }
 
-               if (i == num) {
-                       if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
-                               wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using");
-                               os_free(freqs);
-                               return -1;
-                       } else {
-                               wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using. Use one of the free channels");
-                       }
-               }
-       } else {
-               for (i = 0; i < num; i++) {
-                       if (freqs[i].freq == freq)
-                               break;
-               }
+       /* no preference, select some channel */
+       wpas_p2p_select_go_freq_no_pref(wpa_s, params, channels);
 
-               if (i == num) {
-                       if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
-                               if (freq)
-                                       wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq);
-                               os_free(freqs);
-                               return -1;
-                       } else {
-                               wpa_printf(MSG_DEBUG, "P2P: Use one of the free channels");
-                       }
-               }
+       if (params->freq == 0) {
+               wpa_printf(MSG_DEBUG, "P2P: did not find a freq for GO use");
+               goto fail;
        }
 
+success:
        os_free(freqs);
        return 0;
+fail:
+       os_free(freqs);
+       return -1;
 }
 
 
@@ -5699,12 +5847,12 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
                } else {
                        freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
                        if (freq < 0 ||
-                           (freq > 0 && !freq_included(channels, freq)))
+                           (freq > 0 && !freq_included(wpa_s, channels, freq)))
                                freq = 0;
                }
        } else if (ssid->mode == WPAS_MODE_INFRA) {
                freq = neg_freq;
-               if (freq <= 0 || !freq_included(channels, freq)) {
+               if (freq <= 0 || !freq_included(wpa_s, channels, freq)) {
                        struct os_reltime now;
                        struct wpa_bss *bss =
                                wpa_bss_get_p2p_dev_addr(wpa_s, ssid->bssid);
@@ -5712,7 +5860,7 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
                        os_get_reltime(&now);
                        if (bss &&
                            !os_reltime_expired(&now, &bss->last_update, 5) &&
-                           freq_included(channels, bss->freq))
+                           freq_included(wpa_s, channels, bss->freq))
                                freq = bss->freq;
                        else
                                freq = 0;
@@ -6929,14 +7077,22 @@ void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
 }
 
 
-void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
+                                 enum wpas_p2p_channel_update_trig trig)
 {
        struct p2p_channels chan, cli_chan;
-       struct wpa_supplicant *ifs;
+       struct wpa_used_freq_data *freqs = NULL;
+       unsigned int num = wpa_s->num_multichan_concurrent;
 
        if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
                return;
 
+       freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
+       if (!freqs)
+               return;
+
+       num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+
        os_memset(&chan, 0, sizeof(chan));
        os_memset(&cli_chan, 0, sizeof(cli_chan));
        if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan)) {
@@ -6947,27 +7103,17 @@ void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
 
        p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan);
 
-       for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-               int freq;
-               if (!ifs->current_ssid ||
-                   !ifs->current_ssid->p2p_group ||
-                   (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
-                    ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
-                               continue;
-               freq = ifs->current_ssid->frequency;
-               if (freq_included(&chan, freq)) {
-                       wpa_dbg(ifs, MSG_DEBUG,
-                               "P2P GO operating frequency %d MHz in valid range",
-                               freq);
-                       continue;
-               }
+       wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
 
-               wpa_dbg(ifs, MSG_DEBUG,
-                       "P2P GO operating in invalid frequency %d MHz", freq);
-               /* TODO: Consider using CSA or removing the group within
-                * wpa_supplicant */
-               wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
-       }
+       /*
+        * 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 it to a frequency already used by other station interfaces.
+        */
+       wpas_p2p_consider_moving_gos(wpa_s, freqs, num, trig);
+
+       os_free(freqs);
 }
 
 
@@ -8227,6 +8373,16 @@ static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
        u8 curr_chan, cand, chan;
        unsigned int i;
 
+       /*
+        * If possible, optimize the Listen channel to be a channel that is
+        * already used by one of the other interfaces.
+        */
+       if (!wpa_s->conf->p2p_optimize_listen_chan)
+               return;
+
+       if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
+               return;
+
        curr_chan = p2p_get_listen_channel(wpa_s->global->p2p);
        for (i = 0, cand = 0; i < num; i++) {
                ieee80211_freq_to_chan(freqs[i].freq, &chan);
@@ -8248,35 +8404,245 @@ static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
 }
 
 
-void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s)
 {
-       struct wpa_used_freq_data *freqs;
-       unsigned int num = wpa_s->num_multichan_concurrent;
+       if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled");
+               return -1;
+       }
 
-       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+       /* 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, &params, 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 possible, optimize the Listen channel to be a channel that is
-        * already used by one of the other interfaces.
-        */
-       if (!wpa_s->conf->p2p_optimize_listen_chan)
+       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;
+       }
 
-       if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
+       /* 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;
 
+       wpas_p2p_go_update_common_freqs(wpa_s);
+
+       /*
+        * 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);
+}
+
+
+static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct wpa_used_freq_data *freqs = NULL;
+       unsigned int num = wpa_s->num_multichan_concurrent;
+
        freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
        if (!freqs)
                return;
 
        num = get_shared_radio_freqs_data(wpa_s, freqs, num);
 
-       wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
+       /* Previous attempt to move a GO was not possible -- try again. */
+       wpas_p2p_consider_moving_gos(wpa_s, freqs, num,
+                                    WPAS_P2P_CHANNEL_UPDATE_ANY);
+
        os_free(freqs);
 }
 
 
+/*
+ * 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;
+
+       wpas_p2p_go_update_common_freqs(wpa_s);
+
+       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 (wpas_p2p_in_progress(wpa_s)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "P2P: GO move: policy CS is not allowed - setting timeout to re-consider GO move");
+                       eloop_cancel_timeout(wpas_p2p_reconsider_moving_go,
+                                            wpa_s, NULL);
+                       eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
+                                              wpas_p2p_reconsider_moving_go,
+                                              wpa_s, NULL);
+                       return;
+               }
+       }
+
+       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,
+                                        enum wpas_p2p_channel_update_trig trig)
+{
+       struct wpa_supplicant *ifs;
+
+       eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, ELOOP_ALL_CTX,
+                            NULL);
+
+       /*
+        * 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;
+
+               /*
+                * The GO was just started or completed channel switch, no need
+                * to move it.
+                */
+               if (wpa_s == ifs &&
+                   (trig == WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE ||
+                    trig == WPAS_P2P_CHANNEL_UPDATE_CS)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "P2P: GO move - schedule re-consideration");
+                       eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
+                                              wpas_p2p_reconsider_moving_go,
+                                              wpa_s, NULL);
+                       continue;
+               }
+
+               wpas_p2p_consider_moving_one_go(ifs, freqs, num);
+       }
+}
+
+
+void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return;
+
+       wpas_p2p_update_channel_list(wpa_s,
+                                    WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE);
+}
+
+
 void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
 {
        if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {