P2P: Fix setting of P2P Client Discoverability bit
[mech_eap.git] / src / p2p / p2p.c
index 7697256..295e8f2 100644 (file)
@@ -2,14 +2,8 @@
  * Wi-Fi Direct - P2P module
  * Copyright (c) 2009-2010, Atheros Communications
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
@@ -18,6 +12,7 @@
 #include "eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
 #include "wps/wps_i.h"
 #include "p2p_i.h"
 #include "p2p.h"
@@ -55,11 +50,38 @@ static void p2p_expire_peers(struct p2p_data *p2p)
 {
        struct p2p_device *dev, *n;
        struct os_time now;
+       size_t i;
 
        os_get_time(&now);
        dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) {
                if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec)
                        continue;
+
+               if (p2p->cfg->go_connected &&
+                   p2p->cfg->go_connected(p2p->cfg->cb_ctx,
+                                          dev->info.p2p_device_addr)) {
+                       /*
+                        * We are connected as a client to a group in which the
+                        * peer is the GO, so do not expire the peer entry.
+                        */
+                       os_get_time(&dev->last_seen);
+                       continue;
+               }
+
+               for (i = 0; i < p2p->num_groups; i++) {
+                       if (p2p_group_is_client_connected(
+                                   p2p->groups[i], dev->info.p2p_device_addr))
+                               break;
+               }
+               if (i < p2p->num_groups) {
+                       /*
+                        * The peer is connected as a client in a group where
+                        * we are the GO, so do not expire the peer entry.
+                        */
+                       os_get_time(&dev->last_seen);
+                       continue;
+               }
+
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer "
                        "entry " MACSTR, MAC2STR(dev->info.p2p_device_addr));
                dl_list_del(&dev->list);
@@ -129,14 +151,14 @@ u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr)
 }
 
 
-void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *iface_addr)
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr)
 {
        struct p2p_device *dev = NULL;
 
-       if (!iface_addr || !p2p)
+       if (!addr || !p2p)
                return;
 
-       dev = p2p_get_device_interface(p2p, iface_addr);
+       dev = p2p_get_device(p2p, addr);
        if (dev)
                dev->wps_prov_info = 0;
 }
@@ -251,6 +273,12 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
        p2p->pending_listen_usec = (timeout % 1000) * 1000;
 
        if (p2p->p2p_scan_running) {
+               if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) {
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: p2p_scan running - connect is already "
+                               "pending - skip listen");
+                       return 0;
+               }
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                        "P2P: p2p_scan running - delay start of listen state");
                p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN;
@@ -404,6 +432,9 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
 
        for (c = 0; c < info.num_clients; c++) {
                struct p2p_client_info *cli = &info.client[c];
+               if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr,
+                             ETH_ALEN) == 0)
+                       continue; /* ignore our own entry */
                dev = p2p_get_device(p2p, cli->p2p_device_addr);
                if (dev) {
                        /*
@@ -513,7 +544,7 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req,
 
 
 /**
- * p2p_add_device - Add peer entries based on scan results
+ * p2p_add_device - Add peer entries based on scan results or P2P frames
  * @p2p: P2P module context from p2p_init()
  * @addr: Source address of Beacon or Probe Response frame (may be either
  *     P2P Device Address or P2P Interface Address)
@@ -521,6 +552,7 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req,
  * @freq: Frequency on which the Beacon or Probe Response frame was received
  * @ies: IEs from the Beacon or Probe Response frame
  * @ies_len: Length of ies buffer in octets
+ * @scan_res: Whether this was based on scan results
  * Returns: 0 on success, -1 on failure
  *
  * If the scan result is for a GO, the clients in the group will also be added
@@ -529,7 +561,7 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req,
  * Info attributes.
  */
 int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
-                  const u8 *ies, size_t ies_len)
+                  const u8 *ies, size_t ies_len, int scan_res)
 {
        struct p2p_device *dev;
        struct p2p_message msg;
@@ -598,16 +630,18 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
                }
        }
 
