P2P NFC: Optimize join-a-group operation based on NFC information
[mech_eap.git] / src / p2p / p2p.c
index 9b7744c..64dc8bd 100644 (file)
@@ -41,21 +41,32 @@ static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx);
  * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer
  * entries will be removed
  */
-#define P2P_PEER_EXPIRATION_AGE 300
+#ifndef P2P_PEER_EXPIRATION_AGE
+#define P2P_PEER_EXPIRATION_AGE 60
+#endif /* P2P_PEER_EXPIRATION_AGE */
 
 #define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2)
 
 static void p2p_expire_peers(struct p2p_data *p2p)
 {
        struct p2p_device *dev, *n;
-       struct os_time now;
+       struct os_reltime now;
        size_t i;
 
-       os_get_time(&now);
+       os_get_reltime(&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 (dev == p2p->go_neg_peer) {
+                       /*
+                        * GO Negotiation is in progress with the peer, so
+                        * don't expire the peer entry until GO Negotiation
+                        * fails or times out.
+                        */
+                       continue;
+               }
+
                if (p2p->cfg->go_connected &&
                    p2p->cfg->go_connected(p2p->cfg->cb_ctx,
                                           dev->info.p2p_device_addr)) {
@@ -63,7 +74,7 @@ static void p2p_expire_peers(struct p2p_data *p2p)
                         * 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);
+                       os_get_reltime(&dev->last_seen);
                        continue;
                }
 
@@ -77,7 +88,7 @@ static void p2p_expire_peers(struct p2p_data *p2p)
                         * 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);
+                       os_get_reltime(&dev->last_seen);
                        continue;
                }
 
@@ -127,10 +138,6 @@ static const char * p2p_state_txt(int state)
                return "INVITE";
        case P2P_INVITE_LISTEN:
                return "INVITE_LISTEN";
-       case P2P_SEARCH_WHEN_READY:
-               return "SEARCH_WHEN_READY";
-       case P2P_CONTINUE_SEARCH_WHEN_READY:
-               return "CONTINUE_SEARCH_WHEN_READY";
        default:
                return "?";
        }
@@ -204,6 +211,7 @@ void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
        if (p2p->go_neg_peer) {
                p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
                p2p->go_neg_peer->wps_method = WPS_NOT_READY;
+               p2p->go_neg_peer->oob_pw_id = 0;
        }
        p2p->go_neg_peer = NULL;
 
@@ -377,7 +385,7 @@ static struct p2p_device * p2p_create_device(struct p2p_data *p2p,
        dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
                count++;
                if (oldest == NULL ||
-                   os_time_before(&dev->last_seen, &oldest->last_seen))
+                   os_reltime_before(&dev->last_seen, &oldest->last_seen))
                        oldest = dev;
        }
        if (count + 1 > p2p->cfg->max_peers && oldest) {
@@ -480,7 +488,7 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
 
                os_memcpy(dev->interface_addr, cli->p2p_interface_addr,
                          ETH_ALEN);
-               os_get_time(&dev->last_seen);
+               os_get_reltime(&dev->last_seen);
                os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN);
                os_memcpy(dev->member_in_go_iface, go_interface_addr,
                          ETH_ALEN);
@@ -596,14 +604,14 @@ static void p2p_copy_wps_info(struct p2p_data *p2p, struct p2p_device *dev,
  * Info attributes.
  */
 int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
-                  struct os_time *rx_time, int level, const u8 *ies,
+                  struct os_reltime *rx_time, int level, const u8 *ies,
                   size_t ies_len, int scan_res)
 {
        struct p2p_device *dev;
        struct p2p_message msg;
        const u8 *p2p_dev_addr;
        int i;
-       struct os_time time_now;
+       struct os_reltime time_now;
 
        os_memset(&msg, 0, sizeof(msg));
        if (p2p_parse_ies(ies, ies_len, &msg)) {
@@ -637,7 +645,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
        }
 
        if (rx_time == NULL) {
-               os_get_time(&time_now);
+               os_get_reltime(&time_now);
                rx_time = &time_now;
        }
 
@@ -646,7 +654,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
         * entry is newer than the one previously stored.
         */
        if (dev->last_seen.sec > 0 &&
-           os_time_before(rx_time, &dev->last_seen)) {
+           os_reltime_before(rx_time, &dev->last_seen)) {
                p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u)",
                        (unsigned int) rx_time->sec,
                        (unsigned int) rx_time->usec,
@@ -656,7 +664,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
                return -1;
        }
 
