P2P: Set p2p_go_wait_client in invitation_result() cb
[mech_eap.git] / wpa_supplicant / p2p_supplicant.c
index 78bdd08..88c90d0 100644 (file)
  */
 #define P2P_GO_FREQ_CHANGE_TIME 5
 
+/**
+ * Defines CSA parameters which are used when GO evacuates the no longer valid
+ * channel (and if the driver supports channel switch).
+ */
+#define P2P_GO_CSA_COUNT 7
+#define P2P_GO_CSA_BLOCK_TX 0
+
 #ifndef P2P_MAX_CLIENT_IDLE
 /*
  * How many seconds to try to reconnect to the GO when connection in P2P client
@@ -117,6 +124,10 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
                         int go);
 static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
                               const u8 *ssid, size_t ssid_len);
+static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
+                               int *force_freq, int *pref_freq, int go,
+                               unsigned int *pref_freq_list,
+                               unsigned int *num_pref_freq);
 static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
                                   const u8 *ssid, size_t ssid_len);
 static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
@@ -538,27 +549,39 @@ static unsigned int p2p_group_go_member_count(struct wpa_supplicant *wpa_s)
 }
 
 
+static unsigned int p2p_is_active_persistent_group(struct wpa_supplicant *wpa_s)
+{
+       return !wpa_s->p2p_mgmt && wpa_s->current_ssid &&
+               !wpa_s->current_ssid->disabled &&
+               wpa_s->current_ssid->p2p_group &&
+               wpa_s->current_ssid->p2p_persistent_group;
+}
+
+
+static unsigned int p2p_is_active_persistent_go(struct wpa_supplicant *wpa_s)
+{
+       return p2p_is_active_persistent_group(wpa_s) &&
+               wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO;
+}
+
+
 /* Find an interface for a P2P group where we are the GO */
 static struct wpa_supplicant *
 wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s)
 {
        struct wpa_supplicant *save = NULL;
-       struct wpa_ssid *s;
 
        if (!wpa_s)
                return NULL;
 
        for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
-               for (s = wpa_s->conf->ssid; s; s = s->next) {
-                       if (s->disabled || !s->p2p_group ||
-                           s->mode != WPAS_MODE_P2P_GO)
-                               continue;
+               if (!p2p_is_active_persistent_go(wpa_s))
+                       continue;
 
-                       /* Prefer a group with connected clients */
-                       if (p2p_get_group_num_members(wpa_s->p2p_group))
-                               return wpa_s;
-                       save = wpa_s;
-               }
+               /* Prefer a group with connected clients */
+               if (p2p_get_group_num_members(wpa_s->p2p_group))
+                       return wpa_s;
+               save = wpa_s;
        }
 
        /* No group with connected clients, so pick the one without (if any) */
@@ -566,29 +589,23 @@ wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s)
 }
 
 
-/* Find an active P2P group where we are the GO */
-static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s,
-                                               u8 *bssid)
+static unsigned int p2p_is_active_persistent_cli(struct wpa_supplicant *wpa_s)
 {
-       struct wpa_ssid *s, *empty = NULL;
+       return p2p_is_active_persistent_group(wpa_s) &&
+               wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
+}
 
-       if (!wpa_s)
-               return 0;
 
+/* Find an interface for a P2P group where we are the P2P Client */
+static struct wpa_supplicant *
+wpas_p2p_get_cli_group(struct wpa_supplicant *wpa_s)
+{
        for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
-               for (s = wpa_s->conf->ssid; s; s = s->next) {
-                       if (s->disabled || !s->p2p_group ||
-                           s->mode != WPAS_MODE_P2P_GO)
-                               continue;
-
-                       os_memcpy(bssid, wpa_s->own_addr, ETH_ALEN);
-                       if (p2p_get_group_num_members(wpa_s->p2p_group))
-                               return s;
-                       empty = s;
-               }
+               if (p2p_is_active_persistent_cli(wpa_s))
+                       return wpa_s;
        }
 