-       if (dev->listen_freq && dev->listen_freq != freq) {
+       if (dev->listen_freq && dev->listen_freq != freq && scan_res) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                        "P2P: Update Listen frequency based on scan "
                        "results (" MACSTR " %d -> %d MHz (DS param %d)",
                        MAC2STR(dev->info.p2p_device_addr), dev->listen_freq,
                        freq, msg.ds_params ? *msg.ds_params : -1);
        }
-       dev->listen_freq = freq;
-       if (msg.group_info)
-               dev->oper_freq = freq;
+       if (scan_res) {
+               dev->listen_freq = freq;
+               if (msg.group_info)
+                       dev->oper_freq = freq;
+       }
        dev->info.level = level;
 
        p2p_copy_wps_info(dev, 0, &msg);
@@ -626,8 +660,10 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
                        break;
        }
 
-       p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info,
-                             msg.group_info_len);
+       if (scan_res) {
+               p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq,
+                                     msg.group_info, msg.group_info_len);
+       }
 
        p2p_parse_free(&msg);
 
@@ -744,6 +780,7 @@ static void p2p_search(struct p2p_data *p2p)
 {
        int freq = 0;
        enum p2p_scan_type type;
+       u16 pw_id = DEV_PW_DEFAULT;
 
        if (p2p->drv_in_listen) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still "
@@ -764,6 +801,9 @@ static void p2p_search(struct p2p_data *p2p)
                type = P2P_SCAN_SPECIFIC;
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
                        "for freq %u (GO Neg)", freq);
+
+               /* Advertise immediate availability of WPS credential */
+               pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
        } else if (p2p->invite_peer) {
                /*
                 * Only scan the known listen frequency of the peer
@@ -786,8 +826,8 @@ static void p2p_search(struct p2p_data *p2p)
        }
 
        if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
-                              p2p->num_req_dev_types, p2p->req_dev_types) < 0)
-       {
+                              p2p->num_req_dev_types, p2p->req_dev_types,
+                              p2p->find_dev_id, pw_id)) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                        "P2P: Scan request failed");
                p2p_continue_find(p2p);
@@ -886,7 +926,8 @@ static void p2p_free_req_dev_types(struct p2p_data *p2p)
 
 int p2p_find(struct p2p_data *p2p, unsigned int timeout,
             enum p2p_discovery_type type,
-            unsigned int num_req_dev_types, const u8 *req_dev_types)
+            unsigned int num_req_dev_types, const u8 *req_dev_types,
+            const u8 *dev_id)
 {
        int res;
 
@@ -908,6 +949,12 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
                p2p->num_req_dev_types = num_req_dev_types;
        }
 
+       if (dev_id) {
+               os_memcpy(p2p->find_dev_id_buf, dev_id, ETH_ALEN);
+               p2p->find_dev_id = p2p->find_dev_id_buf;
+       } else
+               p2p->find_dev_id = NULL;
+
        p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
        p2p_clear_timeout(p2p);
        p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
@@ -924,12 +971,14 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
        case P2P_FIND_PROGRESSIVE:
                res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0,
                                         p2p->num_req_dev_types,
-                                        p2p->req_dev_types);
+                                        p2p->req_dev_types, dev_id,
+                                        DEV_PW_DEFAULT);
                break;
        case P2P_FIND_ONLY_SOCIAL:
                res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0,
                                         p2p->num_req_dev_types,
-                                        p2p->req_dev_types);
+                                        p2p->req_dev_types, dev_id,
+                                        DEV_PW_DEFAULT);
                break;
        default:
                return -1;
@@ -966,7 +1015,8 @@ int p2p_other_scan_completed(struct p2p_data *p2p)
        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting pending P2P find "
                "now that previous scan was completed");
        if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type,
-                    p2p->num_req_dev_types, p2p->req_dev_types) < 0)
+                    p2p->num_req_dev_types, p2p->req_dev_types,
+                    p2p->find_dev_id) < 0)
                return 0;
        return 1;
 }
@@ -977,17 +1027,29 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find");
        eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
        p2p_clear_timeout(p2p);
+       if (p2p->state == P2P_SEARCH)
+               wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, P2P_EVENT_FIND_STOPPED);
        p2p_set_state(p2p, P2P_IDLE);
        p2p_free_req_dev_types(p2p);
        p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
        p2p->go_neg_peer = NULL;
        p2p->sd_peer = NULL;
        p2p->invite_peer = NULL;
+       p2p_stop_listen_for_freq(p2p, freq);
+}
+
+
+void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
+{
        if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip stop_listen "
                        "since we are on correct channel for response");
                return;
        }
+       if (p2p->in_listen) {
+               p2p->in_listen = 0;
+               p2p_clear_timeout(p2p);
+       }
        if (p2p->drv_in_listen) {
                /*
                 * The driver may not deliver callback to p2p_listen_end()
@@ -1110,21 +1172,22 @@ static void p2p_set_dev_persistent(struct p2p_device *dev,
 int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
                enum p2p_wps_method wps_method,
                int go_intent, const u8 *own_interface_addr,
-               unsigned int force_freq, int persistent_group)
+               unsigned int force_freq, int persistent_group,
+               const u8 *force_ssid, size_t force_ssid_len,
+               int pd_before_go_neg)
 {
        struct p2p_device *dev;
 
        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                "P2P: Request to start group negotiation - peer=" MACSTR
                "  GO Intent=%d  Intended Interface Address=" MACSTR
-               " wps_method=%d persistent_group=%d",
+               " wps_method=%d persistent_group=%d pd_before_go_neg=%d",
                MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-               wps_method, persistent_group);
+               wps_method, persistent_group, pd_before_go_neg);
 
        if (p2p_prepare_channel(p2p, force_freq) < 0)
                return -1;
 
-       p2p->ssid_set = 0;
        dev = p2p_get_device(p2p, peer_addr);
        if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -1157,10 +1220,23 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
                 */
        }
 