-       os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_time));
+       os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime));
 
        dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
 
@@ -884,9 +892,6 @@ static void p2p_search(struct p2p_data *p2p)
        if (res < 0) {
                p2p_dbg(p2p, "Scan request failed");
                p2p_continue_find(p2p);
-       } else if (res == 1) {
-               p2p_dbg(p2p, "Could not start p2p_scan at this point - will try again after previous scan completes");
-               p2p_set_state(p2p, P2P_CONTINUE_SEARCH_WHEN_READY);
        } else {
                p2p_dbg(p2p, "Running p2p_scan");
                p2p->p2p_scan_running = 1;
@@ -982,7 +987,7 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
        int res;
 
        p2p_dbg(p2p, "Starting find (type=%d)", type);
-       os_get_time(&p2p->find_start);
+       os_get_reltime(&p2p->find_start);
        if (p2p->p2p_scan_running) {
                p2p_dbg(p2p, "p2p_scan is already running");
        }
@@ -1041,11 +1046,9 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
                eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
                eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
                                       p2p, NULL);
-       } else if (res == 1) {
-               p2p_dbg(p2p, "Could not start p2p_scan at this point - will try again after previous scan completes");
-               res = 0;
-               p2p_set_state(p2p, P2P_SEARCH_WHEN_READY);
-               eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+       } else if (p2p->p2p_scan_running) {
+               p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running");
+               /* wait for the previous p2p_scan to complete */
        } else {
                p2p_dbg(p2p, "Failed to start p2p_scan");
                p2p_set_state(p2p, P2P_IDLE);
@@ -1056,34 +1059,12 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
 }
 
 
-int p2p_other_scan_completed(struct p2p_data *p2p)
-{
-       if (p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) {
-               p2p_set_state(p2p, P2P_SEARCH);
-               p2p_search(p2p);
-               return 1;
-       }
-       if (p2p->state != P2P_SEARCH_WHEN_READY)
-               return 0;
-       p2p_dbg(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,
-                    p2p->find_dev_id, p2p->search_delay) < 0) {
-               p2p->cfg->find_stopped(p2p->cfg->cb_ctx);
-               return 0;
-       }
-       return 1;
-}
-
-
 void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
 {
        p2p_dbg(p2p, "Stopping find");
        eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
        p2p_clear_timeout(p2p);
-       if (p2p->state == P2P_SEARCH ||
-           p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY ||
-           p2p->state == P2P_SEARCH_WHEN_READY)
+       if (p2p->state == P2P_SEARCH)
                p2p->cfg->find_stopped(p2p->cfg->cb_ctx);
        p2p_set_state(p2p, P2P_IDLE);
        p2p_free_req_dev_types(p2p);
@@ -1140,19 +1121,21 @@ void p2p_stop_find(struct p2p_data *p2p)
 
 static int p2p_prepare_channel_pref(struct p2p_data *p2p,
                                    unsigned int force_freq,
-                                   unsigned int pref_freq)
+                                   unsigned int pref_freq, int go)
 {
        u8 op_class, op_channel;
        unsigned int freq = force_freq ? force_freq : pref_freq;
 
-       p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u",
-               force_freq, pref_freq);
+       p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u go=%d",
+               force_freq, pref_freq, go);
        if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) {
                p2p_dbg(p2p, "Unsupported frequency %u MHz", freq);
                return -1;
        }
 
