P2P: Add support for automatic channel selection at GO
[mech_eap.git] / wpa_supplicant / p2p_supplicant.c
index cecfb1a..2f6ae89 100644 (file)
@@ -46,6 +46,8 @@ static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
                         const u8 *dev_addr, enum p2p_wps_method wps_method);
 static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
 
 
 static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
@@ -65,7 +67,7 @@ static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
                                         bss->freq, bss->level,
                                         (const u8 *) (bss + 1),
                                         bss->ie_len) > 0)
-                       return;
+                       break;
        }
 
        p2p_scan_res_handled(wpa_s->global->p2p);
@@ -175,6 +177,9 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
 {
        struct wpa_ssid *ssid;
        char *gtype;
+       const char *reason;
+
+       eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
 
        ssid = wpa_s->current_ssid;
        if (ssid == NULL) {
@@ -208,8 +213,22 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
                        P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
                        wpa_s->ifname, wpa_s->cross_connect_uplink);
        }
-       wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s",
-               wpa_s->ifname, gtype);
+       switch (wpa_s->removal_reason) {
+       case P2P_GROUP_REMOVAL_REQUESTED:
+               reason = " reason=REQUESTED";
+               break;
+       case P2P_GROUP_REMOVAL_IDLE_TIMEOUT:
+               reason = " reason=IDLE";
+               break;
+       case P2P_GROUP_REMOVAL_UNAVAILABLE:
+               reason = " reason=UNAVAILABLE";
+               break;
+       default:
+               reason = "";
+               break;
+       }
+       wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s",
+               wpa_s->ifname, gtype, reason);
        if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
                struct wpa_global *global;
                char *ifname;
@@ -451,15 +470,17 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
                        MAC2STR(go_dev_addr),
                        persistent ? " [PERSISTENT]" : "");
                wpas_p2p_cross_connect_setup(wpa_s);
+               wpas_p2p_set_group_idle_timeout(wpa_s);
        } else {
                wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
                        "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
                        "go_dev_addr=" MACSTR "%s",
-                       wpa_s->ifname, ssid_txt, ssid->frequency,
+                       wpa_s->ifname, ssid_txt, ssid ? ssid->frequency : 0,
                        ssid && ssid->passphrase ? ssid->passphrase : "",
                        MAC2STR(go_dev_addr),
                        persistent ? " [PERSISTENT]" : "");
                wpas_p2p_cross_connect_setup(wpa_s);
+               wpas_p2p_set_group_idle_timeout(wpa_s);
        }
 
        if (persistent)
@@ -468,6 +489,35 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
 }
 
 