-       return empty;
+       return NULL;
 }
 
 
@@ -607,20 +624,34 @@ wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s)
 }
 
 
-static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
+static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role,
+                               unsigned int *force_freq,
+                               unsigned int *pref_freq)
 {
-       struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s;
+       struct wpa_supplicant *wpa_s = ctx;
        struct wpa_ssid *s;
        u8 conncap = P2PS_SETUP_NONE;
        unsigned int owned_members = 0;
-       unsigned int owner = 0;
-       unsigned int client = 0;
-       struct wpa_supplicant *go_wpa_s;
+       struct wpa_supplicant *go_wpa_s, *cli_wpa_s;
        struct wpa_ssid *persistent_go;
        int p2p_no_group_iface;
+       unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
 
        wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
 
+       if (force_freq)
+               *force_freq = 0;
+       if (pref_freq)
+               *pref_freq = 0;
+
+       size = P2P_MAX_PREF_CHANNELS;
+       if (force_freq && pref_freq &&
+           !wpas_p2p_setup_freqs(wpa_s, 0, (int *) force_freq,
+                                 (int *) pref_freq, 0, pref_freq_list, &size))
+               wpas_p2p_set_own_freq_preference(wpa_s,
+                                                *force_freq ? *force_freq :
+                                                *pref_freq);
+
        /*
         * For non-concurrent capable devices:
         * If persistent_go, then no new.
@@ -628,36 +659,21 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
         * If client, then no GO.
         */
        go_wpa_s = wpas_p2p_get_go_group(wpa_s);
+       if (go_wpa_s)
+               owned_members = p2p_get_group_num_members(go_wpa_s->p2p_group);
        persistent_go = wpas_p2p_get_persistent_go(wpa_s);
        p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s);
+       cli_wpa_s = wpas_p2p_get_cli_group(wpa_s);
 