+       p2p->ssid_set = 0;
+       if (force_ssid) {
+               wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
+                                 force_ssid, force_ssid_len);
+               os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
+               p2p->ssid_len = force_ssid_len;
+               p2p->ssid_set = 1;
+       }
+
        dev->flags &= ~P2P_DEV_NOT_YET_READY;
        dev->flags &= ~P2P_DEV_USER_REJECTED;
        dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
        dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+       if (pd_before_go_neg)
+               dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG;
+       else
+               dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
        dev->connect_reqs = 0;
        dev->go_neg_req_sent = 0;
        dev->go_state = UNKNOWN_GO;
@@ -1208,7 +1284,8 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
 int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
                  enum p2p_wps_method wps_method,
                  int go_intent, const u8 *own_interface_addr,
-                 unsigned int force_freq, int persistent_group)
+                 unsigned int force_freq, int persistent_group,
+                 const u8 *force_ssid, size_t force_ssid_len)
 {
        struct p2p_device *dev;
 
@@ -1230,6 +1307,15 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
                return -1;
        }
 
+       p2p->ssid_set = 0;
+       if (force_ssid) {
+               wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
+                                 force_ssid, force_ssid_len);
+               os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
+               p2p->ssid_len = force_ssid_len;
+               p2p->ssid_set = 1;
+       }
+
        dev->flags &= ~P2P_DEV_NOT_YET_READY;
        dev->flags &= ~P2P_DEV_USER_REJECTED;
        dev->go_neg_req_sent = 0;
@@ -1714,16 +1800,23 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
 {
        struct wpabuf *buf;
        u8 *len;
+       int pw_id = -1;
 
        buf = wpabuf_alloc(1000);
        if (buf == NULL)
                return NULL;
 
-       p2p_build_wps_ie(p2p, buf, DEV_PW_DEFAULT, 1);
+       if (p2p->go_neg_peer) {
+               /* Advertise immediate availability of WPS credential */
+               pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
+       }
+
+       p2p_build_wps_ie(p2p, buf, pw_id, 1);
 
        /* P2P IE */
        len = p2p_buf_add_ie_hdr(buf);
-       p2p_buf_add_capability(buf, p2p->dev_capab, 0);
+       p2p_buf_add_capability(buf, p2p->dev_capab &
+                              ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
        if (p2p->ext_listen_interval)
                p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
                                              p2p->ext_listen_interval);
@@ -1767,9 +1860,9 @@ static int supp_rates_11b_only(struct ieee802_11_elems *elems)
 }
 
 