+static struct wpa_supplicant *
+wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src)
+{
+       struct wpa_supplicant *iface;
+
+       if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0)
+               return wpa_s;
+
+       /*
+        * Try to find a group interface that matches with the source address.
+        */
+       iface = wpa_s->global->ifaces;
+       while (iface) {
+               if (os_memcmp(wpa_s->pending_action_src,
+                             iface->own_addr, ETH_ALEN) == 0)
+                       break;
+               iface = iface->next;
+       }
+       if (iface) {
+               wpa_printf(MSG_DEBUG, "P2P: Use group interface %s "
+                          "instead of interface %s for Action TX",
+                          iface->ifname, wpa_s->ifname);
+               return iface;
+       }
+
+       return wpa_s;
+}
+
+
 static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
 {
        struct wpa_supplicant *wpa_s = eloop_ctx;
@@ -484,12 +534,26 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
        if (wpa_s->pending_action_tx == NULL)
                return;
 
+       /*
+        * This call is likely going to be on the P2P device instance if the
+        * driver uses a separate interface for that purpose. However, some
+        * Action frames are actually sent within a P2P Group and when that is
+        * the case, we need to follow power saving (e.g., GO buffering the
+        * frame for a client in PS mode or a client following the advertised
+        * NoA from its GO). To make that easier for the driver, select the
+        * correct group interface here.
+        */
+       iface = wpas_get_tx_interface(wpa_s, wpa_s->pending_action_src);
+
        if (wpa_s->off_channel_freq != wpa_s->pending_action_freq &&
-           wpa_s->pending_action_freq != 0) {
+           wpa_s->pending_action_freq != 0 &&
+           wpa_s->pending_action_freq != iface->assoc_freq) {
                wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX "
-                          "waiting for another freq=%u (off_channel_freq=%u)",
+                          "waiting for another freq=%u (off_channel_freq=%u "
+                          "assoc_freq=%u)",
                           wpa_s->pending_action_freq,
-                          wpa_s->off_channel_freq);
+                          wpa_s->off_channel_freq,
+                          iface->assoc_freq);
                if (without_roc && wpa_s->off_channel_freq == 0) {
                        /*
                         * We may get here if wpas_send_action() found us to be
@@ -505,44 +569,15 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
                                           "driver to remain on channel (%u "
                                           "MHz) for Action Frame TX",
                                           wpa_s->pending_action_freq);
-                       } else
+                       } else {
+                               wpa_s->off_channel_freq = 0;
                                wpa_s->roc_waiting_drv_freq =
                                        wpa_s->pending_action_freq;
+                       }
                }
                return;
        }
 
-       /*
-        * This call is likely going to be on the P2P device instance if the
-        * driver uses a separate interface for that purpose. However, some
-        * Action frames are actually sent within a P2P Group and when that is
-        * the case, we need to follow power saving (e.g., GO buffering the
-        * frame for a client in PS mode or a client following the advertised
-        * NoA from its GO). To make that easier for the driver, select the
-        * correct group interface here.
-        */
-       if (os_memcmp(wpa_s->pending_action_src, wpa_s->own_addr, ETH_ALEN) !=
-           0) {
-               /*
-                * Try to find a group interface that matches with the source
-                * address.
-                */
-               iface = wpa_s->global->ifaces;
-               while (iface) {
-                       if (os_memcmp(wpa_s->pending_action_src,
-                                     iface->own_addr, ETH_ALEN) == 0)
-                               break;
-                       iface = iface->next;
-               }
-               if (iface) {
-                       wpa_printf(MSG_DEBUG, "P2P: Use group interface %s "
-                                  "instead of interface %s for Action TX",
-                                  iface->ifname, wpa_s->ifname);
-               } else
-                       iface = wpa_s;
-       } else
-               iface = wpa_s;
-
        wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to "
                   MACSTR " using interface %s",
                   MAC2STR(wpa_s->pending_action_dst), iface->ifname);
@@ -562,13 +597,15 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
                wpas_send_action_tx_status(
                        wpa_s, wpa_s->pending_action_dst,
                        wpabuf_head(wpa_s->pending_action_tx),
-                       wpabuf_len(wpa_s->pending_action_tx), 0);
+                       wpabuf_len(wpa_s->pending_action_tx),
+                       P2P_SEND_ACTION_FAILED);
        }
 }
 
 
 void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
-                               const u8 *data, size_t data_len, int ack)
+                               const u8 *data, size_t data_len,
+                               enum p2p_send_action_result result)
 {
        if (wpa_s->global->p2p_disabled)
                return;
@@ -592,7 +629,7 @@ void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
                           wpa_s->pending_action_dst,
                           wpa_s->pending_action_src,
                           wpa_s->pending_action_bssid,
-                          ack);
+                          result);
 
        if (wpa_s->pending_pd_before_join &&
            (os_memcmp(wpa_s->pending_action_dst, wpa_s->pending_join_dev_addr,
@@ -635,6 +672,16 @@ static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
        os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
        wpa_s->pending_action_freq = freq;
 
+       if (freq) {
+               struct wpa_supplicant *tx_iface;
+               tx_iface = wpas_get_tx_interface(wpa_s, src);
+               if (tx_iface->assoc_freq == freq) {
+                       wpa_printf(MSG_DEBUG, "P2P: Already on requested "
+                                  "channel (TX interface operating channel)");
+                       freq = 0;
+               }
+       }
+
        if (wpa_s->off_channel_freq == freq || freq == 0) {
                wpa_printf(MSG_DEBUG, "P2P: Already on requested channel; "
                           "send Action frame immediately");
@@ -664,6 +711,7 @@ static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
                           "Frame TX", freq);
                return -1;
        }
+       wpa_s->off_channel_freq = 0;
        wpa_s->roc_waiting_drv_freq = freq;
 
        return 0;
@@ -676,7 +724,7 @@ static void wpas_send_action_done(void *ctx)
        wpa_printf(MSG_DEBUG, "P2P: Action frame sequence done notification");
        wpabuf_free(wpa_s->pending_action_tx);
        wpa_s->pending_action_tx = NULL;
-       if (wpa_s->off_channel_freq) {
+       if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
                wpa_drv_cancel_remain_on_channel(wpa_s);
                wpa_s->off_channel_freq = 0;
                wpa_s->roc_waiting_drv_freq = 0;
@@ -741,6 +789,7 @@ static void p2p_go_configured(void *ctx, void *data)
                                wpa_s->parent, ssid,
                                wpa_s->parent->own_addr);
                wpas_p2p_cross_connect_setup(wpa_s);
+               wpas_p2p_set_group_idle_timeout(wpa_s);
                return;
        }
 
@@ -820,6 +869,9 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
        C(device_type);
        C(config_methods);
 #undef C
+
+       d->p2p_group_idle = s->p2p_group_idle;
+       d->p2p_intra_bss = s->p2p_intra_bss;
 }
 
 