-       wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p",
-                  go_wpa_s, persistent_go);
-
-       for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s;
-            tmp_wpa_s = tmp_wpa_s->next) {
-               for (s = tmp_wpa_s->conf->ssid; s; s = s->next) {
-                       wpa_printf(MSG_DEBUG,
-                                  "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
-                                  tmp_wpa_s, s, s->disabled,
-                                  s->p2p_group, s->mode);
-                       if (!s->disabled && s->p2p_group) {
-                               if (s->mode == WPAS_MODE_P2P_GO) {
-                                       owned_members +=
-                                               p2p_get_group_num_members(
-                                                       tmp_wpa_s->p2p_group);
-                                       owner++;
-                               } else
-                                       client++;
-                       }
-               }
-       }
+       wpa_printf(MSG_DEBUG,
+                  "P2P: GO(iface)=%p members=%u CLI(iface)=%p persistent(ssid)=%p",
+                  go_wpa_s, owned_members, cli_wpa_s, persistent_go);
 
        /* If not concurrent, restrict our choices */
        if (p2p_no_group_iface) {
                wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface");
 
-               if (client)
+               if (cli_wpa_s)
                        return P2PS_SETUP_NONE;
 
                if (go_wpa_s) {
@@ -689,10 +705,20 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
        /* If a required role has been specified, handle it here */
        if (role && role != P2PS_SETUP_NEW) {
                switch (incoming) {
+               case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
+               case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
+                       /*
+                        * Peer has an active GO, so if the role allows it and
+                        * we do not have any active roles, become client.
+                        */
+                       if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s &&
+                           !cli_wpa_s)
+                               return P2PS_SETUP_CLIENT;
+
+                       /* fall through */
+
                case P2PS_SETUP_NONE:
                case P2PS_SETUP_NEW:
-               case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
-               case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
                        conncap = role;
                        goto grp_owner;
 
@@ -701,7 +727,7 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
                         * Must be a complimentary role - cannot be a client to
                         * more than one peer.
                         */
-                       if (incoming == role || client)
+                       if (incoming == role || cli_wpa_s)
                                return P2PS_SETUP_NONE;
 
                        return P2PS_SETUP_CLIENT;
@@ -727,7 +753,7 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
        switch (incoming) {
        case P2PS_SETUP_NONE:
        case P2PS_SETUP_NEW:
-               if (client)
+               if (cli_wpa_s)
                        conncap = P2PS_SETUP_GROUP_OWNER;
                else if (!owned_members)
                        conncap = P2PS_SETUP_NEW;
@@ -742,13 +768,20 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
                break;
 
        case P2PS_SETUP_GROUP_OWNER:
-               if (!client)
+               if (!cli_wpa_s)
                        conncap = P2PS_SETUP_CLIENT;
                break;
 
        case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
        case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
-               if (client)
+               /*
+                * Peer has an active GO, so if the role allows it and
+                * we do not have any active roles, become client.
+                */
+               if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s && !cli_wpa_s)
+                       return P2PS_SETUP_CLIENT;
+
+               if (cli_wpa_s)
                        conncap = P2PS_SETUP_GROUP_OWNER;
                else {
                        u8 r;
@@ -770,15 +803,12 @@ grp_owner:
            (!incoming && (conncap & P2PS_SETUP_NEW))) {
                if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group))
                        conncap &= ~P2PS_SETUP_GROUP_OWNER;
-               wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d",
-                          owner, owned_members, conncap);
 
                s = wpas_p2p_get_persistent_go(wpa_s);
-
-               if (!s && !owner && p2p_no_group_iface) {
+               if (!s && !go_wpa_s && p2p_no_group_iface) {
                        p2p_set_intended_addr(wpa_s->global->p2p,
                                              wpa_s->own_addr);
-               } else if (!s && !owner) {
+               } else if (!s && !go_wpa_s) {
                        if (wpas_p2p_add_group_interface(wpa_s,
                                                         WPA_IF_P2P_GO) < 0) {
                                wpa_printf(MSG_ERROR,
@@ -1368,6 +1398,25 @@ struct send_action_work {
 };
 
 
+static void wpas_p2p_free_send_action_work(struct wpa_supplicant *wpa_s)
+{
+       struct send_action_work *awork = wpa_s->p2p_send_action_work->ctx;
+
+       wpa_printf(MSG_DEBUG,
+                  "P2P: Free Action frame radio work @%p (freq=%u dst="
+                  MACSTR " src=" MACSTR " bssid=" MACSTR " wait_time=%u)",
+                  wpa_s->p2p_send_action_work, awork->freq,
+                  MAC2STR(awork->dst), MAC2STR(awork->src),
+                  MAC2STR(awork->bssid), awork->wait_time);
+       wpa_hexdump(MSG_DEBUG, "P2P: Freeing pending Action frame",
+                   awork->buf, awork->len);
+       os_free(awork);
+       wpa_s->p2p_send_action_work->ctx = NULL;
+       radio_work_done(wpa_s->p2p_send_action_work);
+       wpa_s->p2p_send_action_work = NULL;
+}
+
+
 static void wpas_p2p_send_action_work_timeout(void *eloop_ctx,
                                              void *timeout_ctx)
 {
@@ -1377,9 +1426,7 @@ static void wpas_p2p_send_action_work_timeout(void *eloop_ctx,
                return;
 
        wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out");
-       os_free(wpa_s->p2p_send_action_work->ctx);
-       radio_work_done(wpa_s->p2p_send_action_work);
-       wpa_s->p2p_send_action_work = NULL;
+       wpas_p2p_free_send_action_work(wpa_s);
 }
 
 
@@ -1387,11 +1434,13 @@ static void wpas_p2p_action_tx_clear(struct wpa_supplicant *wpa_s)
 {
        if (wpa_s->p2p_send_action_work) {
                struct send_action_work *awork;
+
                awork = wpa_s->p2p_send_action_work->ctx;
+               wpa_printf(MSG_DEBUG,
+                          "P2P: Clear Action TX work @%p (wait_time=%u)",
+                          wpa_s->p2p_send_action_work, awork->wait_time);
                if (awork->wait_time == 0) {
-                       os_free(awork);
-                       radio_work_done(wpa_s->p2p_send_action_work);
-                       wpa_s->p2p_send_action_work = NULL;
+                       wpas_p2p_free_send_action_work(wpa_s);
                } else {
                        /*
                         * In theory, this should not be needed, but number of
@@ -2685,6 +2734,29 @@ static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq)
 }
 
 
+static int wpas_sta_check_ecsa(struct hostapd_data *hapd,
+                              struct sta_info *sta, void *ctx)
+{
+       int *ecsa_support = ctx;
+
+       *ecsa_support &= sta->ecsa_supported;
+
+       return 0;
+}
+
+
+/* Check if all the peers support eCSA */
+static int wpas_p2p_go_clients_support_ecsa(struct wpa_supplicant *wpa_s)
+{
+       int ecsa_support = 1;
+
+       ap_for_each_sta(wpa_s->ap_iface->bss[0], wpas_sta_check_ecsa,
+                       &ecsa_support);
+
+       return ecsa_support;
+}
+
+
 /**
  * Pick the best frequency to use from all the currently used frequencies.
  */
@@ -3027,9 +3099,23 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
        wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR,
                   status, MAC2STR(peer));
        if (wpa_s->pending_invite_ssid_id == -1) {
+               struct wpa_supplicant *group_if =
+                       wpa_s->global->p2p_invite_group;
+
                if (status == P2P_SC_FAIL_UNKNOWN_GROUP)
                        wpas_remove_persistent_client(wpa_s, peer);
-               return; /* Invitation to active group */
+
+               /*
+                * Invitation to an active group. If this is successful and we
+                * are the GO, set the client wait to postpone some concurrent
+                * operations and to allow provisioning and connection to happen
+                * more quickly.
+                */
+               if (status == P2P_SC_SUCCESS &&
+                   group_if && group_if->current_ssid &&
+                   group_if->current_ssid->mode == WPAS_MODE_P2P_GO)
+                       os_get_reltime(&wpa_s->global->p2p_go_wait_client);
+               return;
        }
 
        if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
@@ -3638,11 +3724,12 @@ static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid,
 
 
 static int wpas_get_go_info(void *ctx, u8 *intended_addr,
-                           u8 *ssid, size_t *ssid_len, int *group_iface)
+                           u8 *ssid, size_t *ssid_len, int *group_iface,
+                           unsigned int *freq)
 {
        struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_supplicant *go;
        struct wpa_ssid *s;
-       u8 bssid[ETH_ALEN];
 
        /*
         * group_iface will be set to 1 only if a dedicated interface for P2P
@@ -3652,17 +3739,25 @@ static int wpas_get_go_info(void *ctx, u8 *intended_addr,
         * that the pending interface should be used.
         */
        *group_iface = 0;
-       s = wpas_p2p_group_go_ssid(wpa_s, bssid);
-       if (!s) {
+
+       if (freq)
+               *freq = 0;
+
+       go = wpas_p2p_get_go_group(wpa_s);
+       if (!go) {
                s = wpas_p2p_get_persistent_go(wpa_s);
                *group_iface = wpas_p2p_create_iface(wpa_s);
                if (s)
-                       os_memcpy(bssid, s->bssid, ETH_ALEN);
+                       os_memcpy(intended_addr, s->bssid, ETH_ALEN);
                else
                        return 0;
+       } else {
+               s = go->current_ssid;
+               os_memcpy(intended_addr, go->own_addr, ETH_ALEN);
+               if (freq)
+                       *freq = go->assoc_freq;
        }
 
-       os_memcpy(intended_addr, bssid, ETH_ALEN);
        os_memcpy(ssid, s->ssid, s->ssid_len);
        *ssid_len = s->ssid_len;
 
@@ -3750,11 +3845,12 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
                                    const u8 *persist_ssid,
                                    size_t persist_ssid_size, int response_done,
                                    int prov_start, const char *session_info,
-                                   const u8 *feat_cap, size_t feat_cap_len)
+                                   const u8 *feat_cap, size_t feat_cap_len,
+                                   unsigned int freq)
 {
        struct wpa_supplicant *wpa_s = ctx;
        u8 mac[ETH_ALEN];
-       struct wpa_ssid *persistent_go, *stale, *s;
+       struct wpa_ssid *persistent_go, *stale, *s = NULL;
        int save_config = 0;
        struct wpa_supplicant *go_wpa_s;
        char feat_cap_str[256];
@@ -3825,8 +3921,9 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
        }
 
        /* Clean up stale persistent groups with this device */
-       s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
-                                   persist_ssid_size);
+       if (persist_ssid && persist_ssid_size)
+               s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
+                                           persist_ssid_size);
 
        if (persist_ssid && s && s->mode != WPAS_MODE_P2P_GO &&
            is_zero_ether_addr(grp_mac)) {
@@ -3908,6 +4005,7 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
                go_ifname[0] = '\0';
                if (!go_wpa_s) {
                        wpa_s->global->pending_p2ps_group = 1;
+                       wpa_s->global->pending_p2ps_group_freq = freq;
 
                        if (!wpas_p2p_create_iface(wpa_s))
                                os_memcpy(go_ifname, wpa_s->ifname,
@@ -3922,7 +4020,7 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
                                        wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP,
                                        dev, adv_mac, ses_mac,
                                        grp_mac, adv_id, ses_id, 0, 0,
-                                       NULL, 0, 0, 0, NULL, NULL, 0);
+                                       NULL, 0, 0, 0, NULL, NULL, 0, 0);
                                return;
                        }
 
@@ -3930,13 +4028,13 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
                        if (response_done && persistent_go) {
                                wpas_p2p_group_add_persistent(
                                        wpa_s, persistent_go,
-                                       0, 0, 0, 0, 0, NULL,
+                                       0, 0, freq, 0, 0, NULL,
                                        persistent_go->mode ==
                                        WPAS_MODE_P2P_GO ?
                                        P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
                                        0, 0);
                        } else if (response_done) {
-                               wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+                               wpas_p2p_group_add(wpa_s, 1, freq, 0, 0);
                        }
 
                        if (passwd_id == DEV_PW_P2PS_DEFAULT) {
@@ -4025,10 +4123,13 @@ static int wpas_prov_disc_resp_cb(void *ctx)
 {
        struct wpa_supplicant *wpa_s = ctx;
        struct wpa_ssid *persistent_go;
+       unsigned int freq;
 
        if (!wpa_s->global->pending_p2ps_group)
                return 0;
 
+       freq = wpa_s->global->pending_p2ps_group_freq;
+       wpa_s->global->pending_p2ps_group_freq = 0;
        wpa_s->global->pending_p2ps_group = 0;
 
        if (wpas_p2p_get_go_group(wpa_s))
@@ -4041,7 +4142,7 @@ static int wpas_prov_disc_resp_cb(void *ctx)
                        persistent_go->mode == WPAS_MODE_P2P_GO ?
                        P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0);
        } else {
-               wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+               wpas_p2p_group_add(wpa_s, 1, freq, 0, 0);
        }
 
        return 1;
@@ -5105,6 +5206,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 
        wpa_s->global->p2p_fail_on_wps_complete = 0;
        wpa_s->global->pending_p2ps_group = 0;
+       wpa_s->global->pending_p2ps_group_freq = 0;
        wpa_s->p2ps_method_config_any = 0;
 
        if (go_intent < 0)
@@ -5526,23 +5628,41 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
        struct wpa_used_freq_data *freqs;
        unsigned int cand;
        unsigned int num, i;
+       int ignore_no_freqs = 0;
 
        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);
+       num = get_shared_radio_freqs_data(wpa_s, freqs,
+                                         wpa_s->num_multichan_concurrent);
+
+       if (wpa_s->current_ssid &&
+           wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO &&
+           wpa_s->wpa_state == WPA_COMPLETED) {
+               wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO",
+                          __func__);
+
+               /*
+                * If the frequency selection is done for an active P2P GO that
+                * is not sharing a frequency, allow to select a new frequency
+                * even if there are no unused frequencies as we are about to
+                * move the P2P GO so its frequency can be re-used.
+                */
+               for (i = 0; i < num; i++) {
+                       if (freqs[i].freq == wpa_s->current_ssid->frequency &&
+                           freqs[i].flags == 0) {
+                               ignore_no_freqs = 1;
+                               break;
+                       }
+               }
+       }
 
        /* try using the forced freq */
        if (freq) {
@@ -5563,7 +5683,8 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
                        }
                }
 
-               if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+               if (!ignore_no_freqs &&
+                   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);
@@ -5601,7 +5722,8 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
                }
        }
 