-static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr,
-                           const u8 *dst, const u8 *bssid, const u8 *ie,
-                           size_t ie_len)
+static enum p2p_probe_req_status
+p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+               const u8 *bssid, const u8 *ie, size_t ie_len)
 {
        struct ieee802_11_elems elems;
        struct wpabuf *buf;
@@ -1779,55 +1872,55 @@ static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr,
 
        if (!p2p->in_listen || !p2p->drv_in_listen) {
                /* not in Listen state - ignore Probe Request */
-               return;
+               return P2P_PREQ_NOT_LISTEN;
        }
 
        if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
            ParseFailed) {
                /* Ignore invalid Probe Request frames */
-               return;
+               return P2P_PREQ_MALFORMED;
        }
 
        if (elems.p2p == NULL) {
                /* not a P2P probe - ignore it */
-               return;
+               return P2P_PREQ_NOT_P2P;
        }
 
        if (dst && !is_broadcast_ether_addr(dst) &&
            os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
                /* Not sent to the broadcast address or our P2P Device Address
                 */
-               return;
+               return P2P_PREQ_NOT_PROCESSED;
        }
 
        if (bssid && !is_broadcast_ether_addr(bssid)) {
                /* Not sent to the Wildcard BSSID */
-               return;
+               return P2P_PREQ_NOT_PROCESSED;
        }
 
        if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN ||
            os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) !=
            0) {
                /* not using P2P Wildcard SSID - ignore */
-               return;
+               return P2P_PREQ_NOT_PROCESSED;
        }
 
        if (supp_rates_11b_only(&elems)) {
                /* Indicates support for 11b rates only */
-               return;
+               return P2P_PREQ_NOT_P2P;
        }
 
        os_memset(&msg, 0, sizeof(msg));
        if (p2p_parse_ies(ie, ie_len, &msg) < 0) {
                /* Could not parse P2P attributes */
-               return;
+               return P2P_PREQ_NOT_P2P;
        }
 
        if (msg.device_id &&
-           os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN != 0)) {
+           os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
                /* Device ID did not match */
                p2p_parse_free(&msg);
-               return;
+               return P2P_PREQ_NOT_PROCESSED;
        }
 
        /* Check Requested Device Type match */
@@ -1835,12 +1928,14 @@ static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr,
            !p2p_match_dev_type(p2p, msg.wps_attributes)) {
                /* No match with Requested Device Type */
                p2p_parse_free(&msg);
-               return;
+               return P2P_PREQ_NOT_PROCESSED;
        }
        p2p_parse_free(&msg);
 
-       if (!p2p->cfg->send_probe_resp)
-               return; /* Response generated elsewhere */
+       if (!p2p->cfg->send_probe_resp) {
+               /* Response generated elsewhere */
+               return P2P_PREQ_NOT_PROCESSED;
+       }
 
        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                "P2P: Reply to P2P Probe Request in Listen state");
@@ -1853,12 +1948,12 @@ static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr,
         */
        ies = p2p_build_probe_resp_ies(p2p);
        if (ies == NULL)
-               return;
+               return P2P_PREQ_NOT_PROCESSED;
 
        buf = wpabuf_alloc(200 + wpabuf_len(ies));
        if (buf == NULL) {
                wpabuf_free(ies);
-               return;
+               return P2P_PREQ_NOT_PROCESSED;
        }
 
        resp = NULL;
@@ -1901,15 +1996,20 @@ static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr,
        p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf);
 
        wpabuf_free(buf);
+
+       return P2P_PREQ_NOT_PROCESSED;
 }
 
 