@@ -946,7 +998,7 @@ void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
 {
        struct wpa_supplicant *wpa_s = ctx;
 
-       if (wpa_s->off_channel_freq) {
+       if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
                wpa_drv_cancel_remain_on_channel(wpa_s);
                wpa_s->off_channel_freq = 0;
                wpa_s->roc_waiting_drv_freq = 0;
@@ -1049,6 +1101,7 @@ static int wpas_start_listen(void *ctx, unsigned int freq,
                wpa_s->pending_listen_freq = 0;
                return -1;
        }
+       wpa_s->off_channel_freq = 0;
        wpa_s->roc_waiting_drv_freq = freq;
 
        return 0;
@@ -1283,13 +1336,6 @@ static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
                return;
        }
 
-       version = query[0];
-       str = os_malloc(query_len);
-       if (str == NULL)
-               return;
-       os_memcpy(str, query + 1, query_len - 1);
-       str[query_len - 1] = '\0';
-
        if (wpabuf_tailroom(resp) < 5)
                return;
 
@@ -1298,6 +1344,13 @@ static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
        wpabuf_put_u8(resp, P2P_SERV_UPNP);
        wpabuf_put_u8(resp, srv_trans_id);
 
+       version = query[0];
+       str = os_malloc(query_len);
+       if (str == NULL)
+               return;
+       os_memcpy(str, query + 1, query_len - 1);
+       str[query_len - 1] = '\0';
+
        dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
                         struct p2p_srv_upnp, list) {
                if (version != usrv->version)
@@ -2150,22 +2203,28 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
                os_get_random((u8 *) &r, sizeof(r));
                p2p.channel = 1 + (r % 3) * 5;
        }
+       wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d", p2p.channel);
 
        if (wpa_s->conf->p2p_oper_reg_class &&
            wpa_s->conf->p2p_oper_channel) {
                p2p.op_reg_class = wpa_s->conf->p2p_oper_reg_class;
                p2p.op_channel = wpa_s->conf->p2p_oper_channel;
+               p2p.cfg_op_channel = 1;
+               wpa_printf(MSG_DEBUG, "P2P: Configured operating channel: "
+                          "%d:%d", p2p.op_reg_class, p2p.op_channel);
+
        } else {
                p2p.op_reg_class = 81;
                /*
-                * For initial tests, pick the operation channel randomly.
-                * TODO: Use scan results (etc.) to select the best channel.
+                * Use random operation channel from (1, 6, 11) if no other
+                * preference is indicated.
                 */
-               p2p.op_channel = 1 + r % 11;
+               os_get_random((u8 *) &r, sizeof(r));
+               p2p.op_channel = 1 + (r % 3) * 5;
+               p2p.cfg_op_channel = 0;
+               wpa_printf(MSG_DEBUG, "P2P: Random operating channel: "
+                          "%d:%d", p2p.op_reg_class, p2p.op_channel);
        }
-       wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d  "
-                  "Own preferred operation channel: %d",
-                  p2p.channel, p2p.op_channel);
        if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
                os_memcpy(p2p.country, wpa_s->conf->country, 2);
                p2p.country[2] = 0x04;