-       if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel)) {
+       if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel) &&
+           (go || !p2p_channels_includes(&p2p->cfg->cli_channels, op_class,
+                                         op_channel))) {
                p2p_dbg(p2p, "Frequency %u MHz (oper_class %u channel %u) not allowed for P2P",
                        freq, op_class, op_channel);
                return -1;
@@ -1178,6 +1161,9 @@ static int p2p_prepare_channel_pref(struct p2p_data *p2p,
 static void p2p_prepare_channel_best(struct p2p_data *p2p)
 {
        u8 op_class, op_channel;
+       const int op_classes_5ghz[] = { 124, 115, 0 };
+       const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
+       const int op_classes_vht[] = { 128, 0 };
 
        p2p_dbg(p2p, "Prepare channel best");
 
@@ -1209,6 +1195,21 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p)
                p2p_dbg(p2p, "Select first pref_chan entry as operating channel preference");
                p2p->op_reg_class = p2p->cfg->pref_chan[0].op_class;
                p2p->op_channel = p2p->cfg->pref_chan[0].chan;
+       } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_vht,
+                                     &p2p->op_reg_class, &p2p->op_channel) ==
+                  0) {
+               p2p_dbg(p2p, "Select possible VHT channel (op_class %u channel %u) as operating channel preference",
+                       p2p->op_reg_class, p2p->op_channel);
+       } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_ht40,
+                                     &p2p->op_reg_class, &p2p->op_channel) ==
+                  0) {
+               p2p_dbg(p2p, "Select possible HT40 channel (op_class %u channel %u) as operating channel preference",
+                       p2p->op_reg_class, p2p->op_channel);
+       } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_5ghz,
+                                     &p2p->op_reg_class, &p2p->op_channel) ==
+                  0) {
+               p2p_dbg(p2p, "Select possible 5 GHz channel (op_class %u channel %u) as operating channel preference",
+                       p2p->op_reg_class, p2p->op_channel);
        } else {
                p2p_dbg(p2p, "Select pre-configured channel as operating channel preference");
                p2p->op_reg_class = p2p->cfg->op_reg_class;
@@ -1226,6 +1227,7 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p)
  * @dev: Selected peer device
  * @force_freq: Forced frequency in MHz or 0 if not forced
  * @pref_freq: Preferred frequency in MHz or 0 if no preference
+ * @go: Whether the local end will be forced to be GO
  * Returns: 0 on success, -1 on failure (channel not supported for P2P)
  *
  * This function is used to do initial operating channel selection for GO
@@ -1234,16 +1236,25 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p)
  * is available.
  */
 int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
-                       unsigned int force_freq, unsigned int pref_freq)
+                       unsigned int force_freq, unsigned int pref_freq, int go)
 {
-       p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u",
-               force_freq, pref_freq);
+       p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u go=%d",
+               force_freq, pref_freq, go);
        if (force_freq || pref_freq) {
-               if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq) < 0)
+               if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq, go) <
+                   0)
                        return -1;
        } else {
                p2p_prepare_channel_best(p2p);
        }
+       p2p_channels_dump(p2p, "prepared channels", &p2p->channels);
+       if (go)
+               p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq);
+       else if (!force_freq)
+               p2p_channels_union(&p2p->channels, &p2p->cfg->cli_channels,
+                                  &p2p->channels);
+       p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels);
+
        p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s",
                p2p->op_reg_class, p2p->op_channel,
                force_freq ? " (forced)" : "");
@@ -1282,15 +1293,16 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
                int go_intent, const u8 *own_interface_addr,
                unsigned int force_freq, int persistent_group,
                const u8 *force_ssid, size_t force_ssid_len,
-               int pd_before_go_neg, unsigned int pref_freq)
+               int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id)
 {
        struct p2p_device *dev;
 
        p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR
                "  GO Intent=%d  Intended Interface Address=" MACSTR
-               " wps_method=%d persistent_group=%d pd_before_go_neg=%d",
+               " wps_method=%d persistent_group=%d pd_before_go_neg=%d "
+               "oob_pw_id=%u",
                MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-               wps_method, persistent_group, pd_before_go_neg);
+               wps_method, persistent_group, pd_before_go_neg, oob_pw_id);
 
        dev = p2p_get_device(p2p, peer_addr);
        if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
@@ -1299,7 +1311,8 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
                return -1;
        }
 
-       if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+       if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq,
+                               go_intent == 15) < 0)
                return -1;
 
        if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
@@ -1373,6 +1386,7 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
        }
 
        dev->wps_method = wps_method;