-int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
-                    const u8 *bssid, const u8 *ie, size_t ie_len)
+enum p2p_probe_req_status
+p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+                const u8 *bssid, const u8 *ie, size_t ie_len)
 {
+       enum p2p_probe_req_status res;
+
        p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
 
-       p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
+       res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
 
        if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
            p2p->go_neg_peer &&
@@ -1920,7 +2020,7 @@ int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
                        "P2P: Found GO Negotiation peer - try to start GO "
                        "negotiation from timeout");
                eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
-               return 1;
+               return P2P_PREQ_PROCESSED;
        }
 
        if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
@@ -1932,10 +2032,10 @@ int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
                        "P2P: Found Invite peer - try to start Invite from "
                        "timeout");
                eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
-               return 1;
+               return P2P_PREQ_PROCESSED;
        }
 
-       return 0;
+       return res;
 }
 
 
@@ -2049,6 +2149,35 @@ int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end)
 }
 
 
+int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr)
+{
+       struct wpabuf *p2p_ie;
+       struct p2p_message msg;
+       int ret = -1;
+
+       p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+                                            P2P_IE_VENDOR_TYPE);
+       if (p2p_ie == NULL)
+               return -1;
+       os_memset(&msg, 0, sizeof(msg));
+       if (p2p_parse_p2p_ie(p2p_ie, &msg)) {
+               wpabuf_free(p2p_ie);
+               return -1;
+       }
+
+       if (msg.p2p_device_addr) {
+               os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+               ret = 0;
+       } else if (msg.device_id) {
+               os_memcpy(dev_addr, msg.device_id, ETH_ALEN);
+               ret = 0;
+       }
+
+       wpabuf_free(p2p_ie);
+       return ret;
+}
+
+
 static void p2p_clear_go_neg(struct p2p_data *p2p)
 {
        p2p->go_neg_peer = NULL;
@@ -2123,6 +2252,16 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
                p2p->cfg->model_number = os_strdup(cfg->model_number);
        if (cfg->serial_number)
                p2p->cfg->serial_number = os_strdup(cfg->serial_number);
+       if (cfg->pref_chan) {
+               p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan *
+                                               sizeof(struct p2p_channel));
+               if (p2p->cfg->pref_chan) {
+                       os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan,
+                                 cfg->num_pref_chan *
+                                 sizeof(struct p2p_channel));
+               } else
+                       p2p->cfg->num_pref_chan = 0;
+       }
 
        p2p->min_disc_int = 1;
        p2p->max_disc_int = 3;
@@ -2157,6 +2296,7 @@ void p2p_deinit(struct p2p_data *p2p)
        os_free(p2p->cfg->model_name);
        os_free(p2p->cfg->model_number);
        os_free(p2p->cfg->serial_number);
+       os_free(p2p->cfg->pref_chan);
        os_free(p2p->groups);
        wpabuf_free(p2p->sd_resp);
        os_free(p2p->after_scan_tx);
@@ -2168,11 +2308,7 @@ void p2p_deinit(struct p2p_data *p2p)
 void p2p_flush(struct p2p_data *p2p)
 {
        struct p2p_device *dev, *prev;
-       p2p_clear_timeout(p2p);
-       p2p_set_state(p2p, P2P_IDLE);
-       p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
-       p2p->go_neg_peer = NULL;
-       eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+       p2p_stop_find(p2p);
        dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
                              list) {
                dl_list_del(&dev->list);
@@ -2370,7 +2506,7 @@ void p2p_continue_find(struct p2p_data *p2p)
                                MACSTR " (config methods 0x%x)",
                                MAC2STR(dev->info.p2p_device_addr),
                                dev->req_config_methods);
-                       if (p2p_send_prov_disc_req(p2p, dev, 0) == 0)
+                       if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0)
                                return;
                }
        }
@@ -2439,7 +2575,7 @@ static void p2p_retry_pd(struct p2p_data *p2p)
                        MACSTR " (config methods 0x%x)",
                        MAC2STR(dev->info.p2p_device_addr),
                        dev->req_config_methods);