-       if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+       if (!ignore_no_freqs &&
+           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;
@@ -6164,11 +6286,14 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
        u16 config_methods;
 
        wpa_s->global->pending_p2ps_group = 0;
+       wpa_s->global->pending_p2ps_group_freq = 0;
        wpa_s->p2p_fallback_to_go_neg = 0;
        wpa_s->pending_pd_use = NORMAL_PD;
        if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) {
                p2ps_prov->conncap = p2ps_group_capability(
-                       wpa_s, P2PS_SETUP_NONE, p2ps_prov->role);
+                       wpa_s, P2PS_SETUP_NONE, p2ps_prov->role,
+                       &p2ps_prov->force_freq, &p2ps_prov->pref_freq);
+
                wpa_printf(MSG_DEBUG,
                           "P2P: %s conncap: %d - ASP parsed: %x %x %d %s",
                           __func__, p2ps_prov->conncap,
@@ -6229,7 +6354,8 @@ static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
        if (!offchannel_pending_action_tx(wpa_s))
                return;
 
-       wpas_p2p_action_tx_clear(wpa_s);
+       if (wpa_s->p2p_send_action_work)
+               wpas_p2p_free_send_action_work(wpa_s);
 
        wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
                   "operation request");
@@ -8473,14 +8599,115 @@ static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
 
 static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s)
 {
+       struct hostapd_config *conf;
+       struct p2p_go_neg_results params;
+       struct csa_settings csa_settings;
+       struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+       int old_freq = current_ssid->frequency;
+       int ret;
+
        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;
+       /*
+        * TODO: This function may not always work correctly. For example,
+        * when we have a running GO and a BSS on a DFS channel.
+        */
+       if (wpas_p2p_init_go_params(wpa_s, &params, 0, 0, 0, NULL)) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "P2P CSA: Failed to select new frequency for GO");
+               return -1;
+       }
+
+       if (current_ssid->frequency == params.freq) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "P2P CSA: Selected same frequency - not moving GO");
+               return 0;
+       }
+
+       conf = hostapd_config_defaults();
+       if (!conf) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "P2P CSA: Failed to allocate default config");
+               return -1;
+       }
+
+       current_ssid->frequency = params.freq;
+       if (wpa_supplicant_conf_ap_ht(wpa_s, current_ssid, conf)) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "P2P CSA: Failed to create new GO config");
+               ret = -1;
+               goto out;
+       }
+
+       if (conf->hw_mode != wpa_s->ap_iface->current_mode->mode) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "P2P CSA: CSA to a different band is not supported");
+               ret = -1;
+               goto out;
+       }
+
+       os_memset(&csa_settings, 0, sizeof(csa_settings));
+       csa_settings.cs_count = P2P_GO_CSA_COUNT;
+       csa_settings.block_tx = P2P_GO_CSA_BLOCK_TX;
+       csa_settings.freq_params.freq = params.freq;
+       csa_settings.freq_params.sec_channel_offset = conf->secondary_channel;
+       csa_settings.freq_params.ht_enabled = conf->ieee80211n;
+       csa_settings.freq_params.bandwidth = conf->secondary_channel ? 40 : 20;
+
+       if (conf->ieee80211ac) {
+               int freq1 = 0, freq2 = 0;
+               u8 chan, opclass;
+
+               if (ieee80211_freq_to_channel_ext(params.freq,
+                                                 conf->secondary_channel,
+                                                 conf->vht_oper_chwidth,
+                                                 &opclass, &chan) ==
+                   NUM_HOSTAPD_MODES) {
+                       wpa_printf(MSG_ERROR, "P2P CSA: Bad freq");
+                       ret = -1;
+                       goto out;
+               }
+
+               if (conf->vht_oper_centr_freq_seg0_idx)
+                       freq1 = ieee80211_chan_to_freq(
+                               NULL, opclass,
+                               conf->vht_oper_centr_freq_seg0_idx);
+
+               if (conf->vht_oper_centr_freq_seg1_idx)
+                       freq2 = ieee80211_chan_to_freq(
+                               NULL, opclass,
+                               conf->vht_oper_centr_freq_seg1_idx);
+
+               if (freq1 < 0 || freq2 < 0) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "P2P CSA: Selected invalid VHT center freqs");
+                       ret = -1;
+                       goto out;
+               }
+
+               csa_settings.freq_params.vht_enabled = conf->ieee80211ac;
+               csa_settings.freq_params.center_freq1 = freq1;
+               csa_settings.freq_params.center_freq2 = freq2;
+
+               switch (conf->vht_oper_chwidth) {
+               case VHT_CHANWIDTH_80MHZ:
+               case VHT_CHANWIDTH_80P80MHZ:
+                       csa_settings.freq_params.bandwidth = 80;
+                       break;
+               case VHT_CHANWIDTH_160MHZ:
+                       csa_settings.freq_params.bandwidth = 160;
+                       break;
+               }
+       }
+
+       ret = ap_switch_channel(wpa_s, &csa_settings);
+out:
+       current_ssid->frequency = old_freq;
+       hostapd_config_free(conf);
+       return ret;
 }
 
 