+       dev->oob_pw_id = oob_pw_id;
        dev->status = P2P_SC_SUCCESS;
 
        if (p2p->p2p_scan_running) {
@@ -1392,15 +1406,15 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
                  int go_intent, const u8 *own_interface_addr,
                  unsigned int force_freq, int persistent_group,
                  const u8 *force_ssid, size_t force_ssid_len,
-                 unsigned int pref_freq)
+                 unsigned int pref_freq, u16 oob_pw_id)
 {
        struct p2p_device *dev;
 
        p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR
                "  GO Intent=%d  Intended Interface Address=" MACSTR
-               " wps_method=%d  persistent_group=%d",
+               " wps_method=%d  persistent_group=%d oob_pw_id=%u",
                MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-               wps_method, persistent_group);
+               wps_method, persistent_group, oob_pw_id);
 
        dev = p2p_get_device(p2p, peer_addr);
        if (dev == NULL) {
@@ -1409,7 +1423,8 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
                return -1;
        }
 
-       if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+       if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent ==
+                               15) < 0)
                return -1;
 
        p2p->ssid_set = 0;
@@ -1430,6 +1445,7 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
        os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
 
        dev->wps_method = wps_method;
+       dev->oob_pw_id = oob_pw_id;
        dev->status = P2P_SC_SUCCESS;
 
        return 0;
@@ -1439,7 +1455,7 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
 void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
                      struct p2p_device *dev, struct p2p_message *msg)
 {
-       os_get_time(&dev->last_seen);
+       os_get_reltime(&dev->last_seen);
 
        p2p_copy_wps_info(p2p, dev, 0, msg);
 
@@ -1550,8 +1566,15 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
                }
        }
 
+       p2p_channels_dump(p2p, "own channels", &p2p->channels);
+       p2p_channels_dump(p2p, "peer channels", &peer->channels);
        p2p_channels_intersect(&p2p->channels, &peer->channels,
                               &intersection);
+       if (go) {
+               p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq);
+               p2p_channels_dump(p2p, "intersection after no-GO removal",
+                                 &intersection);
+       }
        freqs = 0;
        for (i = 0; i < intersection.reg_classes; i++) {
                struct p2p_reg_class *c = &intersection.reg_class[i];
@@ -1574,6 +1597,7 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
        p2p->ssid_set = 0;
        peer->go_neg_req_sent = 0;
        peer->wps_method = WPS_NOT_READY;
+       peer->oob_pw_id = 0;
 
        p2p_set_state(p2p, P2P_PROVISIONING);
        p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
@@ -1604,6 +1628,7 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
                                           rx_freq);
                break;
        case P2P_INVITATION_RESP:
+               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
                p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
                break;
        case P2P_PROV_DISC_REQ:
@@ -1770,7 +1795,7 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr,
        if (dev) {
                if (dev->country[0] == 0 && msg.listen_channel)
                        os_memcpy(dev->country, msg.listen_channel, 3);
-               os_get_time(&dev->last_seen);
+               os_get_reltime(&dev->last_seen);
                p2p_parse_free(&msg);
                return; /* already known */
        }
@@ -1781,7 +1806,7 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr,
                return;
        }
 
-       os_get_time(&dev->last_seen);
+       os_get_reltime(&dev->last_seen);
        dev->flags |= P2P_DEV_PROBE_REQ_ONLY;
 
        if (msg.listen_channel) {
@@ -1815,7 +1840,7 @@ struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
 
        dev = p2p_get_device(p2p, addr);
        if (dev) {
-               os_get_time(&dev->last_seen);
+               os_get_reltime(&dev->last_seen);
                return dev; /* already known */
        }
 
@@ -1910,7 +1935,11 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
                pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
        }
 
-       p2p_build_wps_ie(p2p, buf, pw_id, 1);
+       if (p2p_build_wps_ie(p2p, buf, pw_id, 1) < 0) {
+               p2p_dbg(p2p, "Failed to build WPS IE for Probe Response");
+               wpabuf_free(buf);
+               return NULL;
+       }
 
 #ifdef CONFIG_WIFI_DISPLAY
        if (p2p->wfd_ie_probe_resp)
@@ -2361,6 +2390,10 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
        p2p->go_timeout = 100;
        p2p->client_timeout = 20;
 