-               p2p_send_prov_disc_req(p2p, dev, 0);
+               p2p_send_prov_disc_req(p2p, dev, 0, 0);
                return;
        }
 }
@@ -2492,7 +2628,7 @@ static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
 int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
                         int level, const u8 *ies, size_t ies_len)
 {
-       p2p_add_device(p2p, bssid, freq, level, ies, ies_len);
+       p2p_add_device(p2p, bssid, freq, level, ies, ies_len, 1);
 
        if (p2p->go_neg_peer && p2p->state == P2P_SEARCH &&
            os_memcmp(p2p->go_neg_peer->info.p2p_device_addr, bssid, ETH_ALEN)
@@ -2524,10 +2660,13 @@ void p2p_scan_res_handled(struct p2p_data *p2p)
 }
 
 
-void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies)
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
 {
        u8 *len = p2p_buf_add_ie_hdr(ies);
-       p2p_buf_add_capability(ies, p2p->dev_capab, 0);
+       p2p_buf_add_capability(ies, p2p->dev_capab &
+                              ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+       if (dev_id)
+               p2p_buf_add_device_id(ies, dev_id);
        if (p2p->cfg->reg_class && p2p->cfg->channel)
                p2p_buf_add_listen_channel(ies, p2p->cfg->country,
                                           p2p->cfg->reg_class,
@@ -2567,11 +2706,13 @@ static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success)
        }
 
        if (success) {
-               dev->go_neg_req_sent++;
                if (dev->flags & P2P_DEV_USER_REJECTED) {
                        p2p_set_state(p2p, P2P_IDLE);
                        return;
                }
+       } else if (dev->go_neg_req_sent) {
+               /* Cancel the increment from p2p_connect_send() on failure */
+               dev->go_neg_req_sent--;
        }
 
        if (!success &&
@@ -2776,6 +2917,32 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq)
                p2p_connect_send(p2p, p2p->go_neg_peer);
                return 1;
        } else if (p2p->state == P2P_SEARCH) {
+               if (p2p->p2p_scan_running) {
+                        /*
+                         * Search is already in progress. This can happen if
+                         * an Action frame RX is reported immediately after
+                         * the end of a remain-on-channel operation and the
+                         * response frame to that is sent using an offchannel
+                         * operation while in p2p_find. Avoid an attempt to
+                         * restart a scan here.
+                         */
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan "
+                               "already in progress - do not try to start a "
+                               "new one");
+                       return 1;
+               }
+               if (p2p->pending_listen_freq) {
+                       /*
+                        * Better wait a bit if the driver is unable to start
+                        * offchannel operation for some reason. p2p_search()
+                        * will be started from internal timeout.
+                        */
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Listen "
+                               "operation did not seem to start - delay "
+                               "search phase to avoid busy loop");
+                       p2p_set_timeout(p2p, 0, 100000);
+                       return 1;
+               }
                p2p_search(p2p);
                return 1;
        }
@@ -2787,6 +2954,14 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq)
 static void p2p_timeout_connect(struct p2p_data *p2p)
 {
        p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+       if (p2p->go_neg_peer &&
+           (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Wait for GO "
+                       "Negotiation Confirm timed out - assume GO "
+                       "Negotiation failed");
+               p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+               return;
+       }
        p2p_set_state(p2p, P2P_CONNECT_LISTEN);
        p2p_listen_in_find(p2p);
 }
@@ -2825,7 +3000,7 @@ static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p)
         * state once per second to give other uses a chance to use the radio.
         */
        p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
-       p2p_set_timeout(p2p, 1, 0);
+       p2p_set_timeout(p2p, 0, 500000);
 }
 
 
@@ -3029,7 +3204,7 @@ int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr)
 }
 
 