@@ -2202,7 +2261,7 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
        p2p.concurrent_operations = !!(wpa_s->drv_flags &
                                       WPA_DRIVER_FLAGS_P2P_CONCURRENT);
 
-       p2p.max_peers = wpa_s->max_stations ? wpa_s->max_stations : 100;
+       p2p.max_peers = 100;
 
        if (wpa_s->conf->p2p_ssid_postfix) {
                p2p.ssid_postfix_len =
@@ -2242,6 +2301,7 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
        eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
        wpa_s->p2p_long_listen = 0;
        eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+       eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
        wpas_p2p_remove_pending_group_interface(wpa_s);
 
        /* TODO: remove group interface from the driver if this wpa_s instance
@@ -2780,6 +2840,7 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
                while (wpa_s) {
                        prev = wpa_s;
                        wpa_s = wpa_s->next;
+                       prev->removal_reason = P2P_GROUP_REMOVAL_REQUESTED;
                        wpas_p2p_group_delete(prev);
                }
                return 0;
@@ -2793,6 +2854,7 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
        if (wpa_s == NULL)
                return -1;
 
+       wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED;
        wpas_p2p_group_delete(wpa_s);
 
        return 0;
@@ -2808,16 +2870,48 @@ static void wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
 
        os_memset(params, 0, sizeof(*params));
        params->role_go = 1;
-       params->freq = 2412;
-       if (freq)
+       if (freq) {
+               wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced "
+                          "frequency %d MHz", 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)
+       else if (wpa_s->conf->p2p_oper_reg_class == 81 &&
+                  wpa_s->conf->p2p_oper_channel >= 1 &&
+                  wpa_s->conf->p2p_oper_channel <= 11) {
                params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
-       else if (wpa_s->conf->p2p_oper_reg_class == 115 ||
-                wpa_s->conf->p2p_oper_reg_class == 118)
+               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 == 118) {
                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(wpa_s->global->p2p,
+                                     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(wpa_s->global->p2p,
+                                     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(wpa_s->global->p2p,
+                                     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 {
+               params->freq = 2412;
+               wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference "
+                          "known)", params->freq);
+       }
+
        if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
            wpa_s->assoc_freq && !freq) {
                wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are "
@@ -2870,6 +2964,46 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
                       int freq)
 {
        struct p2p_go_neg_results params;
+       unsigned int r;
+
+       if (freq == 2) {
+               wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
+                          "band");
+               if (wpa_s->best_24_freq > 0 &&
+                   p2p_supported_freq(wpa_s->global->p2p,
+                                      wpa_s->best_24_freq)) {
+                       freq = wpa_s->best_24_freq;
+                       wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band "
+                                  "channel: %d MHz", freq);
+               } else {
+                       os_get_random((u8 *) &r, sizeof(r));
+                       freq = 2412 + (r % 3) * 25;
+                       wpa_printf(MSG_DEBUG, "P2P: Use random 2.4 GHz band "
+                                  "channel: %d MHz", freq);
+               }
+       }
+
+       if (freq == 5) {
+               wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz "
+                          "band");
+               if (wpa_s->best_5_freq > 0 &&
+                   p2p_supported_freq(wpa_s->global->p2p,
+                                      wpa_s->best_5_freq)) {
+                       freq = wpa_s->best_5_freq;
+                       wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band "
+                                  "channel: %d MHz", freq);
+               } else {
+                       os_get_random((u8 *) &r, sizeof(r));
+                       freq = 5180 + (r % 4) * 20;
+                       if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+                               wpa_printf(MSG_DEBUG, "P2P: Could not select "
+                                          "5 GHz channel for P2P group");
+                               return -1;
+                       }
+                       wpa_printf(MSG_DEBUG, "P2P: Use random 5 GHz band "
+                                  "channel: %d MHz", freq);
+               }
+       }
 
        if (freq > 0 && !p2p_supported_freq(wpa_s->global->p2p, freq)) {
                wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO "
@@ -2999,6 +3133,19 @@ static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies,
 }
 
 
+static void wpas_p2p_idle_update(void *ctx, int idle)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       if (!wpa_s->ap_iface)
+               return;
+       wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not ");
+       if (idle)
+               wpas_p2p_set_group_idle_timeout(wpa_s);
+       else
+               eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+}
+
+
 struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
                                       int persistent_group,
                                       int group_formation)
@@ -3012,8 +3159,14 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
 
        cfg->persistent_group = persistent_group;
        os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN);
+       if (wpa_s->max_stations &&
+           wpa_s->max_stations < wpa_s->conf->max_num_sta)
+               cfg->max_clients = wpa_s->max_stations;
+       else
+               cfg->max_clients = wpa_s->conf->max_num_sta;
        cfg->cb_ctx = wpa_s;
        cfg->ie_update = wpas_p2p_ie_update;
+       cfg->idle_update = wpas_p2p_idle_update;
 
        group = p2p_group_init(wpa_s->global->p2p, cfg);
        if (group == NULL)
@@ -3322,6 +3475,9 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
                                               ssid->ssid_len);
        os_memcpy(wpa_s->go_dev_addr, go_dev_addr, ETH_ALEN);
 
+       if (wpa_s->global->p2p_group_formation == wpa_s)
+               wpa_s->global->p2p_group_formation = NULL;
+
        if (ssid->passphrase == NULL && ssid->psk_set) {
                char psk[65];
                wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
@@ -3368,6 +3524,39 @@ int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
 }
 
 
+static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+
+       if (wpa_s->conf->p2p_group_idle == 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - "
+                          "disabled");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate "
+                  "group");
+       wpa_s->removal_reason = P2P_GROUP_REMOVAL_IDLE_TIMEOUT;
+       wpas_p2p_group_delete(wpa_s);
+}
+
+
+static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
+{
+       eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+       if (wpa_s->conf->p2p_group_idle == 0)
+               return;
+
+       if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
+               return;
+
+       wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds",
+                  wpa_s->conf->p2p_group_idle);
+       eloop_register_timeout(wpa_s->conf->p2p_group_idle, 0,
+                              wpas_p2p_group_idle_timeout, wpa_s, NULL);
+}
+
+
 void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
                           u16 reason_code, const u8 *ie, size_t ie_len)
 {
@@ -3550,12 +3739,16 @@ void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
                wpas_p2p_disable_cross_connect(wpa_s);
        else
                wpas_p2p_enable_cross_connect(wpa_s);
+       if (!wpa_s->ap_iface)
+               eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
 }
 
 
 void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
 {
        wpas_p2p_disable_cross_connect(wpa_s);
+       if (!wpa_s->ap_iface)
+               wpas_p2p_set_group_idle_timeout(wpa_s);
 }
 
 
@@ -3626,3 +3819,61 @@ void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
 
        p2p_update_channel_list(wpa_s->global->p2p, &chan);
 }
+
+
+int wpas_p2p_cancel(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_global *global = wpa_s->global;
+       int found = 0;
+
+       wpa_printf(MSG_DEBUG, "P2P: Request to cancel group formation");
+
+       if (wpa_s->pending_interface_name[0] &&
+           !is_zero_ether_addr(wpa_s->pending_interface_addr))
+               found = 1;
+
+       wpas_p2p_stop_find(wpa_s);
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               if (wpa_s == global->p2p_group_formation &&
+                   (wpa_s->p2p_in_provisioning ||
+                    wpa_s->parent->pending_interface_type ==
+                    WPA_IF_P2P_CLIENT)) {
+                       wpa_printf(MSG_DEBUG, "P2P: Interface %s in group "
+                                  "formation found - cancelling",
+                                  wpa_s->ifname);
+                       found = 1;
+                       wpas_p2p_group_delete(wpa_s);
+                       break;
+               }
+       }
+
+       if (!found) {
+               wpa_printf(MSG_DEBUG, "P2P: No ongoing group formation found");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
+               return;
+
+       wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not "
+                  "being available anymore");
+       wpa_s->removal_reason = P2P_GROUP_REMOVAL_UNAVAILABLE;
+       wpas_p2p_group_delete(wpa_s);
+}
+
+
+void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+                                  int freq_24, int freq_5, int freq_overall)
+{
+       struct p2p_data *p2p = wpa_s->global->p2p;
+       if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT))
+               return;
+       p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall);
+}