+       p2p_dbg(p2p, "initialized");
+       p2p_channels_dump(p2p, "channels", &p2p->cfg->channels);
+       p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels);
+
        return p2p;
 }
 
@@ -2397,6 +2430,7 @@ void p2p_deinit(struct p2p_data *p2p)
        wpabuf_free(p2p->sd_resp);
        os_free(p2p->after_scan_tx);
        p2p_remove_wps_vendor_extensions(p2p);
+       os_free(p2p->no_go_freq.range);
        os_free(p2p);
 }
 
@@ -2430,6 +2464,7 @@ int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr)
                p2p->go_neg_peer = NULL;
 
        dev->wps_method = WPS_NOT_READY;
+       dev->oob_pw_id = 0;
        dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
        dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
 
@@ -2721,10 +2756,10 @@ 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,
-                        struct os_time *rx_time, int level, const u8 *ies,
+                        struct os_reltime *rx_time, int level, const u8 *ies,
                         size_t ies_len)
 {
-       if (os_time_before(rx_time, &p2p->find_start)) {
+       if (os_reltime_before(rx_time, &p2p->find_start)) {
                /*
                 * The driver may have cached (e.g., in cfg80211 BSS table) the
                 * scan results for relatively long time. To avoid reporting
@@ -3093,7 +3128,12 @@ static void p2p_timeout_connect(struct p2p_data *p2p)
                p2p_connect_send(p2p, p2p->go_neg_peer);
                return;
        }
-
+       if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) {
+               p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)");
+               p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+               p2p_set_timeout(p2p, 0, 30000);
+               return;
+       }
        p2p_set_state(p2p, P2P_CONNECT_LISTEN);
        p2p_listen_in_find(p2p, 0);
 }
@@ -3122,13 +3162,13 @@ static void p2p_timeout_connect_listen(struct p2p_data *p2p)
 
 static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p)
 {
-       /*
-        * TODO: could remain constantly in Listen state for some time if there
-        * are no other concurrent uses for the radio. For now, go to listen
-        * 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, 0, 500000);
+
+       if (p2p->cfg->is_concurrent_session_active &&
+           p2p->cfg->is_concurrent_session_active(p2p->cfg->cb_ctx))
+               p2p_set_timeout(p2p, 0, 500000);
+       else
+               p2p_set_timeout(p2p, 0, 200000);
 }
 
 
@@ -3244,7 +3284,8 @@ static void p2p_timeout_invite_listen(struct p2p_data *p2p)
                        if (p2p->cfg->invitation_result)
                                p2p->cfg->invitation_result(
                                        p2p->cfg->cb_ctx, -1, NULL, NULL,
-                                       p2p->invite_peer->info.p2p_device_addr);
+                                       p2p->invite_peer->info.p2p_device_addr,
+                                       0);
                }
                p2p_set_state(p2p, P2P_IDLE);
        }
@@ -3319,10 +3360,6 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
        case P2P_INVITE_LISTEN:
                p2p_timeout_invite_listen(p2p);
                break;
-       case P2P_SEARCH_WHEN_READY:
-               break;
-       case P2P_CONTINUE_SEARCH_WHEN_READY:
-               break;
        }
 }
 
@@ -3355,6 +3392,8 @@ const char * p2p_wps_method_text(enum p2p_wps_method method)
                return "Keypad";
        case WPS_PBC:
                return "PBC";
+       case WPS_NFC:
+               return "NFC";
        }
 
        return "??";
@@ -3405,7 +3444,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
        struct p2p_device *dev;
        int res;
        char *pos, *end;
-       struct os_time now;
+       struct os_reltime now;
 
        if (info == NULL)
                return -1;
@@ -3416,7 +3455,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
        pos = buf;
        end = buf + buflen;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        res = os_snprintf(pos, end - pos,
                          "age=%d\n"
                          "listen_freq=%d\n"
@@ -3720,6 +3759,11 @@ static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
                return;
        }
 
+       if (p2p->cfg->presence_resp) {
+               p2p->cfg->presence_resp(p2p->cfg->cb_ctx, sa, *msg.status,
+                                       msg.noa, msg.noa_len);
+       }
+
        if (*msg.status) {
                p2p_dbg(p2p, "P2P Presence Request was rejected: status %u",
                        *msg.status);
@@ -3928,6 +3972,31 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
 }
 
 
+int p2p_set_no_go_freq(struct p2p_data *p2p,
+                      const struct wpa_freq_range_list *list)
+{
+       struct wpa_freq_range *tmp;
+
+       if (list == NULL || list->num == 0) {
+               os_free(p2p->no_go_freq.range);
+               p2p->no_go_freq.range = NULL;
+               p2p->no_go_freq.num = 0;
+               return 0;
+       }
+
+       tmp = os_calloc(list->num, sizeof(struct wpa_freq_range));
+       if (tmp == NULL)
+               return -1;
+       os_memcpy(tmp, list->range, list->num * sizeof(struct wpa_freq_range));
+       os_free(p2p->no_go_freq.range);
+       p2p->no_go_freq.range = tmp;
+       p2p->no_go_freq.num = list->num;
+       p2p_dbg(p2p, "Updated no GO chan list");
+
+       return 0;
+}
+
+
 int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
                           u8 *iface_addr)
 {
@@ -3990,10 +4059,16 @@ void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled)
 }
 
 
-void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan)
+void p2p_update_channel_list(struct p2p_data *p2p,
+                            const struct p2p_channels *chan,
+                            const struct p2p_channels *cli_chan)
 {
        p2p_dbg(p2p, "Update channel list");
        os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels));
+       p2p_channels_dump(p2p, "channels", &p2p->cfg->channels);
+       os_memcpy(&p2p->cfg->cli_channels, cli_chan,
+                 sizeof(struct p2p_channels));
+       p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels);
 }
 
 
@@ -4097,8 +4172,7 @@ int p2p_in_progress(struct p2p_data *p2p)
 {
        if (p2p == NULL)
                return 0;
-       if (p2p->state == P2P_SEARCH || p2p->state == P2P_SEARCH_WHEN_READY ||
-           p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY)
+       if (p2p->state == P2P_SEARCH)
                return 2;
        return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING;
 }
@@ -4114,13 +4188,6 @@ void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
 }
 
 
-void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay)
-{
-       if (p2p && p2p->search_delay < delay)
-               p2p->search_delay = delay;
-}
-
-
 #ifdef CONFIG_WIFI_DISPLAY
 
 static void p2p_update_wfd_ie_groups(struct p2p_data *p2p)
@@ -4130,7 +4197,7 @@ static void p2p_update_wfd_ie_groups(struct p2p_data *p2p)
 
        for (g = 0; g < p2p->num_groups; g++) {
                group = p2p->groups[g];
-               p2p_group_update_ies(group);
+               p2p_group_force_beacon_update_ies(group);
        }
 }
 
@@ -4308,3 +4375,199 @@ void p2p_err(struct p2p_data *p2p, const char *fmt, ...)
        va_end(ap);
        p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf);
 }
+
+
+#ifdef CONFIG_WPS_NFC
+
+static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p,
+                                             int client_freq)
+{
+       struct wpabuf *buf;
+       u8 op_class, channel;
+       enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP;
+
+       buf = wpabuf_alloc(1000);
+       if (buf == NULL)
+               return NULL;
+
+       op_class = p2p->cfg->reg_class;
+       channel = p2p->cfg->channel;
+
+       p2p_buf_add_capability(buf, p2p->dev_capab &
+                              ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+       p2p_buf_add_device_info(buf, p2p, NULL);
+
+       if (p2p->num_groups > 0) {
+               role = P2P_GO_IN_A_GROUP;
+               p2p_freq_to_channel(p2p_group_get_freq(p2p->groups[0]),
+                                   &op_class, &channel);
+       } else if (client_freq > 0) {
+               role = P2P_CLIENT_IN_A_GROUP;
+               p2p_freq_to_channel(client_freq, &op_class, &channel);
+       }
+
+       p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class,
+                                      channel, role);
+
+       if (p2p->num_groups > 0) {
+               /* Limit number of clients to avoid very long message */
+               p2p_buf_add_group_info(p2p->groups[0], buf, 5);
+               p2p_group_buf_add_id(p2p->groups[0], buf);
+       }
+
+       return buf;
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+                                          int client_freq)
+{
+       return p2p_build_nfc_handover(p2p, client_freq);
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+                                          int client_freq)
+{
+       return p2p_build_nfc_handover(p2p, client_freq);
+}
+
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+                                       struct p2p_nfc_params *params)
+{
+       struct p2p_message msg;
+       struct p2p_device *dev;
+       const u8 *p2p_dev_addr;
+       int freq;
+       enum p2p_role_indication role;
+
+       params->next_step = NO_ACTION;
+
+       if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len,
+                                  params->p2p_attr, params->p2p_len, &msg)) {
+               p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC");
+               p2p_parse_free(&msg);
+               return -1;
+       }
+
+       if (msg.p2p_device_addr)
+               p2p_dev_addr = msg.p2p_device_addr;
+       else if (msg.device_id)
+               p2p_dev_addr = msg.device_id;
+       else {
+               p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id");
+               p2p_parse_free(&msg);
+               return -1;
+       }
+
+       if (msg.oob_dev_password) {
+               os_memcpy(params->oob_dev_pw, msg.oob_dev_password,
+                         msg.oob_dev_password_len);
+               params->oob_dev_pw_len = msg.oob_dev_password_len;
+       }
+
+       dev = p2p_create_device(p2p, p2p_dev_addr);
+       if (dev == NULL) {
+               p2p_parse_free(&msg);
+               return -1;
+       }
+
+       params->peer = &dev->info;
+
+       os_get_reltime(&dev->last_seen);
+       dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+       p2p_copy_wps_info(p2p, dev, 0, &msg);
+
+       if (!msg.oob_go_neg_channel) {
+               p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included");
+               return -1;
+       }
+
+       if (msg.oob_go_neg_channel[3] == 0 &&
+           msg.oob_go_neg_channel[4] == 0)
+               freq = 0;
+       else
+               freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3],
+                                          msg.oob_go_neg_channel[4]);
+       if (freq < 0) {
+               p2p_dbg(p2p, "Unknown peer OOB GO Neg channel");
+               return -1;
+       }
+       role = msg.oob_go_neg_channel[5];
+
+       if (role == P2P_GO_IN_A_GROUP) {
+               p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq);
+               params->go_freq = freq;
+       } else
+               p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq);
+       dev->oob_go_neg_freq = freq;
+
+       if (!params->sel && role != P2P_GO_IN_A_GROUP) {
+               freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+                                          p2p->cfg->channel);
+               if (freq < 0) {
+                       p2p_dbg(p2p, "Own listen channel not known");
+                       return -1;
+               }
+               p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq);
+               dev->oob_go_neg_freq = freq;
+       }
+
+       if (msg.group_id) {
+               os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN);
+               params->go_ssid_len = msg.group_id_len - ETH_ALEN;
+               os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN,
+                         params->go_ssid_len);
+       }
+
+       p2p_parse_free(&msg);
+
+       if (dev->flags & P2P_DEV_USER_REJECTED) {
+               p2p_dbg(p2p, "Do not report rejected device");
+               return 0;
+       }
+
+       if (!(dev->flags & P2P_DEV_REPORTED)) {
+               p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info,
+                                   !(dev->flags & P2P_DEV_REPORTED_ONCE));
+               dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+       }
+
+       if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0)
+               params->next_step = BOTH_GO;
+       else if (role == P2P_GO_IN_A_GROUP)
+               params->next_step = JOIN_GROUP;
+       else if (role == P2P_CLIENT_IN_A_GROUP) {
+               dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
+               params->next_step = PEER_CLIENT;
+       } else if (p2p->num_groups > 0)
+               params->next_step = AUTH_JOIN;
+       else if (params->sel)
+               params->next_step = INIT_GO_NEG;
+       else
+               params->next_step = RESP_GO_NEG;
+
+       return 0;
+}
+
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+                                     int go_intent,
+                                     const u8 *own_interface_addr)
+{
+
+       p2p->authorized_oob_dev_pw_id = dev_pw_id;
+       if (dev_pw_id == 0) {
+               p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover");
+               return;
+       }
+
+       p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover",
+               dev_pw_id);
+
+       p2p->go_intent = go_intent;
+       os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+}
+
+#endif /* CONFIG_WPS_NFC */