-static const char * p2p_wps_method_text(enum p2p_wps_method method)
+const char * p2p_wps_method_text(enum p2p_wps_method method)
 {
        switch (method) {
        case WPS_NOT_READY:
@@ -3061,14 +3236,10 @@ static const char * p2p_go_state_text(enum p2p_go_state go_state)
 }
 
 
-int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
-                     char *buf, size_t buflen)
+const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p,
+                                              const u8 *addr, int next)
 {
        struct p2p_device *dev;
-       int res;
-       char *pos, *end;
-       struct os_time now;
-       char devtype[WPS_DEV_TYPE_BUFSIZE];
 
        if (addr)
                dev = p2p_get_device(p2p, addr);
@@ -3082,35 +3253,37 @@ int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
        }
 
        if (dev == NULL)
+               return NULL;
+
+       return &dev->info;
+}
+
+
+int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
+                         char *buf, size_t buflen)
+{
+       struct p2p_device *dev;
+       int res;
+       char *pos, *end;
+       struct os_time now;
+
+       if (info == NULL)
                return -1;
 
+       dev = (struct p2p_device *) (((u8 *) info) -
+                                    offsetof(struct p2p_device, info));
+
        pos = buf;
        end = buf + buflen;
 
-       res = os_snprintf(pos, end - pos, MACSTR "\n",
-                         MAC2STR(dev->info.p2p_device_addr));
-       if (res < 0 || res >= end - pos)
-               return pos - buf;
-       pos += res;
-
        os_get_time(&now);
        res = os_snprintf(pos, end - pos,
                          "age=%d\n"
                          "listen_freq=%d\n"
-                         "level=%d\n"
                          "wps_method=%s\n"
                          "interface_addr=" MACSTR "\n"
                          "member_in_go_dev=" MACSTR "\n"
                          "member_in_go_iface=" MACSTR "\n"
-                         "pri_dev_type=%s\n"
-                         "device_name=%s\n"
-                         "manufacturer=%s\n"
-                         "model_name=%s\n"
-                         "model_number=%s\n"
-                         "serial_number=%s\n"
-                         "config_methods=0x%x\n"
-                         "dev_capab=0x%x\n"
-                         "group_capab=0x%x\n"
                          "go_neg_req_sent=%d\n"
                          "go_state=%s\n"
                          "dialog_token=%u\n"
@@ -3124,21 +3297,10 @@ int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
                          "invitation_reqs=%u\n",
                          (int) (now.sec - dev->last_seen.sec),
                          dev->listen_freq,
-                         dev->info.level,
                          p2p_wps_method_text(dev->wps_method),
                          MAC2STR(dev->interface_addr),
                          MAC2STR(dev->member_in_go_dev),
                          MAC2STR(dev->member_in_go_iface),
-                         wps_dev_type_bin2str(dev->info.pri_dev_type,
-                                              devtype, sizeof(devtype)),
-                         dev->info.device_name,
-                         dev->info.manufacturer,
-                         dev->info.model_name,
-                         dev->info.model_number,
-                         dev->info.serial_number,
-                         dev->info.config_methods,
-                         dev->info.dev_capab,
-                         dev->info.group_capab,
                          dev->go_neg_req_sent,
                          p2p_go_state_text(dev->go_state),
                          dev->dialog_token,
@@ -3207,6 +3369,12 @@ int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
 }
 
 
+int p2p_peer_known(struct p2p_data *p2p, const u8 *addr)
+{
+       return p2p_get_device(p2p, addr) != NULL;
+}
+
+
 void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled)
 {
        if (enabled) {
@@ -3607,6 +3775,28 @@ int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
 }
 
 
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+                     const struct p2p_channel *pref_chan)
+{
+       struct p2p_channel *n;
+
+       if (pref_chan) {
+               n = os_malloc(num_pref_chan * sizeof(struct p2p_channel));
+               if (n == NULL)
+                       return -1;
+               os_memcpy(n, pref_chan,
+                         num_pref_chan * sizeof(struct p2p_channel));
+       } else
+               n = NULL;
+
+       os_free(p2p->cfg->pref_chan);
+       p2p->cfg->pref_chan = n;
+       p2p->cfg->num_pref_chan = num_pref_chan;
+
+       return 0;
+}
+
+
 int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
                           u8 *iface_addr)
 {