@@ -8537,6 +8764,13 @@ static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx)
 
        wpas_p2p_go_update_common_freqs(wpa_s);
 
+       /* Do not move GO in the middle of a CSA */
+       if (hostapd_csa_in_progress(wpa_s->ap_iface)) {
+               wpa_printf(MSG_DEBUG,
+                          "P2P: CSA is in progress - not moving GO");
+               return;
+       }
+
        /*
         * First, try a channel switch flow. If it is not supported or fails,
         * take down the GO and bring it up again.
@@ -8613,6 +8847,25 @@ static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s,
                           P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS &&
                           wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
                        policy_move = 1;
+               } else if ((wpa_s->conf->p2p_go_freq_change_policy ==
+                           P2P_GO_FREQ_MOVE_SCM_ECSA) &&
+                          wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
+                       if (!p2p_get_group_num_members(wpa_s->p2p_group)) {
+                               policy_move = 1;
+                       } else if ((wpa_s->drv_flags &
+                                   WPA_DRIVER_FLAGS_AP_CSA) &&
+                                  wpas_p2p_go_clients_support_ecsa(wpa_s)) {
+                               u8 chan;
+
+                               /*
+                                * We do not support CSA between bands, so move
+                                * GO only within the same band.
+                                */
+                               if (wpa_s->ap_iface->current_mode->mode ==
+                                   ieee80211_freq_to_chan(freqs[i].freq,
+                                                          &chan))
+                                       policy_move = 1;
+                       }
                }
        }
 
@@ -8647,6 +8900,16 @@ static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s,
                return;
        }
 
+       /*
+        * Do not consider moving GO if it is in the middle of a CSA. When the
+        * CSA is finished this flow should be retriggered.
+        */
+       if (hostapd_csa_in_progress(wpa_s->ap_iface)) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "P2P: Not initiating a GO frequency change - CSA is in progress");
+               return;
+       }
+
        if (invalid_freq && !wpas_p2p_disallowed_freq(wpa_s->global, freq))
                timeout = P2P_GO_FREQ_CHANGE_TIME;
        else