wpa_supplicant: Initial Revision 0.8.X

Based on:
commit 0725cc7b7efc434910e89865c42eda7ce61bbf08
Author: Jouni Malinen <j@w1.fi>
Date:   Thu Apr 21 20:41:01 2011 +0300

    Enable CONFIG_DRIVER_NL80211=y in the default configuration

    nl80211 should be preferred over WEXT with any recent Linux
    kernel version.

Change-Id: I26aec5afbbd4f4a1f5fd900912545b6f5050de64
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/p2p/Makefile b/src/p2p/Makefile
new file mode 100644
index 0000000..cffba62
--- /dev/null
+++ b/src/p2p/Makefile
@@ -0,0 +1,9 @@
+all:
+	@echo Nothing to be made.
+
+clean:
+	for d in $(SUBDIRS); do make -C $$d clean; done
+	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
new file mode 100644
index 0000000..653609e
--- /dev/null
+++ b/src/p2p/p2p.c
@@ -0,0 +1,3490 @@
+/*
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx);
+static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev);
+static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
+				     const u8 *sa, const u8 *data, size_t len,
+				     int rx_freq);
+static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
+				      const u8 *sa, const u8 *data,
+				      size_t len);
+static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx);
+static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+
+
+/*
+ * p2p_scan recovery timeout
+ *
+ * Many drivers are using 30 second timeout on scan results. Allow a bit larger
+ * timeout for this to avoid hitting P2P timeout unnecessarily.
+ */
+#define P2P_SCAN_TIMEOUT 35
+
+/**
+ * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer
+ * entries will be removed
+ */
+#define P2P_PEER_EXPIRATION_AGE 300
+
+#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;
+
+	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;
+		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);
+		p2p_device_free(p2p, dev);
+	}
+}
+
+
+static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+	p2p_expire_peers(p2p);
+	eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
+			       p2p_expiration_timeout, p2p, NULL);
+}
+
+
+static const char * p2p_state_txt(int state)
+{
+	switch (state) {
+	case P2P_IDLE:
+		return "IDLE";
+	case P2P_SEARCH:
+		return "SEARCH";
+	case P2P_CONNECT:
+		return "CONNECT";
+	case P2P_CONNECT_LISTEN:
+		return "CONNECT_LISTEN";
+	case P2P_GO_NEG:
+		return "GO_NEG";
+	case P2P_LISTEN_ONLY:
+		return "LISTEN_ONLY";
+	case P2P_WAIT_PEER_CONNECT:
+		return "WAIT_PEER_CONNECT";
+	case P2P_WAIT_PEER_IDLE:
+		return "WAIT_PEER_IDLE";
+	case P2P_SD_DURING_FIND:
+		return "SD_DURING_FIND";
+	case P2P_PROVISIONING:
+		return "PROVISIONING";
+	case P2P_PD_DURING_FIND:
+		return "PD_DURING_FIND";
+	case P2P_INVITE:
+		return "INVITE";
+	case P2P_INVITE_LISTEN:
+		return "INVITE_LISTEN";
+	default:
+		return "?";
+	}
+}
+
+
+void p2p_set_state(struct p2p_data *p2p, int new_state)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: State %s -> %s",
+		p2p_state_txt(p2p->state), p2p_state_txt(new_state));
+	p2p->state = new_state;
+}
+
+
+void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Set timeout (state=%s): %u.%06u sec",
+		p2p_state_txt(p2p->state), sec, usec);
+	eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
+	eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL);
+}
+
+
+void p2p_clear_timeout(struct p2p_data *p2p)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear timeout (state=%s)",
+		p2p_state_txt(p2p->state));
+	eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
+}
+
+
+void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
+		       int status)
+{
+	struct p2p_go_neg_results res;
+	p2p_clear_timeout(p2p);
+	p2p_set_state(p2p, P2P_IDLE);
+	p2p->go_neg_peer = NULL;
+
+	os_memset(&res, 0, sizeof(res));
+	res.status = status;
+	if (peer) {
+		os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr,
+			  ETH_ALEN);
+		os_memcpy(res.peer_interface_addr, peer->intended_addr,
+			  ETH_ALEN);
+	}
+	p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+}
+
+
+static void p2p_listen_in_find(struct p2p_data *p2p)
+{
+	unsigned int r, tu;
+	int freq;
+	struct wpabuf *ies;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Starting short listen state (state=%s)",
+		p2p_state_txt(p2p->state));
+
+	freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class,
+				   p2p->cfg->channel);
+	if (freq < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unknown regulatory class/channel");
+		return;
+	}
+
+	os_get_random((u8 *) &r, sizeof(r));
+	tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) +
+	      p2p->min_disc_int) * 100;
+
+	p2p->pending_listen_freq = freq;
+	p2p->pending_listen_sec = 0;
+	p2p->pending_listen_usec = 1024 * tu;
+
+	ies = p2p_build_probe_resp_ies(p2p);
+	if (ies == NULL)
+		return;
+
+	if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000,
+		    ies) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to start listen mode");
+		p2p->pending_listen_freq = 0;
+	}
+	wpabuf_free(ies);
+}
+
+
+int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
+{
+	int freq;
+	struct wpabuf *ies;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Going to listen(only) state");
+
+	freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class,
+				   p2p->cfg->channel);
+	if (freq < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unknown regulatory class/channel");
+		return -1;
+	}
+
+	p2p->pending_listen_freq = freq;
+	p2p->pending_listen_sec = timeout / 1000;
+	p2p->pending_listen_usec = (timeout % 1000) * 1000;
+
+	if (p2p->p2p_scan_running) {
+		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;
+		return 0;
+	}
+
+	ies = p2p_build_probe_resp_ies(p2p);
+	if (ies == NULL)
+		return -1;
+
+	if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to start listen mode");
+		p2p->pending_listen_freq = 0;
+		wpabuf_free(ies);
+		return -1;
+	}
+	wpabuf_free(ies);
+
+	p2p_set_state(p2p, P2P_LISTEN_ONLY);
+
+	return 0;
+}
+
+
+static void p2p_device_clear_reported(struct p2p_data *p2p)
+{
+	struct p2p_device *dev;
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list)
+		dev->flags &= ~P2P_DEV_REPORTED;
+}
+
+
+/**
+ * p2p_get_device - Fetch a peer entry
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: Pointer to the device entry or %NULL if not found
+ */
+struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr)
+{
+	struct p2p_device *dev;
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (os_memcmp(dev->info.p2p_device_addr, addr, ETH_ALEN) == 0)
+			return dev;
+	}
+	return NULL;
+}
+
+
+/**
+ * p2p_get_device_interface - Fetch a peer entry based on P2P Interface Address
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Interface Address of the peer
+ * Returns: Pointer to the device entry or %NULL if not found
+ */
+struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
+					     const u8 *addr)
+{
+	struct p2p_device *dev;
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (os_memcmp(dev->interface_addr, addr, ETH_ALEN) == 0)
+			return dev;
+	}
+	return NULL;
+}
+
+
+/**
+ * p2p_create_device - Create a peer entry
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer
+ * Returns: Pointer to the device entry or %NULL on failure
+ *
+ * If there is already an entry for the peer, it will be returned instead of
+ * creating a new one.
+ */
+static struct p2p_device * p2p_create_device(struct p2p_data *p2p,
+					     const u8 *addr)
+{
+	struct p2p_device *dev, *oldest = NULL;
+	size_t count = 0;
+
+	dev = p2p_get_device(p2p, addr);
+	if (dev)
+		return dev;
+
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		count++;
+		if (oldest == NULL ||
+		    os_time_before(&dev->last_seen, &oldest->last_seen))
+			oldest = dev;
+	}
+	if (count + 1 > p2p->cfg->max_peers && oldest) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Remove oldest peer entry to make room for a new "
+			"peer");
+		dl_list_del(&oldest->list);
+		p2p_device_free(p2p, oldest);
+	}
+
+	dev = os_zalloc(sizeof(*dev));
+	if (dev == NULL)
+		return NULL;
+	dl_list_add(&p2p->devices, &dev->list);
+	os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN);
+
+	return dev;
+}
+
+
+static void p2p_copy_client_info(struct p2p_device *dev,
+				 struct p2p_client_info *cli)
+{
+	os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len);
+	dev->info.device_name[cli->dev_name_len] = '\0';
+	dev->info.dev_capab = cli->dev_capab;
+	dev->info.config_methods = cli->config_methods;
+	os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8);
+	dev->info.wps_sec_dev_type_list_len = 8 * cli->num_sec_dev_types;
+	os_memcpy(dev->info.wps_sec_dev_type_list, cli->sec_dev_types,
+		  dev->info.wps_sec_dev_type_list_len);
+}
+
+
+static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
+				 const u8 *go_interface_addr, int freq,
+				 const u8 *gi, size_t gi_len)
+{
+	struct p2p_group_info info;
+	size_t c;
+	struct p2p_device *dev;
+
+	if (gi == NULL)
+		return 0;
+
+	if (p2p_group_info_parse(gi, gi_len, &info) < 0)
+		return -1;
+
+	/*
+	 * Clear old data for this group; if the devices are still in the
+	 * group, the information will be restored in the loop following this.
+	 */
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (os_memcpy(dev->member_in_go_iface, go_interface_addr,
+			      ETH_ALEN) == 0) {
+			os_memset(dev->member_in_go_iface, 0, ETH_ALEN);
+			os_memset(dev->member_in_go_dev, 0, ETH_ALEN);
+		}
+	}
+
+	for (c = 0; c < info.num_clients; c++) {
+		struct p2p_client_info *cli = &info.client[c];
+		dev = p2p_get_device(p2p, cli->p2p_device_addr);
+		if (dev) {
+			/*
+			 * Update information only if we have not received this
+			 * directly from the client.
+			 */
+			if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY |
+					  P2P_DEV_PROBE_REQ_ONLY))
+				p2p_copy_client_info(dev, cli);
+			if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+				dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
+			}
+		} else {
+			dev = p2p_create_device(p2p, cli->p2p_device_addr);
+			if (dev == NULL)
+				continue;
+			dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
+			p2p_copy_client_info(dev, cli);
+			dev->oper_freq = freq;
+			p2p->cfg->dev_found(p2p->cfg->cb_ctx,
+					    dev->info.p2p_device_addr,
+					    &dev->info, 1);
+			dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+		}
+
+		os_memcpy(dev->interface_addr, cli->p2p_interface_addr,
+			  ETH_ALEN);
+		os_get_time(&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);
+	}
+
+	return 0;
+}
+
+
+static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req,
+			      const struct p2p_message *msg)
+{
+	os_memcpy(dev->info.device_name, msg->device_name,
+		  sizeof(dev->info.device_name));
+
+	if (msg->manufacturer &&
+	    msg->manufacturer_len < sizeof(dev->info.manufacturer)) {
+		os_memset(dev->info.manufacturer, 0,
+			  sizeof(dev->info.manufacturer));
+		os_memcpy(dev->info.manufacturer, msg->manufacturer,
+			  msg->manufacturer_len);
+	}
+
+	if (msg->model_name &&
+	    msg->model_name_len < sizeof(dev->info.model_name)) {
+		os_memset(dev->info.model_name, 0,
+			  sizeof(dev->info.model_name));
+		os_memcpy(dev->info.model_name, msg->model_name,
+			  msg->model_name_len);
+	}
+
+	if (msg->model_number &&
+	    msg->model_number_len < sizeof(dev->info.model_number)) {
+		os_memset(dev->info.model_number, 0,
+			  sizeof(dev->info.model_number));
+		os_memcpy(dev->info.model_number, msg->model_number,
+			  msg->model_number_len);
+	}
+
+	if (msg->serial_number &&
+	    msg->serial_number_len < sizeof(dev->info.serial_number)) {
+		os_memset(dev->info.serial_number, 0,
+			  sizeof(dev->info.serial_number));
+		os_memcpy(dev->info.serial_number, msg->serial_number,
+			  msg->serial_number_len);
+	}
+
+	if (msg->pri_dev_type)
+		os_memcpy(dev->info.pri_dev_type, msg->pri_dev_type,
+			  sizeof(dev->info.pri_dev_type));
+	else if (msg->wps_pri_dev_type)
+		os_memcpy(dev->info.pri_dev_type, msg->wps_pri_dev_type,
+			  sizeof(dev->info.pri_dev_type));
+
+	if (msg->wps_sec_dev_type_list) {
+		os_memcpy(dev->info.wps_sec_dev_type_list,
+			  msg->wps_sec_dev_type_list,
+			  msg->wps_sec_dev_type_list_len);
+		dev->info.wps_sec_dev_type_list_len =
+			msg->wps_sec_dev_type_list_len;
+	}
+
+	if (msg->capability) {
+		dev->info.dev_capab = msg->capability[0];
+		dev->info.group_capab = msg->capability[1];
+	}
+
+	if (msg->ext_listen_timing) {
+		dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing);
+		dev->ext_listen_interval =
+			WPA_GET_LE16(msg->ext_listen_timing + 2);
+	}
+
+	if (!probe_req) {
+		dev->info.config_methods = msg->config_methods ?
+			msg->config_methods : msg->wps_config_methods;
+	}
+}
+
+
+/**
+ * p2p_add_device - Add peer entries based on scan results
+ * @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)
+ * @level: Signal level (signal strength of the received frame from the peer)
+ * @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
+ * Returns: 0 on success, -1 on failure
+ *
+ * If the scan result is for a GO, the clients in the group will also be added
+ * to the peer table. This function can also be used with some other frames
+ * like Provision Discovery Request that contains P2P Capability and P2P Device
+ * Info attributes.
+ */
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
+		   const u8 *ies, size_t ies_len)
+{
+	struct p2p_device *dev;
+	struct p2p_message msg;
+	const u8 *p2p_dev_addr;
+	int i;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_ies(ies, ies_len, &msg)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to parse P2P IE for a device entry");
+		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 {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Ignore scan data without P2P Device Info or "
+			"P2P Device Id");
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	if (!is_zero_ether_addr(p2p->peer_filter) &&
+	    os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Do not add peer "
+			"filter for " MACSTR " due to peer filter",
+			MAC2STR(p2p_dev_addr));
+		return 0;
+	}
+
+	dev = p2p_create_device(p2p, p2p_dev_addr);
+	if (dev == NULL) {
+		p2p_parse_free(&msg);
+		return -1;
+	}
+	os_get_time(&dev->last_seen);
+	dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+
+	if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0)
+		os_memcpy(dev->interface_addr, addr, ETH_ALEN);
+	if (msg.ssid &&
+	    (msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
+	     os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
+	     != 0)) {
+		os_memcpy(dev->oper_ssid, msg.ssid + 2, msg.ssid[1]);
+		dev->oper_ssid_len = msg.ssid[1];
+	}
+
+	if (freq >= 2412 && freq <= 2484 && msg.ds_params &&
+	    *msg.ds_params >= 1 && *msg.ds_params <= 14) {
+		int ds_freq;
+		if (*msg.ds_params == 14)
+			ds_freq = 2484;
+		else
+			ds_freq = 2407 + *msg.ds_params * 5;
+		if (freq != ds_freq) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Update Listen frequency based on DS "
+				"Parameter Set IE: %d -> %d MHz",
+				freq, ds_freq);
+			freq = ds_freq;
+		}
+	}
+
+	if (dev->listen_freq && dev->listen_freq != freq) {
+		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;
+	dev->level = level;
+
+	p2p_copy_wps_info(dev, 0, &msg);
+
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		wpabuf_free(dev->info.wps_vendor_ext[i]);
+		dev->info.wps_vendor_ext[i] = NULL;
+	}
+
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		if (msg.wps_vendor_ext[i] == NULL)
+			break;
+		dev->info.wps_vendor_ext[i] = wpabuf_alloc_copy(
+			msg.wps_vendor_ext[i], msg.wps_vendor_ext_len[i]);
+		if (dev->info.wps_vendor_ext[i] == NULL)
+			break;
+	}
+
+	p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info,
+			      msg.group_info_len);
+
+	p2p_parse_free(&msg);
+
+	if (p2p_pending_sd_req(p2p, dev))
+		dev->flags |= P2P_DEV_SD_SCHEDULE;
+
+	if (dev->flags & P2P_DEV_REPORTED)
+		return 0;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Peer found with Listen frequency %d MHz", freq);
+	if (dev->flags & P2P_DEV_USER_REJECTED) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Do not report rejected device");
+		return 0;
+	}
+
+	p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info,
+			    !(dev->flags & P2P_DEV_REPORTED_ONCE));
+	dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+
+	return 0;
+}
+
+
+static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
+{
+	int i;
+
+	if (p2p->go_neg_peer == dev)
+		p2p->go_neg_peer = NULL;
+	if (p2p->invite_peer == dev)
+		p2p->invite_peer = NULL;
+	if (p2p->sd_peer == dev)
+		p2p->sd_peer = NULL;
+	if (p2p->pending_client_disc_go == dev)
+		p2p->pending_client_disc_go = NULL;
+
+	p2p->cfg->dev_lost(p2p->cfg->cb_ctx, dev->info.p2p_device_addr);
+
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		wpabuf_free(dev->info.wps_vendor_ext[i]);
+		dev->info.wps_vendor_ext[i] = NULL;
+	}
+
+	os_free(dev);
+}
+
+
+static int p2p_get_next_prog_freq(struct p2p_data *p2p)
+{
+	struct p2p_channels *c;
+	struct p2p_reg_class *cla;
+	size_t cl, ch;
+	int found = 0;
+	u8 reg_class;
+	u8 channel;
+	int freq;
+
+	c = &p2p->cfg->channels;
+	for (cl = 0; cl < c->reg_classes; cl++) {
+		cla = &c->reg_class[cl];
+		if (cla->reg_class != p2p->last_prog_scan_class)
+			continue;
+		for (ch = 0; ch < cla->channels; ch++) {
+			if (cla->channel[ch] == p2p->last_prog_scan_chan) {
+				found = 1;
+				break;
+			}
+		}
+		if (found)
+			break;
+	}
+
+	if (!found) {
+		/* Start from beginning */
+		reg_class = c->reg_class[0].reg_class;
+		channel = c->reg_class[0].channel[0];
+	} else {
+		/* Pick the next channel */
+		ch++;
+		if (ch == cla->channels) {
+			cl++;
+			if (cl == c->reg_classes)
+				cl = 0;
+			ch = 0;
+		}
+		reg_class = c->reg_class[cl].reg_class;
+		channel = c->reg_class[cl].channel[ch];
+	}
+
+	freq = p2p_channel_to_freq(p2p->cfg->country, reg_class, channel);
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Next progressive search "
+		"channel: reg_class %u channel %u -> %d MHz",
+		reg_class, channel, freq);
+	p2p->last_prog_scan_class = reg_class;
+	p2p->last_prog_scan_chan = channel;
+
+	if (freq == 2412 || freq == 2437 || freq == 2462)
+		return 0; /* No need to add social channels */
+	return freq;
+}
+
+
+static void p2p_search(struct p2p_data *p2p)
+{
+	int freq = 0;
+	enum p2p_scan_type type;
+
+	if (p2p->drv_in_listen) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still "
+			"in Listen state - wait for it to end before "
+			"continuing");
+		return;
+	}
+	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+
+	if (p2p->go_neg_peer) {
+		/*
+		 * Only scan the known listen frequency of the peer
+		 * during GO Negotiation start.
+		 */
+		freq = p2p->go_neg_peer->listen_freq;
+		if (freq <= 0)
+			freq = p2p->go_neg_peer->oper_freq;
+		type = P2P_SCAN_SPECIFIC;
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+			"for freq %u (GO Neg)", freq);
+	} else if (p2p->invite_peer) {
+		/*
+		 * Only scan the known listen frequency of the peer
+		 * during Invite start.
+		 */
+		freq = p2p->invite_peer->listen_freq;
+		if (freq <= 0)
+			freq = p2p->invite_peer->oper_freq;
+		type = P2P_SCAN_SPECIFIC;
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+			"for freq %u (Invite)", freq);
+	} else if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
+		   (freq = p2p_get_next_prog_freq(p2p)) > 0) {
+		type = P2P_SCAN_SOCIAL_PLUS_ONE;
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
+			"(+ freq %u)", freq);
+	} else {
+		type = P2P_SCAN_SOCIAL;
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search");
+	}
+
+	if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
+			       p2p->num_req_dev_types, p2p->req_dev_types) < 0)
+	{
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Scan request failed");
+		p2p_continue_find(p2p);
+	} else {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan");
+		p2p->p2p_scan_running = 1;
+		eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+		eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
+				       p2p, NULL);
+	}
+}
+
+
+static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Find timeout -> stop");
+	p2p_stop_find(p2p);
+}
+
+
+static int p2p_run_after_scan(struct p2p_data *p2p)
+{
+	struct p2p_device *dev;
+	enum p2p_after_scan op;
+
+	if (p2p->after_scan_tx) {
+		int ret;
+		/* TODO: schedule p2p_run_after_scan to be called from TX
+		 * status callback(?) */
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send pending "
+			"Action frame at p2p_scan completion");
+		ret = p2p->cfg->send_action(p2p->cfg->cb_ctx,
+					    p2p->after_scan_tx->freq,
+					    p2p->after_scan_tx->dst,
+					    p2p->after_scan_tx->src,
+					    p2p->after_scan_tx->bssid,
+					    (u8 *) (p2p->after_scan_tx + 1),
+					    p2p->after_scan_tx->len,
+					    p2p->after_scan_tx->wait_time);
+		os_free(p2p->after_scan_tx);
+		p2p->after_scan_tx = NULL;
+		return 1;
+	}
+
+	op = p2p->start_after_scan;
+	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+	switch (op) {
+	case P2P_AFTER_SCAN_NOTHING:
+		break;
+	case P2P_AFTER_SCAN_LISTEN:
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously "
+			"requested Listen state");
+		p2p_listen(p2p, p2p->pending_listen_sec * 1000 +
+			   p2p->pending_listen_usec / 1000);
+		return 1;
+	case P2P_AFTER_SCAN_CONNECT:
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously "
+			"requested connect with " MACSTR,
+			MAC2STR(p2p->after_scan_peer));
+		dev = p2p_get_device(p2p, p2p->after_scan_peer);
+		if (dev == NULL) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer not "
+				"known anymore");
+			break;
+		}
+		p2p_connect_send(p2p, dev);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+	int running;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan timeout "
+		"(running=%d)", p2p->p2p_scan_running);
+	running = p2p->p2p_scan_running;
+	/* Make sure we recover from missed scan results callback */
+	p2p->p2p_scan_running = 0;
+
+	if (running)
+		p2p_run_after_scan(p2p);
+}
+
+
+static void p2p_free_req_dev_types(struct p2p_data *p2p)
+{
+	p2p->num_req_dev_types = 0;
+	os_free(p2p->req_dev_types);
+	p2p->req_dev_types = NULL;
+}
+
+
+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)
+{
+	int res;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting find (type=%d)",
+		type);
+	if (p2p->p2p_scan_running) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan is "
+			"already running");
+	}
+
+	p2p_free_req_dev_types(p2p);
+	if (req_dev_types && num_req_dev_types) {
+		p2p->req_dev_types = os_malloc(num_req_dev_types *
+					       WPS_DEV_TYPE_LEN);
+		if (p2p->req_dev_types == NULL)
+			return -1;
+		os_memcpy(p2p->req_dev_types, req_dev_types,
+			  num_req_dev_types * WPS_DEV_TYPE_LEN);
+		p2p->num_req_dev_types = num_req_dev_types;
+	}
+
+	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+	p2p_clear_timeout(p2p);
+	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+	p2p->find_type = type;
+	p2p_device_clear_reported(p2p);
+	p2p_set_state(p2p, P2P_SEARCH);
+	eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+	if (timeout)
+		eloop_register_timeout(timeout, 0, p2p_find_timeout,
+				       p2p, NULL);
+	switch (type) {
+	case P2P_FIND_START_WITH_FULL:
+	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);
+		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);
+		break;
+	default:
+		return -1;
+	}
+
+	if (res == 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Running p2p_scan");
+		p2p->p2p_scan_running = 1;
+		eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+		eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
+				       p2p, NULL);
+	} else {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start "
+			"p2p_scan");
+	}
+
+	return res;
+}
+
+
+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);
+	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;
+	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;
+	}
+	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+}
+
+
+void p2p_stop_find(struct p2p_data *p2p)
+{
+	p2p_stop_find_for_freq(p2p, 0);
+}
+
+
+static int p2p_prepare_channel(struct p2p_data *p2p, unsigned int force_freq)
+{
+	if (force_freq) {
+		u8 op_reg_class, op_channel;
+		if (p2p_freq_to_channel(p2p->cfg->country, force_freq,
+					&op_reg_class, &op_channel) < 0) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Unsupported frequency %u MHz",
+				force_freq);
+			return -1;
+		}
+		if (!p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+					   op_channel)) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Frequency %u MHz (oper_class %u "
+				"channel %u) not allowed for P2P",
+				force_freq, op_reg_class, op_channel);
+			return -1;
+		}
+		p2p->op_reg_class = op_reg_class;
+		p2p->op_channel = op_channel;
+		p2p->channels.reg_classes = 1;
+		p2p->channels.reg_class[0].channels = 1;
+		p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
+		p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
+	} else {
+		u8 op_reg_class, op_channel;
+
+		if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 &&
+		    p2p_supported_freq(p2p, p2p->best_freq_overall) &&
+		    p2p_freq_to_channel(p2p->cfg->country,
+					p2p->best_freq_overall,
+					&op_reg_class, &op_channel) == 0) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Select best overall channel as "
+				"operating channel preference");
+			p2p->op_reg_class = op_reg_class;
+			p2p->op_channel = op_channel;
+		} else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 &&
+			   p2p_supported_freq(p2p, p2p->best_freq_5) &&
+			   p2p_freq_to_channel(p2p->cfg->country,
+					       p2p->best_freq_5,
+					       &op_reg_class, &op_channel) ==
+			   0) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Select best 5 GHz channel as "
+				"operating channel preference");
+			p2p->op_reg_class = op_reg_class;
+			p2p->op_channel = op_channel;
+		} else if (!p2p->cfg->cfg_op_channel &&
+			   p2p->best_freq_24 > 0 &&
+			   p2p_supported_freq(p2p, p2p->best_freq_24) &&
+			   p2p_freq_to_channel(p2p->cfg->country,
+					       p2p->best_freq_24,
+					       &op_reg_class, &op_channel) ==
+			   0) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Select best 2.4 GHz channel as "
+				"operating channel preference");
+			p2p->op_reg_class = op_reg_class;
+			p2p->op_channel = op_channel;
+		} else {
+			p2p->op_reg_class = p2p->cfg->op_reg_class;
+			p2p->op_channel = p2p->cfg->op_channel;
+		}
+
+		os_memcpy(&p2p->channels, &p2p->cfg->channels,
+			  sizeof(struct p2p_channels));
+	}
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Own preference for operation channel: "
+		"Operating Class %u Channel %u%s",
+		p2p->op_reg_class, p2p->op_channel,
+		force_freq ? " (forced)" : "");
+
+	return 0;
+}
+
+
+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)
+{
+	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",
+		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
+		wps_method, persistent_group);
+
+	if (p2p_prepare_channel(p2p, force_freq) < 0)
+		return -1;
+
+	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,
+			"P2P: Cannot connect to unknown P2P Device " MACSTR,
+			MAC2STR(peer_addr));
+		return -1;
+	}
+
+	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+		if (!(dev->info.dev_capab &
+		      P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Cannot connect to P2P Device " MACSTR
+				" that is in a group and is not discoverable",
+				MAC2STR(peer_addr));
+			return -1;
+		}
+		if (dev->oper_freq <= 0) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Cannot connect to P2P Device " MACSTR
+				" with incomplete information",
+				MAC2STR(peer_addr));
+			return -1;
+		}
+
+		/*
+		 * First, try to connect directly. If the peer does not
+		 * acknowledge frames, assume it is sleeping and use device
+		 * discoverability via the GO at that point.
+		 */
+	}
+
+	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;
+	dev->connect_reqs = 0;
+	dev->go_neg_req_sent = 0;
+	dev->go_state = UNKNOWN_GO;
+	if (persistent_group)
+		dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
+	else
+		dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_GROUP;
+	p2p->go_intent = go_intent;
+	os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+
+	if (p2p->state != P2P_IDLE)
+		p2p_stop_find(p2p);
+
+	if (p2p->after_scan_tx) {
+		/*
+		 * We need to drop the pending frame to avoid issues with the
+		 * new GO Negotiation, e.g., when the pending frame was from a
+		 * previous attempt at starting a GO Negotiation.
+		 */
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped "
+			"previous pending Action frame TX that was waiting "
+			"for p2p_scan completion");
+		os_free(p2p->after_scan_tx);
+		p2p->after_scan_tx = NULL;
+	}
+
+	dev->wps_method = wps_method;
+	dev->status = P2P_SC_SUCCESS;
+
+	if (force_freq)
+		dev->flags |= P2P_DEV_FORCE_FREQ;
+	else
+		dev->flags &= ~P2P_DEV_FORCE_FREQ;
+
+	if (p2p->p2p_scan_running) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: p2p_scan running - delay connect send");
+		p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT;
+		os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN);
+		return 0;
+	}
+	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+
+	return p2p_connect_send(p2p, dev);
+}
+
+
+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)
+{
+	struct p2p_device *dev;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Request to authorize group negotiation - peer=" MACSTR
+		"  GO Intent=%d  Intended Interface Address=" MACSTR
+		" wps_method=%d  persistent_group=%d",
+		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
+		wps_method, persistent_group);
+
+	if (p2p_prepare_channel(p2p, force_freq) < 0)
+		return -1;
+
+	dev = p2p_get_device(p2p, peer_addr);
+	if (dev == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Cannot authorize unknown P2P Device " MACSTR,
+			MAC2STR(peer_addr));
+		return -1;
+	}
+
+	dev->flags &= ~P2P_DEV_NOT_YET_READY;
+	dev->flags &= ~P2P_DEV_USER_REJECTED;
+	dev->go_neg_req_sent = 0;
+	dev->go_state = UNKNOWN_GO;
+	if (persistent_group)
+		dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
+	else
+		dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_GROUP;
+	p2p->go_intent = go_intent;
+	os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+
+	dev->wps_method = wps_method;
+	dev->status = P2P_SC_SUCCESS;
+
+	if (force_freq)
+		dev->flags |= P2P_DEV_FORCE_FREQ;
+	else
+		dev->flags &= ~P2P_DEV_FORCE_FREQ;
+
+	return 0;
+}
+
+
+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);
+
+	p2p_copy_wps_info(dev, 0, msg);
+
+	if (msg->listen_channel) {
+		int freq;
+		freq = p2p_channel_to_freq((char *) msg->listen_channel,
+					   msg->listen_channel[3],
+					   msg->listen_channel[4]);
+		if (freq < 0) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Unknown peer Listen channel: "
+				"country=%c%c(0x%02x) reg_class=%u channel=%u",
+				msg->listen_channel[0],
+				msg->listen_channel[1],
+				msg->listen_channel[2],
+				msg->listen_channel[3],
+				msg->listen_channel[4]);
+		} else {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update "
+				"peer " MACSTR " Listen channel: %u -> %u MHz",
+				MAC2STR(dev->info.p2p_device_addr),
+				dev->listen_freq, freq);
+			dev->listen_freq = freq;
+		}
+	}
+
+	if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+		dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Completed device entry based on data from "
+			"GO Negotiation Request");
+	} else {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Created device entry based on GO Neg Req: "
+			MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' "
+			"listen_freq=%d",
+			MAC2STR(dev->info.p2p_device_addr),
+			dev->info.dev_capab, dev->info.group_capab,
+			dev->info.device_name, dev->listen_freq);
+	}
+
+	dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY;
+
+	if (dev->flags & P2P_DEV_USER_REJECTED) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Do not report rejected device");
+		return;
+	}
+
+	p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info,
+			    !(dev->flags & P2P_DEV_REPORTED_ONCE));
+	dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+}
+
+
+void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len)
+{
+	os_memcpy(ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+	p2p_random((char *) &ssid[P2P_WILDCARD_SSID_LEN], 2);
+	os_memcpy(&ssid[P2P_WILDCARD_SSID_LEN + 2],
+		  p2p->cfg->ssid_postfix, p2p->cfg->ssid_postfix_len);
+	*ssid_len = P2P_WILDCARD_SSID_LEN + 2 + p2p->cfg->ssid_postfix_len;
+}
+
+
+int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params)
+{
+	p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
+	p2p_random(params->passphrase, 8);
+	return 0;
+}
+
+
+void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
+{
+	struct p2p_go_neg_results res;
+	int go = peer->go_state == LOCAL_GO;
+	struct p2p_channels intersection;
+	int freqs;
+	size_t i, j;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: GO Negotiation with " MACSTR " completed (%s will be "
+		"GO)", MAC2STR(peer->info.p2p_device_addr),
+		go ? "local end" : "peer");
+
+	os_memset(&res, 0, sizeof(res));
+	res.role_go = go;
+	os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
+	os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
+	res.wps_method = peer->wps_method;
+	if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP)
+		res.persistent_group = 1;
+
+	if (go) {
+		/* Setup AP mode for WPS provisioning */
+		res.freq = p2p_channel_to_freq(p2p->cfg->country,
+					       p2p->op_reg_class,
+					       p2p->op_channel);
+		os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
+		res.ssid_len = p2p->ssid_len;
+		p2p_random(res.passphrase, 8);
+	} else {
+		res.freq = peer->oper_freq;
+		if (p2p->ssid_len) {
+			os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
+			res.ssid_len = p2p->ssid_len;
+		}
+	}
+
+	p2p_channels_intersect(&p2p->channels, &peer->channels,
+			       &intersection);
+	freqs = 0;
+	for (i = 0; i < intersection.reg_classes; i++) {
+		struct p2p_reg_class *c = &intersection.reg_class[i];
+		if (freqs + 1 == P2P_MAX_CHANNELS)
+			break;
+		for (j = 0; j < c->channels; j++) {
+			int freq;
+			if (freqs + 1 == P2P_MAX_CHANNELS)
+				break;
+			freq = p2p_channel_to_freq(peer->country, c->reg_class,
+						   c->channel[j]);
+			if (freq < 0)
+				continue;
+			res.freq_list[freqs++] = freq;
+		}
+	}
+
+	res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout;
+
+	p2p_clear_timeout(p2p);
+	peer->go_neg_req_sent = 0;
+	peer->wps_method = WPS_NOT_READY;
+
+	p2p_set_state(p2p, P2P_PROVISIONING);
+	p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
+}
+
+
+static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
+			      const u8 *data, size_t len, int rx_freq)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: RX P2P Public Action from " MACSTR, MAC2STR(sa));
+	wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len);
+
+	if (len < 1)
+		return;
+
+	switch (data[0]) {
+	case P2P_GO_NEG_REQ:
+		p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq);
+		break;
+	case P2P_GO_NEG_RESP:
+		p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq);
+		break;
+	case P2P_GO_NEG_CONF:
+		p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1);
+		break;
+	case P2P_INVITATION_REQ:
+		p2p_process_invitation_req(p2p, sa, data + 1, len - 1,
+					   rx_freq);
+		break;
+	case P2P_INVITATION_RESP:
+		p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
+		break;
+	case P2P_PROV_DISC_REQ:
+		p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
+		break;
+	case P2P_PROV_DISC_RESP:
+		p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1);
+		break;
+	case P2P_DEV_DISC_REQ:
+		p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
+		break;
+	case P2P_DEV_DISC_RESP:
+		p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1);
+		break;
+	default:
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported P2P Public Action frame type %d",
+			data[0]);
+		break;
+	}
+}
+
+
+void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+			  const u8 *bssid, const u8 *data, size_t len,
+			  int freq)
+{
+	if (len < 1)
+		return;
+
+	switch (data[0]) {
+	case WLAN_PA_VENDOR_SPECIFIC:
+		data++;
+		len--;
+		if (len < 3)
+			return;
+		if (WPA_GET_BE24(data) != OUI_WFA)
+			return;
+
+		data += 3;
+		len -= 3;
+		if (len < 1)
+			return;
+
+		if (*data != P2P_OUI_TYPE)
+			return;
+
+		p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq);
+		break;
+	case WLAN_PA_GAS_INITIAL_REQ:
+		p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq);
+		break;
+	case WLAN_PA_GAS_INITIAL_RESP:
+		p2p_rx_gas_initial_resp(p2p, sa, data + 1, len - 1, freq);
+		break;
+	case WLAN_PA_GAS_COMEBACK_REQ:
+		p2p_rx_gas_comeback_req(p2p, sa, data + 1, len - 1, freq);
+		break;
+	case WLAN_PA_GAS_COMEBACK_RESP:
+		p2p_rx_gas_comeback_resp(p2p, sa, data + 1, len - 1, freq);
+		break;
+	}
+}
+
+
+void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+		   const u8 *bssid, u8 category,
+		   const u8 *data, size_t len, int freq)
+{
+	if (category == WLAN_ACTION_PUBLIC) {
+		p2p_rx_action_public(p2p, da, sa, bssid, data, len, freq);
+		return;
+	}
+
+	if (category != WLAN_ACTION_VENDOR_SPECIFIC)
+		return;
+
+	if (len < 4)
+		return;
+
+	if (WPA_GET_BE24(data) != OUI_WFA)
+		return;
+	data += 3;
+	len -= 3;
+
+	if (*data != P2P_OUI_TYPE)
+		return;
+	data++;
+	len--;
+
+	/* P2P action frame */
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: RX P2P Action from " MACSTR, MAC2STR(sa));
+	wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len);
+
+	if (len < 1)
+		return;
+	switch (data[0]) {
+	case P2P_NOA:
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Received P2P Action - Notice of Absence");
+		/* TODO */
+		break;
+	case P2P_PRESENCE_REQ:
+		p2p_process_presence_req(p2p, da, sa, data + 1, len - 1, freq);
+		break;
+	case P2P_PRESENCE_RESP:
+		p2p_process_presence_resp(p2p, da, sa, data + 1, len - 1);
+		break;
+	case P2P_GO_DISC_REQ:
+		p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq);
+		break;
+	default:
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Received P2P Action - unknown type %u", data[0]);
+		break;
+	}
+}
+
+
+static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+	if (p2p->go_neg_peer == NULL)
+		return;
+	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+	p2p->go_neg_peer->status = P2P_SC_SUCCESS;
+	p2p_connect_send(p2p, p2p->go_neg_peer);
+}
+
+
+static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+	if (p2p->invite_peer == NULL)
+		return;
+	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+	p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr);
+}
+
+
+static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr,
+				       const u8 *ie, size_t ie_len)
+{
+	struct p2p_message msg;
+	struct p2p_device *dev;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_ies(ie, ie_len, &msg) < 0 || msg.p2p_attributes == NULL)
+	{
+		p2p_parse_free(&msg);
+		return; /* not a P2P probe */
+	}
+
+	if (msg.ssid == NULL || msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
+	    os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
+	    != 0) {
+		/* The Probe Request is not part of P2P Device Discovery. It is
+		 * not known whether the source address of the frame is the P2P
+		 * Device Address or P2P Interface Address. Do not add a new
+		 * peer entry based on this frames.
+		 */
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	dev = p2p_get_device(p2p, addr);
+	if (dev) {
+		if (dev->country[0] == 0 && msg.listen_channel)
+			os_memcpy(dev->country, msg.listen_channel, 3);
+		p2p_parse_free(&msg);
+		return; /* already known */
+	}
+
+	dev = p2p_create_device(p2p, addr);
+	if (dev == NULL) {
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	os_get_time(&dev->last_seen);
+	dev->flags |= P2P_DEV_PROBE_REQ_ONLY;
+
+	if (msg.listen_channel) {
+		os_memcpy(dev->country, msg.listen_channel, 3);
+		dev->listen_freq = p2p_channel_to_freq(dev->country,
+						       msg.listen_channel[3],
+						       msg.listen_channel[4]);
+	}
+
+	p2p_copy_wps_info(dev, 1, &msg);
+
+	p2p_parse_free(&msg);
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Created device entry based on Probe Req: " MACSTR
+		" dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d",
+		MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab,
+		dev->info.group_capab, dev->info.device_name,
+		dev->listen_freq);
+}
+
+
+struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
+						const u8 *addr,
+						struct p2p_message *msg)
+{
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, addr);
+	if (dev) {
+		os_get_time(&dev->last_seen);
+		return dev; /* already known */
+	}
+
+	dev = p2p_create_device(p2p, addr);
+	if (dev == NULL)
+		return NULL;
+
+	p2p_add_dev_info(p2p, addr, dev, msg);
+
+	return dev;
+}
+
+
+static int dev_type_match(const u8 *dev_type, const u8 *req_dev_type)
+{
+	if (os_memcmp(dev_type, req_dev_type, WPS_DEV_TYPE_LEN) == 0)
+		return 1;
+	if (os_memcmp(dev_type, req_dev_type, 2) == 0 &&
+	    WPA_GET_BE32(&req_dev_type[2]) == 0 &&
+	    WPA_GET_BE16(&req_dev_type[6]) == 0)
+		return 1; /* Category match with wildcard OUI/sub-category */
+	return 0;
+}
+
+
+int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
+			size_t num_req_dev_type)
+{
+	size_t i;
+	for (i = 0; i < num_req_dev_type; i++) {
+		if (dev_type_match(dev_type, req_dev_type[i]))
+			return 1;
+	}
+	return 0;
+}
+
+
+/**
+ * p2p_match_dev_type - Match local device type with requested type
+ * @p2p: P2P module context from p2p_init()
+ * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
+ * Returns: 1 on match, 0 on mismatch
+ *
+ * This function can be used to match the Requested Device Type attribute in
+ * WPS IE with the local device types for deciding whether to reply to a Probe
+ * Request frame.
+ */
+int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps)
+{
+	struct wps_parse_attr attr;
+	size_t i;
+
+	if (wps_parse_msg(wps, &attr))
+		return 1; /* assume no Requested Device Type attributes */
+
+	if (attr.num_req_dev_type == 0)
+		return 1; /* no Requested Device Type attributes -> match */
+
+	if (dev_type_list_match(p2p->cfg->pri_dev_type, attr.req_dev_type,
+				attr.num_req_dev_type))
+		return 1; /* Own Primary Device Type matches */
+
+	for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
+		if (dev_type_list_match(p2p->cfg->sec_dev_type[i],
+					attr.req_dev_type,
+					attr.num_req_dev_type))
+		return 1; /* Own Secondary Device Type matches */
+
+	/* No matching device type found */
+	return 0;
+}
+
+
+struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
+{
+	struct wpabuf *buf;
+	u8 *len;
+
+	buf = wpabuf_alloc(1000);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_build_wps_ie(p2p, buf, DEV_PW_DEFAULT, 1);
+
+	/* P2P IE */
+	len = p2p_buf_add_ie_hdr(buf);
+	p2p_buf_add_capability(buf, p2p->dev_capab, 0);
+	if (p2p->ext_listen_interval)
+		p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
+					      p2p->ext_listen_interval);
+	p2p_buf_add_device_info(buf, p2p, NULL);
+	p2p_buf_update_ie_hdr(buf, len);
+
+	return buf;
+}
+
+
+static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *ie,
+			    size_t ie_len)
+{
+	struct ieee802_11_elems elems;
+	struct wpabuf *buf;
+	struct ieee80211_mgmt *resp;
+	struct wpabuf *wps;
+	struct wpabuf *ies;
+
+	if (!p2p->in_listen || !p2p->drv_in_listen) {
+		/* not in Listen state - ignore Probe Request */
+		return;
+	}
+
+	if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
+	    ParseFailed) {
+		/* Ignore invalid Probe Request frames */
+		return;
+	}
+
+	if (elems.p2p == NULL) {
+		/* not a P2P probe - ignore it */
+		return;
+	}
+
+	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;
+	}
+
+	/* Check Requested Device Type match */
+	wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+	if (wps && !p2p_match_dev_type(p2p, wps)) {
+		wpabuf_free(wps);
+		/* No match with Requested Device Type */
+		return;
+	}
+	wpabuf_free(wps);
+
+	if (!p2p->cfg->send_probe_resp)
+		return; /* Response generated elsewhere */
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Reply to P2P Probe Request in Listen state");
+
+	/*
+	 * We do not really have a specific BSS that this frame is advertising,
+	 * so build a frame that has some information in valid format. This is
+	 * really only used for discovery purposes, not to learn exact BSS
+	 * parameters.
+	 */
+	ies = p2p_build_probe_resp_ies(p2p);
+	if (ies == NULL)
+		return;
+
+	buf = wpabuf_alloc(200 + wpabuf_len(ies));
+	if (buf == NULL) {
+		wpabuf_free(ies);
+		return;
+	}
+
+	resp = NULL;
+	resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp);
+
+	resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+					   (WLAN_FC_STYPE_PROBE_RESP << 4));
+	os_memcpy(resp->da, addr, ETH_ALEN);
+	os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN);
+	os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN);
+	resp->u.probe_resp.beacon_int = host_to_le16(100);
+	/* hardware or low-level driver will setup seq_ctrl and timestamp */
+	resp->u.probe_resp.capab_info =
+		host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE |
+			     WLAN_CAPABILITY_PRIVACY |
+			     WLAN_CAPABILITY_SHORT_SLOT_TIME);
+
+	wpabuf_put_u8(buf, WLAN_EID_SSID);
+	wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN);
+	wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+
+	wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES);
+	wpabuf_put_u8(buf, 8);
+	wpabuf_put_u8(buf, (60 / 5) | 0x80);
+	wpabuf_put_u8(buf, 90 / 5);
+	wpabuf_put_u8(buf, (120 / 5) | 0x80);
+	wpabuf_put_u8(buf, 180 / 5);
+	wpabuf_put_u8(buf, (240 / 5) | 0x80);
+	wpabuf_put_u8(buf, 360 / 5);
+	wpabuf_put_u8(buf, 480 / 5);
+	wpabuf_put_u8(buf, 540 / 5);
+
+	wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS);
+	wpabuf_put_u8(buf, 1);
+	wpabuf_put_u8(buf, p2p->cfg->channel);
+
+	wpabuf_put_buf(buf, ies);
+	wpabuf_free(ies);
+
+	p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf);
+
+	wpabuf_free(buf);
+}
+
+
+int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *ie,
+		     size_t ie_len)
+{
+	p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
+
+	p2p_reply_probe(p2p, addr, ie, ie_len);
+
+	if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
+	    p2p->go_neg_peer &&
+	    os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN)
+	    == 0) {
+		/* Received a Probe Request from GO Negotiation peer */
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"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;
+	}
+
+	if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
+	    p2p->invite_peer &&
+	    os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN)
+	    == 0) {
+		/* Received a Probe Request from Invite peer */
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Found Invite peer - try to start Invite from "
+			"timeout");
+		eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid,
+				    u8 *buf, size_t len, struct wpabuf *p2p_ie)
+{
+	struct wpabuf *tmp;
+	u8 *lpos;
+	size_t tmplen;
+	int res;
+	u8 group_capab;
+
+	if (p2p_ie == NULL)
+		return 0; /* WLAN AP is not a P2P manager */
+
+	/*
+	 * (Re)Association Request - P2P IE
+	 * P2P Capability attribute (shall be present)
+	 * P2P Interface attribute (present if concurrent device and
+	 *	P2P Management is enabled)
+	 */
+	tmp = wpabuf_alloc(200);
+	if (tmp == NULL)
+		return -1;
+
+	lpos = p2p_buf_add_ie_hdr(tmp);
+	group_capab = 0;
+	if (p2p->num_groups > 0) {
+		group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
+		if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
+		    (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) &&
+		    p2p->cross_connect)
+			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+	}
+	p2p_buf_add_capability(tmp, p2p->dev_capab, group_capab);
+	if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
+	    (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED))
+		p2p_buf_add_p2p_interface(tmp, p2p);
+	p2p_buf_update_ie_hdr(tmp, lpos);
+
+	tmplen = wpabuf_len(tmp);
+	if (tmplen > len)
+		res = -1;
+	else {
+		os_memcpy(buf, wpabuf_head(tmp), tmplen);
+		res = tmplen;
+	}
+	wpabuf_free(tmp);
+
+	return res;
+}
+
+
+int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
+		     size_t len, int p2p_group, struct wpabuf *p2p_ie)
+{
+	struct wpabuf *tmp;
+	u8 *lpos;
+	struct p2p_device *peer;
+	size_t tmplen;
+	int res;
+
+	if (!p2p_group)
+		return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie);
+
+	/*
+	 * (Re)Association Request - P2P IE
+	 * P2P Capability attribute (shall be present)
+	 * Extended Listen Timing (may be present)
+	 * P2P Device Info attribute (shall be present)
+	 */
+	tmp = wpabuf_alloc(200);
+	if (tmp == NULL)
+		return -1;
+
+	peer = bssid ? p2p_get_device(p2p, bssid) : NULL;
+
+	lpos = p2p_buf_add_ie_hdr(tmp);
+	p2p_buf_add_capability(tmp, p2p->dev_capab, 0);
+	if (p2p->ext_listen_interval)
+		p2p_buf_add_ext_listen_timing(tmp, p2p->ext_listen_period,
+					      p2p->ext_listen_interval);
+	p2p_buf_add_device_info(tmp, p2p, peer);
+	p2p_buf_update_ie_hdr(tmp, lpos);
+
+	tmplen = wpabuf_len(tmp);
+	if (tmplen > len)
+		res = -1;
+	else {
+		os_memcpy(buf, wpabuf_head(tmp), tmplen);
+		res = tmplen;
+	}
+	wpabuf_free(tmp);
+
+	return res;
+}
+
+
+int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end)
+{
+	struct wpabuf *p2p_ie;
+	int ret;
+
+	p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE);
+	if (p2p_ie == NULL)
+		return 0;
+
+	ret = p2p_attr_text(p2p_ie, buf, end);
+	wpabuf_free(p2p_ie);
+	return ret;
+}
+
+
+static void p2p_clear_go_neg(struct p2p_data *p2p)
+{
+	p2p->go_neg_peer = NULL;
+	p2p_clear_timeout(p2p);
+	p2p_set_state(p2p, P2P_IDLE);
+}
+
+
+void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr)
+{
+	if (p2p->go_neg_peer == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No pending Group Formation - "
+			"ignore WPS registration success notification");
+		return; /* No pending Group Formation */
+	}
+
+	if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) !=
+	    0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Ignore WPS registration success notification "
+			"for " MACSTR " (GO Negotiation peer " MACSTR ")",
+			MAC2STR(mac_addr),
+			MAC2STR(p2p->go_neg_peer->intended_addr));
+		return; /* Ignore unexpected peer address */
+	}
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Group Formation completed successfully with " MACSTR,
+		MAC2STR(mac_addr));
+
+	p2p_clear_go_neg(p2p);
+}
+
+
+void p2p_group_formation_failed(struct p2p_data *p2p)
+{
+	if (p2p->go_neg_peer == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No pending Group Formation - "
+			"ignore group formation failure notification");
+		return; /* No pending Group Formation */
+	}
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Group Formation failed with " MACSTR,
+		MAC2STR(p2p->go_neg_peer->intended_addr));
+
+	p2p_clear_go_neg(p2p);
+}
+
+
+struct p2p_data * p2p_init(const struct p2p_config *cfg)
+{
+	struct p2p_data *p2p;
+
+	if (cfg->max_peers < 1)
+		return NULL;
+
+	p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg));
+	if (p2p == NULL)
+		return NULL;
+	p2p->cfg = (struct p2p_config *) (p2p + 1);
+	os_memcpy(p2p->cfg, cfg, sizeof(*cfg));
+	if (cfg->dev_name)
+		p2p->cfg->dev_name = os_strdup(cfg->dev_name);
+	if (cfg->manufacturer)
+		p2p->cfg->manufacturer = os_strdup(cfg->manufacturer);
+	if (cfg->model_name)
+		p2p->cfg->model_name = os_strdup(cfg->model_name);
+	if (cfg->model_number)
+		p2p->cfg->model_number = os_strdup(cfg->model_number);
+	if (cfg->serial_number)
+		p2p->cfg->serial_number = os_strdup(cfg->serial_number);
+
+	p2p->min_disc_int = 1;
+	p2p->max_disc_int = 3;
+
+	os_get_random(&p2p->next_tie_breaker, 1);
+	p2p->next_tie_breaker &= 0x01;
+	if (cfg->sd_request)
+		p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+	p2p->dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
+	if (cfg->concurrent_operations)
+		p2p->dev_capab |= P2P_DEV_CAPAB_CONCURRENT_OPER;
+	p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+
+	dl_list_init(&p2p->devices);
+
+	eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
+			       p2p_expiration_timeout, p2p, NULL);
+
+	return p2p;
+}
+
+
+void p2p_deinit(struct p2p_data *p2p)
+{
+	eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL);
+	eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
+	eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+	p2p_flush(p2p);
+	p2p_free_req_dev_types(p2p);
+	os_free(p2p->cfg->dev_name);
+	os_free(p2p->cfg->manufacturer);
+	os_free(p2p->cfg->model_name);
+	os_free(p2p->cfg->model_number);
+	os_free(p2p->cfg->serial_number);
+	os_free(p2p->groups);
+	wpabuf_free(p2p->sd_resp);
+	os_free(p2p->after_scan_tx);
+	p2p_remove_wps_vendor_extensions(p2p);
+	os_free(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);
+	dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
+			      list) {
+		dl_list_del(&dev->list);
+		p2p_device_free(p2p, dev);
+	}
+	p2p_free_sd_queries(p2p);
+	os_free(p2p->after_scan_tx);
+	p2p->after_scan_tx = NULL;
+}
+
+
+int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr)
+{
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, addr);
+	if (dev == NULL)
+		return -1;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unauthorizing " MACSTR,
+		MAC2STR(addr));
+
+	if (p2p->go_neg_peer == dev)
+		p2p->go_neg_peer = NULL;
+
+	dev->wps_method = WPS_NOT_READY;
+	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+
+	/* Check if after_scan_tx is for this peer. If so free it */
+	if (p2p->after_scan_tx &&
+	    os_memcmp(addr, p2p->after_scan_tx->dst, ETH_ALEN) == 0) {
+		os_free(p2p->after_scan_tx);
+		p2p->after_scan_tx = NULL;
+	}
+
+	return 0;
+}
+
+
+int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name)
+{
+	os_free(p2p->cfg->dev_name);
+	if (dev_name) {
+		p2p->cfg->dev_name = os_strdup(dev_name);
+		if (p2p->cfg->dev_name == NULL)
+			return -1;
+	} else
+		p2p->cfg->dev_name = NULL;
+	return 0;
+}
+
+
+int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer)
+{
+	os_free(p2p->cfg->manufacturer);
+	p2p->cfg->manufacturer = NULL;
+	if (manufacturer) {
+		p2p->cfg->manufacturer = os_strdup(manufacturer);
+		if (p2p->cfg->manufacturer == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+int p2p_set_model_name(struct p2p_data *p2p, const char *model_name)
+{
+	os_free(p2p->cfg->model_name);
+	p2p->cfg->model_name = NULL;
+	if (model_name) {
+		p2p->cfg->model_name = os_strdup(model_name);
+		if (p2p->cfg->model_name == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+int p2p_set_model_number(struct p2p_data *p2p, const char *model_number)
+{
+	os_free(p2p->cfg->model_number);
+	p2p->cfg->model_number = NULL;
+	if (model_number) {
+		p2p->cfg->model_number = os_strdup(model_number);
+		if (p2p->cfg->model_number == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number)
+{
+	os_free(p2p->cfg->serial_number);
+	p2p->cfg->serial_number = NULL;
+	if (serial_number) {
+		p2p->cfg->serial_number = os_strdup(serial_number);
+		if (p2p->cfg->serial_number == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods)
+{
+	p2p->cfg->config_methods = config_methods;
+}
+
+
+void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid)
+{
+	os_memcpy(p2p->cfg->uuid, uuid, 16);
+}
+
+
+int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type)
+{
+	os_memcpy(p2p->cfg->pri_dev_type, pri_dev_type, 8);
+	return 0;
+}
+
+
+int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
+			  size_t num_dev_types)
+{
+	if (num_dev_types > P2P_SEC_DEVICE_TYPES)
+		num_dev_types = P2P_SEC_DEVICE_TYPES;
+	p2p->cfg->num_sec_dev_types = num_dev_types;
+	os_memcpy(p2p->cfg->sec_dev_type, dev_types, num_dev_types * 8);
+	return 0;
+}
+
+
+void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p)
+{
+	int i;
+
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		wpabuf_free(p2p->wps_vendor_ext[i]);
+		p2p->wps_vendor_ext[i] = NULL;
+	}
+}
+
+
+int p2p_add_wps_vendor_extension(struct p2p_data *p2p,
+				 const struct wpabuf *vendor_ext)
+{
+	int i;
+
+	if (vendor_ext == NULL)
+		return -1;
+
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		if (p2p->wps_vendor_ext[i] == NULL)
+			break;
+	}
+	if (i >= P2P_MAX_WPS_VENDOR_EXT)
+		return -1;
+
+	p2p->wps_vendor_ext[i] = wpabuf_dup(vendor_ext);
+	if (p2p->wps_vendor_ext[i] == NULL)
+		return -1;
+
+	return 0;
+}
+
+
+int p2p_set_country(struct p2p_data *p2p, const char *country)
+{
+	os_memcpy(p2p->cfg->country, country, 3);
+	return 0;
+}
+
+
+void p2p_continue_find(struct p2p_data *p2p)
+{
+	struct p2p_device *dev;
+	p2p_set_state(p2p, P2P_SEARCH);
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (dev->flags & P2P_DEV_SD_SCHEDULE) {
+			if (p2p_start_sd(p2p, dev) == 0)
+				return;
+			else
+				break;
+		} else if (dev->req_config_methods &&
+			   !(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send "
+				"pending Provisioning Discovery Request to "
+				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)
+				return;
+		}
+	}
+
+	p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_sd_cb(struct p2p_data *p2p, int success)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Service Discovery Query TX callback: success=%d",
+		success);
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+	if (!success) {
+		if (p2p->sd_peer) {
+			p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+			p2p->sd_peer = NULL;
+		}
+		p2p_continue_find(p2p);
+		return;
+	}
+
+	if (p2p->sd_peer == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No SD peer entry known");
+		p2p_continue_find(p2p);
+		return;
+	}
+
+	/* Wait for response from the peer */
+	p2p_set_state(p2p, P2P_SD_DURING_FIND);
+	p2p_set_timeout(p2p, 0, 200000);
+}
+
+static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Provision Discovery Request TX callback: success=%d",
+		success);
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+	if (!success) {
+		if (p2p->state != P2P_IDLE)
+			p2p_continue_find(p2p);
+		return;
+	}
+
+	/* Wait for response from the peer */
+	if (p2p->state == P2P_SEARCH)
+		p2p_set_state(p2p, P2P_PD_DURING_FIND);
+	p2p_set_timeout(p2p, 0, 200000);
+}
+
+
+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);
+
+	if (p2p->go_neg_peer && p2p->state == P2P_SEARCH &&
+	    os_memcmp(p2p->go_neg_peer->info.p2p_device_addr, bssid, ETH_ALEN)
+	    == 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Found GO Negotiation peer - try to start GO "
+			"negotiation");
+		p2p_connect_send(p2p, p2p->go_neg_peer);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+void p2p_scan_res_handled(struct p2p_data *p2p)
+{
+	if (!p2p->p2p_scan_running) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan was not "
+			"running, but scan results received");
+	}
+	p2p->p2p_scan_running = 0;
+	eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+
+	if (p2p_run_after_scan(p2p))
+		return;
+	if (p2p->state == P2P_SEARCH)
+		p2p_continue_find(p2p);
+}
+
+
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies)
+{
+	u8 *len = p2p_buf_add_ie_hdr(ies);
+	p2p_buf_add_capability(ies, p2p->dev_capab, 0);
+	if (p2p->cfg->reg_class && p2p->cfg->channel)
+		p2p_buf_add_listen_channel(ies, p2p->cfg->country,
+					   p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+	if (p2p->ext_listen_interval)
+		p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period,
+					      p2p->ext_listen_interval);
+	/* TODO: p2p_buf_add_operating_channel() if GO */
+	p2p_buf_update_ie_hdr(ies, len);
+}
+
+
+int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end)
+{
+	return p2p_attr_text(p2p_ie, buf, end);
+}
+
+
+static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success)
+{
+	struct p2p_device *dev = p2p->go_neg_peer;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: GO Negotiation Request TX callback: success=%d",
+		success);
+
+	if (dev == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No pending GO Negotiation");
+		return;
+	}
+
+	if (success) {
+		dev->go_neg_req_sent++;
+		if (dev->flags & P2P_DEV_USER_REJECTED) {
+			p2p_set_state(p2p, P2P_IDLE);
+			return;
+		}
+	}
+
+	if (!success &&
+	    (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) &&
+	    !is_zero_ether_addr(dev->member_in_go_dev)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Peer " MACSTR " did not acknowledge request - "
+			"try to use device discoverability through its GO",
+			MAC2STR(dev->info.p2p_device_addr));
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		p2p_send_dev_disc_req(p2p, dev);
+		return;
+	}
+
+	/*
+	 * Use P2P find, if needed, to find the other device from its listen
+	 * channel.
+	 */
+	p2p_set_state(p2p, P2P_CONNECT);
+	p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: GO Negotiation Response TX callback: success=%d",
+		success);
+	if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Ignore TX callback event - GO Negotiation is "
+			"not running anymore");
+		return;
+	}
+	p2p_set_state(p2p, P2P_CONNECT);
+	p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: GO Negotiation Response (failure) TX callback: "
+		"success=%d", success);
+	if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) {
+		p2p_go_neg_failed(p2p, p2p->go_neg_peer,
+				  p2p->go_neg_peer->status);
+	}
+}
+
+
+static void p2p_go_neg_conf_cb(struct p2p_data *p2p,
+			       enum p2p_send_action_result result)
+{
+	struct p2p_device *dev;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: GO Negotiation Confirm TX callback: result=%d",
+		result);
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	if (result == P2P_SEND_ACTION_FAILED) {
+		p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+		return;
+	}
+	if (result == P2P_SEND_ACTION_NO_ACK) {
+		/*
+		 * It looks like the TX status for GO Negotiation Confirm is
+		 * often showing failure even when the peer has actually
+		 * received the frame. Since the peer may change channels
+		 * immediately after having received the frame, we may not see
+		 * an Ack for retries, so just dropping a single frame may
+		 * trigger this. To allow the group formation to succeed if the
+		 * peer did indeed receive the frame, continue regardless of
+		 * the TX status.
+		 */
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Assume GO Negotiation Confirm TX was actually "
+			"received by the peer even though Ack was not "
+			"reported");
+	}
+
+	dev = p2p->go_neg_peer;
+	if (dev == NULL)
+		return;
+
+	p2p_go_complete(p2p, dev);
+}
+
+
+void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+			const u8 *src, const u8 *bssid,
+			enum p2p_send_action_result result)
+{
+	enum p2p_pending_action_state state;
+	int success;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Action frame TX callback (state=%d freq=%u dst=" MACSTR
+		" src=" MACSTR " bssid=" MACSTR " result=%d",
+		p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
+		MAC2STR(bssid), result);
+	success = result == P2P_SEND_ACTION_SUCCESS;
+	state = p2p->pending_action_state;
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	switch (state) {
+	case P2P_NO_PENDING_ACTION:
+		break;
+	case P2P_PENDING_GO_NEG_REQUEST:
+		p2p_go_neg_req_cb(p2p, success);
+		break;
+	case P2P_PENDING_GO_NEG_RESPONSE:
+		p2p_go_neg_resp_cb(p2p, success);
+		break;
+	case P2P_PENDING_GO_NEG_RESPONSE_FAILURE:
+		p2p_go_neg_resp_failure_cb(p2p, success);
+		break;
+	case P2P_PENDING_GO_NEG_CONFIRM:
+		p2p_go_neg_conf_cb(p2p, result);
+		break;
+	case P2P_PENDING_SD:
+		p2p_sd_cb(p2p, success);
+		break;
+	case P2P_PENDING_PD:
+		p2p_prov_disc_cb(p2p, success);
+		break;
+	case P2P_PENDING_INVITATION_REQUEST:
+		p2p_invitation_req_cb(p2p, success);
+		break;
+	case P2P_PENDING_INVITATION_RESPONSE:
+		p2p_invitation_resp_cb(p2p, success);
+		break;
+	case P2P_PENDING_DEV_DISC_REQUEST:
+		p2p_dev_disc_req_cb(p2p, success);
+		break;
+	case P2P_PENDING_DEV_DISC_RESPONSE:
+		p2p_dev_disc_resp_cb(p2p, success);
+		break;
+	case P2P_PENDING_GO_DISC_REQ:
+		p2p_go_disc_req_cb(p2p, success);
+		break;
+	}
+}
+
+
+void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
+		   unsigned int duration)
+{
+	if (freq == p2p->pending_client_disc_freq) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Client discoverability remain-awake completed");
+		p2p->pending_client_disc_freq = 0;
+		return;
+	}
+
+	if (freq != p2p->pending_listen_freq) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unexpected listen callback for freq=%u "
+			"duration=%u (pending_listen_freq=%u)",
+			freq, duration, p2p->pending_listen_freq);
+		return;
+	}
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Starting Listen timeout(%u,%u) on freq=%u based on "
+		"callback",
+		p2p->pending_listen_sec, p2p->pending_listen_usec,
+		p2p->pending_listen_freq);
+	p2p->in_listen = 1;
+	p2p->drv_in_listen = freq;
+	if (p2p->pending_listen_sec || p2p->pending_listen_usec) {
+		/*
+		 * Add 20 msec extra wait to avoid race condition with driver
+		 * remain-on-channel end event, i.e., give driver more time to
+		 * complete the operation before our timeout expires.
+		 */
+		p2p_set_timeout(p2p, p2p->pending_listen_sec,
+				p2p->pending_listen_usec + 20000);
+	}
+
+	p2p->pending_listen_freq = 0;
+}
+
+
+int p2p_listen_end(struct p2p_data *p2p, unsigned int freq)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver ended Listen "
+		"state (freq=%u)", freq);
+	p2p->drv_in_listen = 0;
+	if (p2p->in_listen)
+		return 0; /* Internal timeout will trigger the next step */
+
+	if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) {
+		if (p2p->go_neg_peer->connect_reqs >= 120) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Timeout on sending GO Negotiation "
+				"Request without getting response");
+			p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+			return 0;
+		}
+
+		p2p_set_state(p2p, P2P_CONNECT);
+		p2p_connect_send(p2p, p2p->go_neg_peer);
+		return 1;
+	} else if (p2p->state == P2P_SEARCH) {
+		p2p_search(p2p);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static void p2p_timeout_connect(struct p2p_data *p2p)
+{
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+	p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_timeout_connect_listen(struct p2p_data *p2p)
+{
+	if (p2p->go_neg_peer) {
+		if (p2p->drv_in_listen) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is "
+				"still in Listen state; wait for it to "
+				"complete");
+			return;
+		}
+
+		if (p2p->go_neg_peer->connect_reqs >= 120) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Timeout on sending GO Negotiation "
+				"Request without getting response");
+			p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+			return;
+		}
+
+		p2p_set_state(p2p, P2P_CONNECT);
+		p2p_connect_send(p2p, p2p->go_neg_peer);
+	} else
+		p2p_set_state(p2p, P2P_IDLE);
+}
+
+
+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, 1, 0);
+}
+
+
+static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p)
+{
+	struct p2p_device *dev = p2p->go_neg_peer;
+
+	if (dev == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unknown GO Neg peer - stop GO Neg wait");
+		return;
+	}
+
+	dev->wait_count++;
+	if (dev->wait_count >= 120) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Timeout on waiting peer to become ready for GO "
+			"Negotiation");
+		p2p_go_neg_failed(p2p, dev, -1);
+		return;
+	}
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Go to Listen state while waiting for the peer to become "
+		"ready for GO Negotiation");
+	p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
+	p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_timeout_sd_during_find(struct p2p_data *p2p)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Service Discovery Query timeout");
+	if (p2p->sd_peer) {
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+		p2p->sd_peer = NULL;
+	}
+	p2p_continue_find(p2p);
+}
+
+
+static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Provision Discovery Request timeout");
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	p2p_continue_find(p2p);
+}
+
+
+static void p2p_timeout_invite(struct p2p_data *p2p)
+{
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	p2p_set_state(p2p, P2P_INVITE_LISTEN);
+	if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) {
+		/*
+		 * Better remain on operating channel instead of listen channel
+		 * when running a group.
+		 */
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Inviting in "
+			"active GO role - wait on operating channel");
+		p2p_set_timeout(p2p, 0, 100000);
+		return;
+	}
+	p2p_listen_in_find(p2p);
+}
+
+
+static void p2p_timeout_invite_listen(struct p2p_data *p2p)
+{
+	if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) {
+		p2p_set_state(p2p, P2P_INVITE);
+		p2p_invite_send(p2p, p2p->invite_peer,
+				p2p->invite_go_dev_addr);
+	} else {
+		if (p2p->invite_peer) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Invitation Request retry limit reached");
+			if (p2p->cfg->invitation_result)
+				p2p->cfg->invitation_result(
+					p2p->cfg->cb_ctx, -1, NULL);
+		}
+		p2p_set_state(p2p, P2P_IDLE);
+	}
+}
+
+
+static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Timeout (state=%s)",
+		p2p_state_txt(p2p->state));
+
+	p2p->in_listen = 0;
+
+	switch (p2p->state) {
+	case P2P_IDLE:
+		break;
+	case P2P_SEARCH:
+		p2p_search(p2p);
+		break;
+	case P2P_CONNECT:
+		p2p_timeout_connect(p2p);
+		break;
+	case P2P_CONNECT_LISTEN:
+		p2p_timeout_connect_listen(p2p);
+		break;
+	case P2P_GO_NEG:
+		break;
+	case P2P_LISTEN_ONLY:
+		if (p2p->ext_listen_only) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Extended Listen Timing - Listen State "
+				"completed");
+			p2p->ext_listen_only = 0;
+			p2p_set_state(p2p, P2P_IDLE);
+		}
+		break;
+	case P2P_WAIT_PEER_CONNECT:
+		p2p_timeout_wait_peer_connect(p2p);
+		break;
+	case P2P_WAIT_PEER_IDLE:
+		p2p_timeout_wait_peer_idle(p2p);
+		break;
+	case P2P_SD_DURING_FIND:
+		p2p_timeout_sd_during_find(p2p);
+		break;
+	case P2P_PROVISIONING:
+		break;
+	case P2P_PD_DURING_FIND:
+		p2p_timeout_prov_disc_during_find(p2p);
+		break;
+	case P2P_INVITE:
+		p2p_timeout_invite(p2p);
+		break;
+	case P2P_INVITE_LISTEN:
+		p2p_timeout_invite_listen(p2p);
+		break;
+	}
+}
+
+
+int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr)
+{
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, peer_addr);
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Local request to reject "
+		"connection attempts by peer " MACSTR, MAC2STR(peer_addr));
+	if (dev == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+			" unknown", MAC2STR(peer_addr));
+		return -1;
+	}
+	dev->status = P2P_SC_FAIL_REJECTED_BY_USER;
+	dev->flags |= P2P_DEV_USER_REJECTED;
+	return 0;
+}
+
+
+static const char * p2p_wps_method_text(enum p2p_wps_method method)
+{
+	switch (method) {
+	case WPS_NOT_READY:
+		return "not-ready";
+	case WPS_PIN_LABEL:
+		return "Label";
+	case WPS_PIN_DISPLAY:
+		return "Display";
+	case WPS_PIN_KEYPAD:
+		return "Keypad";
+	case WPS_PBC:
+		return "PBC";
+	}
+
+	return "??";
+}
+
+
+static const char * p2p_go_state_text(enum p2p_go_state go_state)
+{
+	switch (go_state) {
+	case UNKNOWN_GO:
+		return "unknown";
+	case LOCAL_GO:
+		return "local";
+	case  REMOTE_GO:
+		return "remote";
+	}
+
+	return "??";
+}
+
+
+int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
+		      char *buf, size_t buflen)
+{
+	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);
+	else
+		dev = dl_list_first(&p2p->devices, struct p2p_device, list);
+
+	if (dev && next) {
+		dev = dl_list_first(&dev->list, struct p2p_device, list);
+		if (&dev->list == &p2p->devices)
+			dev = NULL;
+	}
+
+	if (dev == NULL)
+		return -1;
+
+	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"
+			  "intended_addr=" MACSTR "\n"
+			  "country=%c%c\n"
+			  "oper_freq=%d\n"
+			  "req_config_methods=0x%x\n"
+			  "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
+			  "status=%d\n"
+			  "wait_count=%u\n"
+			  "invitation_reqs=%u\n",
+			  (int) (now.sec - dev->last_seen.sec),
+			  dev->listen_freq,
+			  dev->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,
+			  MAC2STR(dev->intended_addr),
+			  dev->country[0] ? dev->country[0] : '_',
+			  dev->country[1] ? dev->country[1] : '_',
+			  dev->oper_freq,
+			  dev->req_config_methods,
+			  dev->flags & P2P_DEV_PROBE_REQ_ONLY ?
+			  "[PROBE_REQ_ONLY]" : "",
+			  dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "",
+			  dev->flags & P2P_DEV_NOT_YET_READY ?
+			  "[NOT_YET_READY]" : "",
+			  dev->flags & P2P_DEV_SD_INFO ? "[SD_INFO]" : "",
+			  dev->flags & P2P_DEV_SD_SCHEDULE ? "[SD_SCHEDULE]" :
+			  "",
+			  dev->flags & P2P_DEV_PD_PEER_DISPLAY ?
+			  "[PD_PEER_DISPLAY]" : "",
+			  dev->flags & P2P_DEV_PD_PEER_KEYPAD ?
+			  "[PD_PEER_KEYPAD]" : "",
+			  dev->flags & P2P_DEV_USER_REJECTED ?
+			  "[USER_REJECTED]" : "",
+			  dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ?
+			  "[PEER_WAITING_RESPONSE]" : "",
+			  dev->flags & P2P_DEV_PREFER_PERSISTENT_GROUP ?
+			  "[PREFER_PERSISTENT_GROUP]" : "",
+			  dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE ?
+			  "[WAIT_GO_NEG_RESPONSE]" : "",
+			  dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM ?
+			  "[WAIT_GO_NEG_CONFIRM]" : "",
+			  dev->flags & P2P_DEV_GROUP_CLIENT_ONLY ?
+			  "[GROUP_CLIENT_ONLY]" : "",
+			  dev->flags & P2P_DEV_FORCE_FREQ ?
+			  "[FORCE_FREQ]" : "",
+			  dev->flags & P2P_DEV_PD_FOR_JOIN ?
+			  "[PD_FOR_JOIN]" : "",
+			  dev->status,
+			  dev->wait_count,
+			  dev->invitation_reqs);
+	if (res < 0 || res >= end - pos)
+		return pos - buf;
+	pos += res;
+
+	if (dev->ext_listen_period) {
+		res = os_snprintf(pos, end - pos,
+				  "ext_listen_period=%u\n"
+				  "ext_listen_interval=%u\n",
+				  dev->ext_listen_period,
+				  dev->ext_listen_interval);
+		if (res < 0 || res >= end - pos)
+			return pos - buf;
+		pos += res;
+	}
+
+	if (dev->oper_ssid_len) {
+		res = os_snprintf(pos, end - pos,
+				  "oper_ssid=%s\n",
+				  wpa_ssid_txt(dev->oper_ssid,
+					       dev->oper_ssid_len));
+		if (res < 0 || res >= end - pos)
+			return pos - buf;
+		pos += res;
+	}
+
+	return pos - buf;
+}
+
+
+void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled)
+{
+	if (enabled) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client "
+			"discoverability enabled");
+		p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+	} else {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client "
+			"discoverability disabled");
+		p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+	}
+}
+
+
+static struct wpabuf * p2p_build_presence_req(u32 duration1, u32 interval1,
+					      u32 duration2, u32 interval2)
+{
+	struct wpabuf *req;
+	struct p2p_noa_desc desc1, desc2, *ptr1 = NULL, *ptr2 = NULL;
+	u8 *len;
+
+	req = wpabuf_alloc(100);
+	if (req == NULL)
+		return NULL;
+
+	if (duration1 || interval1) {
+		os_memset(&desc1, 0, sizeof(desc1));
+		desc1.count_type = 1;
+		desc1.duration = duration1;
+		desc1.interval = interval1;
+		ptr1 = &desc1;
+
+		if (duration2 || interval2) {
+			os_memset(&desc2, 0, sizeof(desc2));
+			desc2.count_type = 2;
+			desc2.duration = duration2;
+			desc2.interval = interval2;
+			ptr2 = &desc2;
+		}
+	}
+
+	p2p_buf_add_action_hdr(req, P2P_PRESENCE_REQ, 1);
+	len = p2p_buf_add_ie_hdr(req);
+	p2p_buf_add_noa(req, 0, 0, 0, ptr1, ptr2);
+	p2p_buf_update_ie_hdr(req, len);
+
+	return req;
+}
+
+
+int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
+		     const u8 *own_interface_addr, unsigned int freq,
+		     u32 duration1, u32 interval1, u32 duration2,
+		     u32 interval2)
+{
+	struct wpabuf *req;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send Presence Request to "
+		"GO " MACSTR " (own interface " MACSTR ") freq=%u dur1=%u "
+		"int1=%u dur2=%u int2=%u",
+		MAC2STR(go_interface_addr), MAC2STR(own_interface_addr),
+		freq, duration1, interval1, duration2, interval2);
+
+	req = p2p_build_presence_req(duration1, interval1, duration2,
+				     interval2);
+	if (req == NULL)
+		return -1;
+
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr,
+			    go_interface_addr,
+			    wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+	}
+	wpabuf_free(req);
+
+	return 0;
+}
+
+
+static struct wpabuf * p2p_build_presence_resp(u8 status, const u8 *noa,
+					       size_t noa_len, u8 dialog_token)
+{
+	struct wpabuf *resp;
+	u8 *len;
+
+	resp = wpabuf_alloc(100 + noa_len);
+	if (resp == NULL)
+		return NULL;
+
+	p2p_buf_add_action_hdr(resp, P2P_PRESENCE_RESP, dialog_token);
+	len = p2p_buf_add_ie_hdr(resp);
+	p2p_buf_add_status(resp, status);
+	if (noa) {
+		wpabuf_put_u8(resp, P2P_ATTR_NOTICE_OF_ABSENCE);
+		wpabuf_put_le16(resp, noa_len);
+		wpabuf_put_data(resp, noa, noa_len);
+	} else
+		p2p_buf_add_noa(resp, 0, 0, 0, NULL, NULL);
+	p2p_buf_update_ie_hdr(resp, len);
+
+	return resp;
+}
+
+
+static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
+				     const u8 *sa, const u8 *data, size_t len,
+				     int rx_freq)
+{
+	struct p2p_message msg;
+	u8 status;
+	struct wpabuf *resp;
+	size_t g;
+	struct p2p_group *group = NULL;
+	int parsed = 0;
+	u8 noa[50];
+	int noa_len;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received P2P Action - P2P Presence Request");
+
+	for (g = 0; g < p2p->num_groups; g++) {
+		if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]),
+			      ETH_ALEN) == 0) {
+			group = p2p->groups[g];
+			break;
+		}
+	}
+	if (group == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Ignore P2P Presence Request for unknown group "
+			MACSTR, MAC2STR(da));
+		return;
+	}
+
+	if (p2p_parse(data, len, &msg) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to parse P2P Presence Request");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+	parsed = 1;
+
+	if (msg.noa == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No NoA attribute in P2P Presence Request");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+
+	status = p2p_group_presence_req(group, sa, msg.noa, msg.noa_len);
+
+fail:
+	if (p2p->cfg->get_noa)
+		noa_len = p2p->cfg->get_noa(p2p->cfg->cb_ctx, da, noa,
+					    sizeof(noa));
+	else
+		noa_len = -1;
+	resp = p2p_build_presence_resp(status, noa_len > 0 ? noa : NULL,
+				       noa_len > 0 ? noa_len : 0,
+				       msg.dialog_token);
+	if (parsed)
+		p2p_parse_free(&msg);
+	if (resp == NULL)
+		return;
+
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	if (p2p_send_action(p2p, rx_freq, sa, da, da,
+			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+	}
+	wpabuf_free(resp);
+}
+
+
+static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
+				      const u8 *sa, const u8 *data, size_t len)
+{
+	struct p2p_message msg;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received P2P Action - P2P Presence Response");
+
+	if (p2p_parse(data, len, &msg) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to parse P2P Presence Response");
+		return;
+	}
+
+	if (msg.status == NULL || msg.noa == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Status or NoA attribute in P2P Presence "
+			"Response");
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (*msg.status) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: P2P Presence Request was rejected: status %u",
+			*msg.status);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: P2P Presence Request was accepted");
+	wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA",
+		    msg.noa, msg.noa_len);
+	/* TODO: process NoA */
+	p2p_parse_free(&msg);
+}
+
+
+static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+
+	if (p2p->ext_listen_interval) {
+		/* Schedule next extended listen timeout */
+		eloop_register_timeout(p2p->ext_listen_interval_sec,
+				       p2p->ext_listen_interval_usec,
+				       p2p_ext_listen_timeout, p2p, NULL);
+	}
+
+	if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) {
+		/*
+		 * This should not really happen, but it looks like the Listen
+		 * command may fail is something else (e.g., a scan) was
+		 * running at an inconvenient time. As a workaround, allow new
+		 * Extended Listen operation to be started.
+		 */
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Previous "
+			"Extended Listen operation had not been completed - "
+			"try again");
+		p2p->ext_listen_only = 0;
+		p2p_set_state(p2p, P2P_IDLE);
+	}
+
+	if (p2p->state != P2P_IDLE) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip Extended "
+			"Listen timeout in active state (%s)",
+			p2p_state_txt(p2p->state));
+		return;
+	}
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Extended Listen timeout");
+	p2p->ext_listen_only = 1;
+	if (p2p_listen(p2p, p2p->ext_listen_period) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start "
+			"Listen state for Extended Listen Timing");
+		p2p->ext_listen_only = 0;
+	}
+}
+
+
+int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
+		   unsigned int interval)
+{
+	if (period > 65535 || interval > 65535 || period > interval ||
+	    (period == 0 && interval > 0) || (period > 0 && interval == 0)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Invalid Extended Listen Timing request: "
+			"period=%u interval=%u", period, interval);
+		return -1;
+	}
+
+	eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
+
+	if (interval == 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Disabling Extended Listen Timing");
+		p2p->ext_listen_period = 0;
+		p2p->ext_listen_interval = 0;
+		return 0;
+	}
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Enabling Extended Listen Timing: period %u msec, "
+		"interval %u msec", period, interval);
+	p2p->ext_listen_period = period;
+	p2p->ext_listen_interval = interval;
+	p2p->ext_listen_interval_sec = interval / 1000;
+	p2p->ext_listen_interval_usec = (interval % 1000) * 1000;
+
+	eloop_register_timeout(p2p->ext_listen_interval_sec,
+			       p2p->ext_listen_interval_usec,
+			       p2p_ext_listen_timeout, p2p, NULL);
+
+	return 0;
+}
+
+
+void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+		      const u8 *ie, size_t ie_len)
+{
+	struct p2p_message msg;
+
+	if (bssid == NULL || ie == NULL)
+		return;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_ies(ie, ie_len, &msg))
+		return;
+	if (msg.minor_reason_code == NULL)
+		return;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+		"P2P: Deauthentication notification BSSID " MACSTR
+		" reason_code=%u minor_reason_code=%u",
+		MAC2STR(bssid), reason_code, *msg.minor_reason_code);
+
+	p2p_parse_free(&msg);
+}
+
+
+void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+			const u8 *ie, size_t ie_len)
+{
+	struct p2p_message msg;
+
+	if (bssid == NULL || ie == NULL)
+		return;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_ies(ie, ie_len, &msg))
+		return;
+	if (msg.minor_reason_code == NULL)
+		return;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+		"P2P: Disassociation notification BSSID " MACSTR
+		" reason_code=%u minor_reason_code=%u",
+		MAC2STR(bssid), reason_code, *msg.minor_reason_code);
+
+	p2p_parse_free(&msg);
+}
+
+
+void p2p_set_managed_oper(struct p2p_data *p2p, int enabled)
+{
+	if (enabled) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P "
+			"Device operations enabled");
+		p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED;
+	} else {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P "
+			"Device operations disabled");
+		p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED;
+	}
+}
+
+
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel)
+{
+	if (p2p_channel_to_freq(p2p->cfg->country, reg_class, channel) < 0)
+		return -1;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set Listen channel: "
+		"reg_class %u channel %u", reg_class, channel);
+	p2p->cfg->reg_class = reg_class;
+	p2p->cfg->channel = channel;
+
+	return 0;
+}
+
+
+int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len)
+{
+	wpa_hexdump_ascii(MSG_DEBUG, "P2P: New SSID postfix", postfix, len);
+	if (postfix == NULL) {
+		p2p->cfg->ssid_postfix_len = 0;
+		return 0;
+	}
+	if (len > sizeof(p2p->cfg->ssid_postfix))
+		return -1;
+	os_memcpy(p2p->cfg->ssid_postfix, postfix, len);
+	p2p->cfg->ssid_postfix_len = len;
+	return 0;
+}
+
+
+int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
+			   u8 *iface_addr)
+{
+	struct p2p_device *dev = p2p_get_device(p2p, dev_addr);
+	if (dev == NULL || is_zero_ether_addr(dev->interface_addr))
+		return -1;
+	os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN);
+	return 0;
+}
+
+
+int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
+			   u8 *dev_addr)
+{
+	struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
+	if (dev == NULL)
+		return -1;
+	os_memcpy(dev_addr, dev->info.p2p_device_addr, ETH_ALEN);
+	return 0;
+}
+
+
+void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr)
+{
+	os_memcpy(p2p->peer_filter, addr, ETH_ALEN);
+	if (is_zero_ether_addr(p2p->peer_filter))
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Disable peer "
+			"filter");
+	else
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Enable peer "
+			"filter for " MACSTR, MAC2STR(p2p->peer_filter));
+}
+
+
+void p2p_set_cross_connect(struct p2p_data *p2p, int enabled)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Cross connection %s",
+		enabled ? "enabled" : "disabled");
+	if (p2p->cross_connect == enabled)
+		return;
+	p2p->cross_connect = enabled;
+	/* TODO: may need to tear down any action group where we are GO(?) */
+}
+
+
+int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr)
+{
+	struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
+	if (dev == NULL)
+		return -1;
+	if (dev->oper_freq <= 0)
+		return -1;
+	return dev->oper_freq;
+}
+
+
+void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Intra BSS distribution %s",
+		enabled ? "enabled" : "disabled");
+	p2p->cfg->p2p_intra_bss = enabled;
+}
+
+
+void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update channel list");
+	os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels));
+}
+
+
+int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+		    const u8 *src, const u8 *bssid, const u8 *buf,
+		    size_t len, unsigned int wait_time)
+{
+	if (p2p->p2p_scan_running) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay Action "
+			"frame TX until p2p_scan completes");
+		if (p2p->after_scan_tx) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped "
+				"previous pending Action frame TX");
+			os_free(p2p->after_scan_tx);
+		}
+		p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) +
+					       len);
+		if (p2p->after_scan_tx == NULL)
+			return -1;
+		p2p->after_scan_tx->freq = freq;
+		os_memcpy(p2p->after_scan_tx->dst, dst, ETH_ALEN);
+		os_memcpy(p2p->after_scan_tx->src, src, ETH_ALEN);
+		os_memcpy(p2p->after_scan_tx->bssid, bssid, ETH_ALEN);
+		p2p->after_scan_tx->len = len;
+		p2p->after_scan_tx->wait_time = wait_time;
+		os_memcpy(p2p->after_scan_tx + 1, buf, len);
+		return 0;
+	}
+
+	return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid,
+				     buf, len, wait_time);
+}
+
+
+void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5,
+			   int freq_overall)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Best channel: 2.4 GHz: %d,"
+		"  5 GHz: %d,  overall: %d", freq_24, freq_5, freq_overall);
+	p2p->best_freq_24 = freq_24;
+	p2p->best_freq_5 = freq_5;
+	p2p->best_freq_overall = freq_overall;
+}
+
+
+const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p)
+{
+	if (p2p == NULL || p2p->go_neg_peer == NULL)
+		return NULL;
+	return p2p->go_neg_peer->info.p2p_device_addr;
+}
+
+
+const struct p2p_peer_info *
+p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next)
+{
+	struct p2p_device *dev;
+
+	if (addr) {
+		dev = p2p_get_device(p2p, addr);
+		if (!dev)
+			return NULL;
+
+		if (!next) {
+			if (dev->flags & P2P_DEV_PROBE_REQ_ONLY)
+				return NULL;
+
+			return &dev->info;
+		} else {
+			do {
+				dev = dl_list_first(&dev->list,
+						    struct p2p_device,
+						    list);
+				if (&dev->list == &p2p->devices)
+					return NULL;
+			} while (dev->flags & P2P_DEV_PROBE_REQ_ONLY);
+		}
+	} else {
+		dev = dl_list_first(&p2p->devices, struct p2p_device, list);
+		if (!dev)
+			return NULL;
+		while (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
+			dev = dl_list_first(&dev->list,
+					    struct p2p_device,
+					    list);
+			if (&dev->list == &p2p->devices)
+				return NULL;
+		}
+	}
+
+	return &dev->info;
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
new file mode 100644
index 0000000..954f562
--- /dev/null
+++ b/src/p2p/p2p.h
@@ -0,0 +1,1473 @@
+/*
+ * 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.
+ */
+
+#ifndef P2P_H
+#define P2P_H
+
+/**
+ * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
+ */
+#define P2P_MAX_REG_CLASSES 10
+
+/**
+ * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class
+ */
+#define P2P_MAX_REG_CLASS_CHANNELS 20
+
+/**
+ * struct p2p_channels - List of supported channels
+ */
+struct p2p_channels {
+	/**
+	 * struct p2p_reg_class - Supported regulatory class
+	 */
+	struct p2p_reg_class {
+		/**
+		 * reg_class - Regulatory class (IEEE 802.11-2007, Annex J)
+		 */
+		u8 reg_class;
+
+		/**
+		 * channel - Supported channels
+		 */
+		u8 channel[P2P_MAX_REG_CLASS_CHANNELS];
+
+		/**
+		 * channels - Number of channel entries in use
+		 */
+		size_t channels;
+	} reg_class[P2P_MAX_REG_CLASSES];
+
+	/**
+	 * reg_classes - Number of reg_class entries in use
+	 */
+	size_t reg_classes;
+};
+
+enum p2p_wps_method {
+	WPS_NOT_READY, WPS_PIN_LABEL, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC
+};
+
+/**
+ * struct p2p_go_neg_results - P2P Group Owner Negotiation results
+ */
+struct p2p_go_neg_results {
+	/**
+	 * status - Negotiation result (Status Code)
+	 *
+	 * 0 (P2P_SC_SUCCESS) indicates success. Non-zero values indicate
+	 * failed negotiation.
+	 */
+	int status;
+
+	/**
+	 * role_go - Whether local end is Group Owner
+	 */
+	int role_go;
+
+	/**
+	 * freq - Frequency of the group operational channel in MHz
+	 */
+	int freq;
+
+	/**
+	 * ssid - SSID of the group
+	 */
+	u8 ssid[32];
+
+	/**
+	 * ssid_len - Length of SSID in octets
+	 */
+	size_t ssid_len;
+
+	/**
+	 * passphrase - WPA2-Personal passphrase for the group (GO only)
+	 */
+	char passphrase[64];
+
+	/**
+	 * peer_device_addr - P2P Device Address of the peer
+	 */
+	u8 peer_device_addr[ETH_ALEN];
+
+	/**
+	 * peer_interface_addr - P2P Interface Address of the peer
+	 */
+	u8 peer_interface_addr[ETH_ALEN];
+
+	/**
+	 * wps_method - WPS method to be used during provisioning
+	 */
+	enum p2p_wps_method wps_method;
+
+#define P2P_MAX_CHANNELS 50
+
+	/**
+	 * freq_list - Zero-terminated list of possible operational channels
+	 */
+	int freq_list[P2P_MAX_CHANNELS];
+
+	/**
+	 * persistent_group - Whether the group should be made persistent
+	 */
+	int persistent_group;
+
+	/**
+	 * peer_config_timeout - Peer configuration timeout (in 10 msec units)
+	 */
+	unsigned int peer_config_timeout;
+};
+
+struct p2p_data;
+
+enum p2p_scan_type {
+	P2P_SCAN_SOCIAL,
+	P2P_SCAN_FULL,
+	P2P_SCAN_SPECIFIC,
+	P2P_SCAN_SOCIAL_PLUS_ONE
+};
+
+#define P2P_MAX_WPS_VENDOR_EXT 10
+
+/**
+ * struct p2p_peer_info - P2P peer information
+ */
+struct p2p_peer_info {
+	/**
+	 * p2p_device_addr - P2P Device Address of the peer
+	 */
+	u8 p2p_device_addr[ETH_ALEN];
+
+	/**
+	 * pri_dev_type - Primary Device Type
+	 */
+	u8 pri_dev_type[8];
+
+	/**
+	 * device_name - Device Name (0..32 octets encoded in UTF-8)
+	 */
+	char device_name[33];
+
+	/**
+	 * manufacturer - Manufacturer (0..64 octets encoded in UTF-8)
+	 */
+	char manufacturer[65];
+
+	/**
+	 * model_name - Model Name (0..32 octets encoded in UTF-8)
+	 */
+	char model_name[33];
+
+	/**
+	 * model_number - Model Number (0..32 octets encoded in UTF-8)
+	 */
+	char model_number[33];
+
+	/**
+	 * serial_number - Serial Number (0..32 octets encoded in UTF-8)
+	 */
+	char serial_number[33];
+
+	/**
+	 * config_methods - WPS Configuration Methods
+	 */
+	u16 config_methods;
+
+	/**
+	 * dev_capab - Device Capabilities
+	 */
+	u8 dev_capab;
+
+	/**
+	 * group_capab - Group Capabilities
+	 */
+	u8 group_capab;
+
+	/**
+	 * wps_sec_dev_type_list - WPS secondary device type list
+	 *
+	 * This list includes from 0 to 16 Secondary Device Types as indicated
+	 * by wps_sec_dev_type_list_len (8 * number of types).
+	 */
+	u8 wps_sec_dev_type_list[128];
+
+	/**
+	 * wps_sec_dev_type_list_len - Length of secondary device type list
+	 */
+	size_t wps_sec_dev_type_list_len;
+
+	struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+};
+
+/**
+ * struct p2p_config - P2P configuration
+ *
+ * This configuration is provided to the P2P module during initialization with
+ * p2p_init().
+ */
+struct p2p_config {
+	/**
+	 * country - Country code to use in P2P operations
+	 */
+	char country[3];
+
+	/**
+	 * reg_class - Regulatory class for own listen channel
+	 */
+	u8 reg_class;
+
+	/**
+	 * channel - Own listen channel
+	 */
+	u8 channel;
+
+	/**
+	 * Regulatory class for own operational channel
+	 */
+	u8 op_reg_class;
+
+	/**
+	 * op_channel - Own operational channel
+	 */
+	u8 op_channel;
+
+	/**
+	 * cfg_op_channel - Whether op_channel is hardcoded in configuration
+	 */
+	u8 cfg_op_channel;
+
+	/**
+	 * channels - Own supported regulatory classes and channels
+	 *
+	 * List of supposerted channels per regulatory class. The regulatory
+	 * classes are defined in IEEE Std 802.11-2007 Annex J and the
+	 * numbering of the clases depends on the configured country code.
+	 */
+	struct p2p_channels channels;
+
+	/**
+	 * pri_dev_type - Primary Device Type (see WPS)
+	 */
+	u8 pri_dev_type[8];
+
+	/**
+	 * P2P_SEC_DEVICE_TYPES - Maximum number of secondary device types
+	 */
+#define P2P_SEC_DEVICE_TYPES 5
+
+	/**
+	 * sec_dev_type - Optional secondary device types
+	 */
+	u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8];
+
+	/**
+	 * num_sec_dev_types - Number of sec_dev_type entries
+	 */
+	size_t num_sec_dev_types;
+
+	/**
+	 * dev_addr - P2P Device Address
+	 */
+	u8 dev_addr[ETH_ALEN];
+
+	/**
+	 * dev_name - Device Name
+	 */
+	char *dev_name;
+
+	char *manufacturer;
+	char *model_name;
+	char *model_number;
+	char *serial_number;
+
+	u8 uuid[16];
+	u16 config_methods;
+
+	/**
+	 * concurrent_operations - Whether concurrent operations are supported
+	 */
+	int concurrent_operations;
+
+	/**
+	 * max_peers - Maximum number of discovered peers to remember
+	 *
+	 * If more peers are discovered, older entries will be removed to make
+	 * room for the new ones.
+	 */
+	size_t max_peers;
+
+	/**
+	 * p2p_intra_bss - Intra BSS communication is supported
+	 */
+	int p2p_intra_bss;
+
+	/**
+	 * ssid_postfix - Postfix data to add to the SSID
+	 *
+	 * This data will be added to the end of the SSID after the
+	 * DIRECT-<random two octets> prefix.
+	 */
+	u8 ssid_postfix[32 - 9];
+
+	/**
+	 * ssid_postfix_len - Length of the ssid_postfix data
+	 */
+	size_t ssid_postfix_len;
+
+	/**
+	 * msg_ctx - Context to use with wpa_msg() calls
+	 */
+	void *msg_ctx;
+
+	/**
+	 * cb_ctx - Context to use with callback functions
+	 */
+	void *cb_ctx;
+
+
+	/* Callbacks to request lower layer driver operations */
+
+	/**
+	 * p2p_scan - Request a P2P scan/search
+	 * @ctx: Callback context from cb_ctx
+	 * @type: Scan type
+	 * @freq: Specific frequency (MHz) to scan or 0 for no restriction
+	 * @num_req_dev_types: Number of requested device types
+	 * @req_dev_types: Array containing requested device types
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This callback function is used to request a P2P scan or search
+	 * operation to be completed. Type type argument specifies which type
+	 * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the
+	 * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL
+	 * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC
+	 * request a scan of a single channel specified by freq.
+	 * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels
+	 * plus one extra channel specified by freq.
+	 *
+	 * The full scan is used for the initial scan to find group owners from
+	 * all. The other types are used during search phase scan of the social
+	 * channels (with potential variation if the Listen channel of the
+	 * target peer is known or if other channels are scanned in steps).
+	 *
+	 * The scan results are returned after this call by calling
+	 * p2p_scan_res_handler() for each scan result that has a P2P IE and
+	 * then calling p2p_scan_res_handled() to indicate that all scan
+	 * results have been indicated.
+	 */
+	int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq,
+			unsigned int num_req_dev_types,
+			const u8 *req_dev_types);
+
+	/**
+	 * send_probe_resp - Transmit a Probe Response frame
+	 * @ctx: Callback context from cb_ctx
+	 * @buf: Probe Response frame (including the header and body)
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function is used to reply to Probe Request frames that were
+	 * indicated with a call to p2p_probe_req_rx(). The response is to be
+	 * sent on the same channel or to be dropped if the driver is not
+	 * anymore listening to Probe Request frames.
+	 *
+	 * Alternatively, the responsibility for building the Probe Response
+	 * frames in Listen state may be in another system component in which
+	 * case this function need to be implemented (i.e., the function
+	 * pointer can be %NULL). The WPS and P2P IEs to be added for Probe
+	 * Response frames in such a case are available from the
+	 * start_listen() callback. It should be noted that the received Probe
+	 * Request frames must be indicated by calling p2p_probe_req_rx() even
+	 * if this send_probe_resp() is not used.
+	 */
+	int (*send_probe_resp)(void *ctx, const struct wpabuf *buf);
+
+	/**
+	 * send_action - Transmit an Action frame
+	 * @ctx: Callback context from cb_ctx
+	 * @freq: Frequency in MHz for the channel on which to transmit
+	 * @dst: Destination MAC address (Address 1)
+	 * @src: Source MAC address (Address 2)
+	 * @bssid: BSSID (Address 3)
+	 * @buf: Frame body (starting from Category field)
+	 * @len: Length of buf in octets
+	 * @wait_time: How many msec to wait for a response frame
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * The Action frame may not be transmitted immediately and the status
+	 * of the transmission must be reported by calling
+	 * p2p_send_action_cb() once the frame has either been transmitted or
+	 * it has been dropped due to excessive retries or other failure to
+	 * transmit.
+	 */
+	int (*send_action)(void *ctx, unsigned int freq, const u8 *dst,
+			   const u8 *src, const u8 *bssid, const u8 *buf,
+			   size_t len, unsigned int wait_time);
+
+	/**
+	 * send_action_done - Notify that Action frame sequence was completed
+	 * @ctx: Callback context from cb_ctx
+	 *
+	 * This function is called when the Action frame sequence that was
+	 * started with send_action() has been completed, i.e., when there is
+	 * no need to wait for a response from the destination peer anymore.
+	 */
+	void (*send_action_done)(void *ctx);
+
+	/**
+	 * start_listen - Start Listen state
+	 * @ctx: Callback context from cb_ctx
+	 * @freq: Frequency of the listen channel in MHz
+	 * @duration: Duration for the Listen state in milliseconds
+	 * @probe_resp_ie: IE(s) to be added to Probe Response frames
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This Listen state may not start immediately since the driver may
+	 * have other pending operations to complete first. Once the Listen
+	 * state has started, p2p_listen_cb() must be called to notify the P2P
+	 * module. Once the Listen state is stopped, p2p_listen_end() must be
+	 * called to notify the P2P module that the driver is not in the Listen
+	 * state anymore.
+	 *
+	 * If the send_probe_resp() is not used for generating the response,
+	 * the IEs from probe_resp_ie need to be added to the end of the Probe
+	 * Response frame body. If send_probe_resp() is used, the probe_resp_ie
+	 * information can be ignored.
+	 */
+	int (*start_listen)(void *ctx, unsigned int freq,
+			    unsigned int duration,
+			    const struct wpabuf *probe_resp_ie);
+	/**
+	 * stop_listen - Stop Listen state
+	 * @ctx: Callback context from cb_ctx
+	 *
+	 * This callback can be used to stop a Listen state operation that was
+	 * previously requested with start_listen().
+	 */
+	void (*stop_listen)(void *ctx);
+
+	/**
+	 * get_noa - Get current Notice of Absence attribute payload
+	 * @ctx: Callback context from cb_ctx
+	 * @interface_addr: P2P Interface Address of the GO
+	 * @buf: Buffer for returning NoA
+	 * @buf_len: Buffer length in octets
+	 * Returns: Number of octets used in buf, 0 to indicate no NoA is being
+	 * advertized, or -1 on failure
+	 *
+	 * This function is used to fetch the current Notice of Absence
+	 * attribute value from GO.
+	 */
+	int (*get_noa)(void *ctx, const u8 *interface_addr, u8 *buf,
+		       size_t buf_len);
+
+	/* Callbacks to notify events to upper layer management entity */
+
+	/**
+	 * dev_found - Notification of a found P2P Device
+	 * @ctx: Callback context from cb_ctx
+	 * @addr: Source address of the message triggering this notification
+	 * @info: P2P peer information
+	 * @new_device: Inform if the peer is newly found
+	 *
+	 * This callback is used to notify that a new P2P Device has been
+	 * found. This may happen, e.g., during Search state based on scan
+	 * results or during Listen state based on receive Probe Request and
+	 * Group Owner Negotiation Request.
+	 */
+	void (*dev_found)(void *ctx, const u8 *addr,
+			  const struct p2p_peer_info *info,
+			  int new_device);
+
+	/**
+	 * dev_lost - Notification of a lost P2P Device
+	 * @ctx: Callback context from cb_ctx
+	 * @dev_addr: P2P Device Address of the lost P2P Device
+	 *
+	 * This callback is used to notify that a P2P Device has been deleted.
+	 */
+	void (*dev_lost)(void *ctx, const u8 *dev_addr);
+
+	/**
+	 * go_neg_req_rx - Notification of a receive GO Negotiation Request
+	 * @ctx: Callback context from cb_ctx
+	 * @src: Source address of the message triggering this notification
+	 * @dev_passwd_id: WPS Device Password ID
+	 *
+	 * This callback is used to notify that a P2P Device is requesting
+	 * group owner negotiation with us, but we do not have all the
+	 * necessary information to start GO Negotiation. This indicates that
+	 * the local user has not authorized the connection yet by providing a
+	 * PIN or PBC button press. This information can be provided with a
+	 * call to p2p_connect().
+	 */
+	void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id);
+
+	/**
+	 * go_neg_completed - Notification of GO Negotiation results
+	 * @ctx: Callback context from cb_ctx
+	 * @res: GO Negotiation results
+	 *
+	 * This callback is used to notify that Group Owner Negotiation has
+	 * been completed. Non-zero struct p2p_go_neg_results::status indicates
+	 * failed negotiation. In case of success, this function is responsible
+	 * for creating a new group interface (or using the existing interface
+	 * depending on driver features), setting up the group interface in
+	 * proper mode based on struct p2p_go_neg_results::role_go and
+	 * initializing WPS provisioning either as a Registrar (if GO) or as an
+	 * Enrollee. Successful WPS provisioning must be indicated by calling
+	 * p2p_wps_success_cb(). The callee is responsible for timing out group
+	 * formation if WPS provisioning cannot be completed successfully
+	 * within 15 seconds.
+	 */
+	void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res);
+
+	/**
+	 * sd_request - Callback on Service Discovery Request
+	 * @ctx: Callback context from cb_ctx
+	 * @freq: Frequency (in MHz) of the channel
+	 * @sa: Source address of the request
+	 * @dialog_token: Dialog token
+	 * @update_indic: Service Update Indicator from the source of request
+	 * @tlvs: P2P Service Request TLV(s)
+	 * @tlvs_len: Length of tlvs buffer in octets
+	 *
+	 * This callback is used to indicate reception of a service discovery
+	 * request. Response to the query must be indicated by calling
+	 * p2p_sd_response() with the context information from the arguments to
+	 * this callback function.
+	 *
+	 * This callback handler can be set to %NULL to indicate that service
+	 * discovery is not supported.
+	 */
+	void (*sd_request)(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+			   u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+
+	/**
+	 * sd_response - Callback on Service Discovery Response
+	 * @ctx: Callback context from cb_ctx
+	 * @sa: Source address of the request
+	 * @update_indic: Service Update Indicator from the source of response
+	 * @tlvs: P2P Service Response TLV(s)
+	 * @tlvs_len: Length of tlvs buffer in octets
+	 *
+	 * This callback is used to indicate reception of a service discovery
+	 * response. This callback handler can be set to %NULL if no service
+	 * discovery requests are used. The information provided with this call
+	 * is replies to the queries scheduled with p2p_sd_request().
+	 */
+	void (*sd_response)(void *ctx, const u8 *sa, u16 update_indic,
+			    const u8 *tlvs, size_t tlvs_len);
+
+	/**
+	 * prov_disc_req - Callback on Provisiong Discovery Request
+	 * @ctx: Callback context from cb_ctx
+	 * @peer: Source address of the request
+	 * @config_methods: Requested WPS Config Method
+	 * @dev_addr: P2P Device Address of the found P2P Device
+	 * @pri_dev_type: Primary Device Type
+	 * @dev_name: Device Name
+	 * @supp_config_methods: Supported configuration Methods
+	 * @dev_capab: Device Capabilities
+	 * @group_capab: Group Capabilities
+	 *
+	 * This callback is used to indicate reception of a Provision Discovery
+	 * Request frame that the P2P module accepted.
+	 */
+	void (*prov_disc_req)(void *ctx, const u8 *peer, u16 config_methods,
+			      const u8 *dev_addr, const u8 *pri_dev_type,
+			      const char *dev_name, u16 supp_config_methods,
+			      u8 dev_capab, u8 group_capab);
+
+	/**
+	 * prov_disc_resp - Callback on Provisiong Discovery Response
+	 * @ctx: Callback context from cb_ctx
+	 * @peer: Source address of the response
+	 * @config_methods: Value from p2p_prov_disc_req() or 0 on failure
+	 *
+	 * This callback is used to indicate reception of a Provision Discovery
+	 * Response frame for a pending request scheduled with
+	 * p2p_prov_disc_req(). This callback handler can be set to %NULL if
+	 * provision discovery is not used.
+	 */
+	void (*prov_disc_resp)(void *ctx, const u8 *peer, u16 config_methods);
+
+	/**
+	 * invitation_process - Optional callback for processing Invitations
+	 * @ctx: Callback context from cb_ctx
+	 * @sa: Source address of the Invitation Request
+	 * @bssid: P2P Group BSSID from the request or %NULL if not included
+	 * @go_dev_addr: GO Device Address from P2P Group ID
+	 * @ssid: SSID from P2P Group ID
+	 * @ssid_len: Length of ssid buffer in octets
+	 * @go: Variable for returning whether the local end is GO in the group
+	 * @group_bssid: Buffer for returning P2P Group BSSID (if local end GO)
+	 * @force_freq: Variable for returning forced frequency for the group
+	 * @persistent_group: Whether this is an invitation to reinvoke a
+	 *	persistent group (instead of invitation to join an active
+	 *	group)
+	 * Returns: Status code (P2P_SC_*)
+	 *
+	 * This optional callback can be used to implement persistent reconnect
+	 * by allowing automatic restarting of persistent groups without user
+	 * interaction. If this callback is not implemented (i.e., is %NULL),
+	 * the received Invitation Request frames are replied with
+	 * %P2P_SC_REQ_RECEIVED status and indicated to upper layer with the
+	 * invitation_result() callback.
+	 *
+	 * If the requested parameters are acceptable and the group is known,
+	 * %P2P_SC_SUCCESS may be returned. If the requested group is unknown,
+	 * %P2P_SC_FAIL_UNKNOWN_GROUP should be returned. %P2P_SC_REQ_RECEIVED
+	 * can be returned if there is not enough data to provide immediate
+	 * response, i.e., if some sort of user interaction is needed. The
+	 * invitation_received() callback will be called in that case
+	 * immediately after this call.
+	 */
+	u8 (*invitation_process)(void *ctx, const u8 *sa, const u8 *bssid,
+				 const u8 *go_dev_addr, const u8 *ssid,
+				 size_t ssid_len, int *go, u8 *group_bssid,
+				 int *force_freq, int persistent_group);
+
+	/**
+	 * invitation_received - Callback on Invitation Request RX
+	 * @ctx: Callback context from cb_ctx
+	 * @sa: Source address of the Invitation Request
+	 * @bssid: P2P Group BSSID or %NULL if not received
+	 * @ssid: SSID of the group
+	 * @ssid_len: Length of ssid in octets
+	 * @go_dev_addr: GO Device Address
+	 * @status: Response Status
+	 * @op_freq: Operational frequency for the group
+	 *
+	 * This callback is used to indicate sending of an Invitation Response
+	 * for a received Invitation Request. If status == 0 (success), the
+	 * upper layer code is responsible for starting the group. status == 1
+	 * indicates need to get user authorization for the group. Other status
+	 * values indicate that the invitation request was rejected.
+	 */
+	void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid,
+				    const u8 *ssid, size_t ssid_len,
+				    const u8 *go_dev_addr, u8 status,
+				    int op_freq);
+
+	/**
+	 * invitation_result - Callback on Invitation result
+	 * @ctx: Callback context from cb_ctx
+	 * @status: Negotiation result (Status Code)
+	 * @bssid: P2P Group BSSID or %NULL if not received
+	 *
+	 * This callback is used to indicate result of an Invitation procedure
+	 * started with a call to p2p_invite(). The indicated status code is
+	 * the value received from the peer in Invitation Response with 0
+	 * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a
+	 * local failure in transmitting the Invitation Request.
+	 */
+	void (*invitation_result)(void *ctx, int status, const u8 *bssid);
+};
+
+
+/* P2P module initialization/deinitialization */
+
+/**
+ * p2p_init - Initialize P2P module
+ * @cfg: P2P module configuration
+ * Returns: Pointer to private data or %NULL on failure
+ *
+ * This function is used to initialize global P2P module context (one per
+ * device). The P2P module will keep a copy of the configuration data, so the
+ * caller does not need to maintain this structure. However, the callback
+ * functions and the context parameters to them must be kept available until
+ * the P2P module is deinitialized with p2p_deinit().
+ */
+struct p2p_data * p2p_init(const struct p2p_config *cfg);
+
+/**
+ * p2p_deinit - Deinitialize P2P module
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_deinit(struct p2p_data *p2p);
+
+/**
+ * p2p_flush - Flush P2P module state
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This command removes the P2P module state like peer device entries.
+ */
+void p2p_flush(struct p2p_data *p2p);
+
+/**
+ * p2p_unauthorize - Unauthorize the specified peer device
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P peer entry to be unauthorized
+ * Returns: 0 on success, -1 on failure
+ *
+ * This command removes any connection authorization from the specified P2P
+ * peer device address. This can be used, e.g., to cancel effect of a previous
+ * p2p_authorize() or p2p_connect() call that has not yet resulted in completed
+ * GO Negotiation.
+ */
+int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_dev_name - Set device name
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name);
+
+int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer);
+int p2p_set_model_name(struct p2p_data *p2p, const char *model_name);
+int p2p_set_model_number(struct p2p_data *p2p, const char *model_number);
+int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number);
+
+void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods);
+void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid);
+
+/**
+ * p2p_set_pri_dev_type - Set primary device type
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type);
+
+/**
+ * p2p_set_sec_dev_types - Set secondary device types
+ * @p2p: P2P module context from p2p_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to update the P2P module configuration with
+ * information that was not available at the time of the p2p_init() call.
+ */
+int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
+			  size_t num_dev_types);
+
+int p2p_set_country(struct p2p_data *p2p, const char *country);
+
+
+/* Commands from upper layer management entity */
+
+enum p2p_discovery_type {
+	P2P_FIND_START_WITH_FULL,
+	P2P_FIND_ONLY_SOCIAL,
+	P2P_FIND_PROGRESSIVE
+};
+
+/**
+ * p2p_find - Start P2P Find (Device Discovery)
+ * @p2p: P2P module context from p2p_init()
+ * @timeout: Timeout for find operation in seconds or 0 for no timeout
+ * @type: Device Discovery type
+ * @num_req_dev_types: Number of requested device types
+ * @req_dev_types: Requested device types array, must be an array
+ *	containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no
+ *	requested device types.
+ * Returns: 0 on success, -1 on failure
+ */
+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);
+
+/**
+ * p2p_stop_find - Stop P2P Find (Device Discovery)
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_stop_find(struct p2p_data *p2p);
+
+/**
+ * p2p_stop_find_for_freq - Stop P2P Find for next oper on specific freq
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Frequency in MHz for next operation
+ *
+ * This is like p2p_stop_find(), but Listen state is not stopped if we are
+ * already on the same frequency.
+ */
+void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq);
+
+/**
+ * p2p_listen - Start P2P Listen state for specified duration
+ * @p2p: P2P module context from p2p_init()
+ * @timeout: Listen state duration in milliseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request the P2P module to keep the device
+ * discoverable on the listen channel for an extended set of time. At least in
+ * its current form, this is mainly used for testing purposes and may not be of
+ * much use for normal P2P operations.
+ */
+int p2p_listen(struct p2p_data *p2p, unsigned int timeout);
+
+/**
+ * p2p_connect - Start P2P group formation (GO negotiation)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: WPS method to be used in provisioning
+ * @go_intent: Local GO intent value (1..15)
+ * @own_interface_addr: Intended interface address to use with the group
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @persistent_group: Whether to create a persistent group
+ * Returns: 0 on success, -1 on failure
+ */
+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);
+
+/**
+ * p2p_authorize - Authorize P2P group formation (GO negotiation)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @wps_method: WPS method to be used in provisioning
+ * @go_intent: Local GO intent value (1..15)
+ * @own_interface_addr: Intended interface address to use with the group
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @persistent_group: Whether to create a persistent group
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is like p2p_connect(), but the actual group negotiation is not
+ * initiated automatically, i.e., the other end is expected to do that.
+ */
+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);
+
+/**
+ * p2p_reject - Reject peer device (explicitly block connection attempts)
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr);
+
+/**
+ * p2p_prov_disc_req - Send Provision Discovery Request
+ * @p2p: P2P module context from p2p_init()
+ * @peer_addr: MAC address of the peer P2P client
+ * @config_methods: WPS Config Methods value (only one bit set)
+ * @join: Whether this is used by a client joining an active group
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to request a discovered P2P peer to display a PIN
+ * (config_methods = WPS_CONFIG_DISPLAY) or be prepared to enter a PIN from us
+ * (config_methods = WPS_CONFIG_KEYPAD). The Provision Discovery Request frame
+ * is transmitted once immediately and if no response is received, the frame
+ * will be sent again whenever the target device is discovered during device
+ * dsicovery (start with a p2p_find() call). Response from the peer is
+ * indicated with the p2p_config::prov_disc_resp() callback.
+ */
+int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+		      u16 config_methods, int join);
+
+/**
+ * p2p_sd_request - Schedule a service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @dst: Destination peer or %NULL to apply for all peers
+ * @tlvs: P2P Service Query TLV(s)
+ * Returns: Reference to the query or %NULL on failure
+ *
+ * Response to the query is indicated with the p2p_config::sd_response()
+ * callback.
+ */
+void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
+		      const struct wpabuf *tlvs);
+
+/**
+ * p2p_sd_cancel_request - Cancel a pending service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @req: Query reference from p2p_sd_request()
+ * Returns: 0 if request for cancelled; -1 if not found
+ */
+int p2p_sd_cancel_request(struct p2p_data *p2p, void *req);
+
+/**
+ * p2p_sd_response - Send response to a service discovery query
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Frequency from p2p_config::sd_request() callback
+ * @dst: Destination address from p2p_config::sd_request() callback
+ * @dialog_token: Dialog token from p2p_config::sd_request() callback
+ * @resp_tlvs: P2P Service Response TLV(s)
+ *
+ * This function is called as a response to the request indicated with
+ * p2p_config::sd_request() callback.
+ */
+void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
+		     u8 dialog_token, const struct wpabuf *resp_tlvs);
+
+/**
+ * p2p_sd_service_update - Indicate a change in local services
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function needs to be called whenever there is a change in availability
+ * of the local services. This will increment the Service Update Indicator
+ * value which will be used in SD Request and Response frames.
+ */
+void p2p_sd_service_update(struct p2p_data *p2p);
+
+
+enum p2p_invite_role {
+	P2P_INVITE_ROLE_GO,
+	P2P_INVITE_ROLE_ACTIVE_GO,
+	P2P_INVITE_ROLE_CLIENT
+};
+
+/**
+ * p2p_invite - Invite a P2P Device into a group
+ * @p2p: P2P module context from p2p_init()
+ * @peer: Device Address of the peer P2P Device
+ * @role: Local role in the group
+ * @bssid: Group BSSID or %NULL if not known
+ * @ssid: Group SSID
+ * @ssid_len: Length of ssid in octets
+ * @force_freq: The only allowed channel frequency in MHz or 0
+ * @go_dev_addr: Forced GO Device Address or %NULL if none
+ * @persistent_group: Whether this is to reinvoke a persistent group
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
+	       const u8 *bssid, const u8 *ssid, size_t ssid_len,
+	       unsigned int force_freq, const u8 *go_dev_addr,
+	       int persistent_group);
+
+/**
+ * p2p_presence_req - Request GO presence
+ * @p2p: P2P module context from p2p_init()
+ * @go_interface_addr: GO P2P Interface Address
+ * @own_interface_addr: Own P2P Interface Address for this group
+ * @freq: Group operating frequence (in MHz)
+ * @duration1: Preferred presence duration in microseconds
+ * @interval1: Preferred presence interval in microseconds
+ * @duration2: Acceptable presence duration in microseconds
+ * @interval2: Acceptable presence interval in microseconds
+ * Returns: 0 on success, -1 on failure
+ *
+ * If both duration and interval values are zero, the parameter pair is not
+ * specified (i.e., to remove Presence Request, use duration1 = interval1 = 0).
+ */
+int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
+		     const u8 *own_interface_addr, unsigned int freq,
+		     u32 duration1, u32 interval1, u32 duration2,
+		     u32 interval2);
+
+/**
+ * p2p_ext_listen - Set Extended Listen Timing
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Group operating frequence (in MHz)
+ * @period: Availability period in milliseconds (1-65535; 0 to disable)
+ * @interval: Availability interval in milliseconds (1-65535; 0 to disable)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to enable or disable (period = interval = 0)
+ * Extended Listen Timing. When enabled, the P2P Device will become
+ * discoverable (go into Listen State) every @interval milliseconds for at
+ * least @period milliseconds.
+ */
+int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
+		   unsigned int interval);
+
+/* Event notifications from upper layer management operations */
+
+/**
+ * p2p_wps_success_cb - Report successfully completed WPS provisioning
+ * @p2p: P2P module context from p2p_init()
+ * @mac_addr: Peer address
+ *
+ * This function is used to report successfully completed WPS provisioning
+ * during group formation in both GO/Registrar and client/Enrollee roles.
+ */
+void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr);
+
+/**
+ * p2p_group_formation_failed - Report failed WPS provisioning
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function is used to report failed group formation. This can happen
+ * either due to failed WPS provisioning or due to 15 second timeout during
+ * the provisioning phase.
+ */
+void p2p_group_formation_failed(struct p2p_data *p2p);
+
+
+/* Event notifications from lower layer driver operations */
+
+/**
+ * p2p_probe_req_rx - Report reception of a Probe Request frame
+ * @p2p: P2P module context from p2p_init()
+ * @addr: Source MAC address
+ * @ie: Information elements from the Probe Request frame body
+ * @ie_len: Length of ie buffer in octets
+ * Returns: 0 to indicate the frame was not processed or 1 if it was
+ */
+int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *ie,
+		     size_t ie_len);
+
+/**
+ * p2p_rx_action - Report received Action frame
+ * @p2p: P2P module context from p2p_init()
+ * @da: Destination address of the received Action frame
+ * @sa: Source address of the received Action frame
+ * @bssid: Address 3 of the received Action frame
+ * @category: Category of the received Action frame
+ * @data: Action frame body after the Category field
+ * @len: Length of the data buffer in octets
+ * @freq: Frequency (in MHz) on which the frame was received
+ */
+void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+		   const u8 *bssid, u8 category,
+		   const u8 *data, size_t len, int freq);
+
+/**
+ * p2p_scan_res_handler - Indicate a P2P scan results
+ * @p2p: P2P module context from p2p_init()
+ * @bssid: BSSID of the scan result
+ * @freq: Frequency of the channel on which the device was found in MHz
+ * @level: Signal level (signal strength of the received Beacon/Probe Response
+ *	frame)
+ * @ies: Pointer to IEs from the scan result
+ * @ies_len: Length of the ies buffer
+ * Returns: 0 to continue or 1 to stop scan result indication
+ *
+ * This function is called to indicate a scan result entry with P2P IE from a
+ * scan requested with struct p2p_config::p2p_scan(). This can be called during
+ * the actual scan process (i.e., whenever a new device is found) or as a
+ * sequence of calls after the full scan has been completed. The former option
+ * can result in optimized operations, but may not be supported by all
+ * driver/firmware designs. The ies buffer need to include at least the P2P IE,
+ * but it is recommended to include all IEs received from the device. The
+ * caller does not need to check that the IEs contain a P2P IE before calling
+ * this function since frames will be filtered internally if needed.
+ *
+ * This function will return 1 if it wants to stop scan result iteration (and
+ * scan in general if it is still in progress). This is used to allow faster
+ * start of a pending operation, e.g., to start a pending GO negotiation.
+ */
+int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
+			 int level, const u8 *ies, size_t ies_len);
+
+/**
+ * p2p_scan_res_handled - Indicate end of scan results
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This function is called to indicate that all P2P scan results from a scan
+ * have been reported with zero or more calls to p2p_scan_res_handler(). This
+ * function must be called as a response to successful
+ * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler()
+ * calls stopped iteration.
+ */
+void p2p_scan_res_handled(struct p2p_data *p2p);
+
+enum p2p_send_action_result {
+	P2P_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */,
+	P2P_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged */,
+	P2P_SEND_ACTION_FAILED /* Frame was not sent due to a failure */
+};
+
+/**
+ * p2p_send_action_cb - Notify TX status of an Action frame
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * @dst: Destination MAC address (Address 1)
+ * @src: Source MAC address (Address 2)
+ * @bssid: BSSID (Address 3)
+ * @result: Result of the transmission attempt
+ *
+ * This function is used to indicate the result of an Action frame transmission
+ * that was requested with struct p2p_config::send_action() callback.
+ */
+void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+			const u8 *src, const u8 *bssid,
+			enum p2p_send_action_result result);
+
+/**
+ * p2p_listen_cb - Indicate the start of a requested Listen state
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Listen channel frequency in MHz
+ * @duration: Duration for the Listen state in milliseconds
+ *
+ * This function is used to indicate that a Listen state requested with
+ * struct p2p_config::start_listen() callback has started.
+ */
+void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
+		   unsigned int duration);
+
+/**
+ * p2p_listen_end - Indicate the end of a requested Listen state
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Listen channel frequency in MHz
+ * Returns: 0 if no operations were started, 1 if an operation was started
+ *
+ * This function is used to indicate that a Listen state requested with
+ * struct p2p_config::start_listen() callback has ended.
+ */
+int p2p_listen_end(struct p2p_data *p2p, unsigned int freq);
+
+void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+		      const u8 *ie, size_t ie_len);
+
+void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
+			const u8 *ie, size_t ie_len);
+
+
+/* Per-group P2P state for GO */
+
+struct p2p_group;
+
+/**
+ * struct p2p_group_config - P2P group configuration
+ *
+ * This configuration is provided to the P2P module during initialization of
+ * the per-group information with p2p_group_init().
+ */
+struct p2p_group_config {
+	/**
+	 * persistent_group - Whether the group is persistent
+	 */
+	int persistent_group;
+
+	/**
+	 * interface_addr - P2P Interface Address of the group
+	 */
+	u8 interface_addr[ETH_ALEN];
+
+	/**
+	 * max_clients - Maximum number of clients in the group
+	 */
+	unsigned int max_clients;
+
+	/**
+	 * cb_ctx - Context to use with callback functions
+	 */
+	void *cb_ctx;
+
+	/**
+	 * ie_update - Notification of IE update
+	 * @ctx: Callback context from cb_ctx
+	 * @beacon_ies: P2P IE for Beacon frames or %NULL if no change
+	 * @proberesp_ies: P2P Ie for Probe Response frames
+	 *
+	 * P2P module uses this callback function to notify whenever the P2P IE
+	 * in Beacon or Probe Response frames should be updated based on group
+	 * events.
+	 *
+	 * The callee is responsible for freeing the returned buffer(s) with
+	 * wpabuf_free().
+	 */
+	void (*ie_update)(void *ctx, struct wpabuf *beacon_ies,
+			  struct wpabuf *proberesp_ies);
+
+	/**
+	 * idle_update - Notification of changes in group idle state
+	 * @ctx: Callback context from cb_ctx
+	 * @idle: Whether the group is idle (no associated stations)
+	 */
+	void (*idle_update)(void *ctx, int idle);
+};
+
+/**
+ * p2p_group_init - Initialize P2P group
+ * @p2p: P2P module context from p2p_init()
+ * @config: P2P group configuration (will be freed by p2p_group_deinit())
+ * Returns: Pointer to private data or %NULL on failure
+ *
+ * This function is used to initialize per-group P2P module context. Currently,
+ * this is only used to manage GO functionality and P2P clients do not need to
+ * create an instance of this per-group information.
+ */
+struct p2p_group * p2p_group_init(struct p2p_data *p2p,
+				  struct p2p_group_config *config);
+
+/**
+ * p2p_group_deinit - Deinitialize P2P group
+ * @group: P2P group context from p2p_group_init()
+ */
+void p2p_group_deinit(struct p2p_group *group);
+
+/**
+ * p2p_group_notif_assoc - Notification of P2P client association with GO
+ * @group: P2P group context from p2p_group_init()
+ * @addr: Interface address of the P2P client
+ * @ie: IEs from the (Re)association Request frame
+ * @len: Length of the ie buffer in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
+			  const u8 *ie, size_t len);
+
+/**
+ * p2p_group_assoc_resp_ie - Build P2P IE for (re)association response
+ * @group: P2P group context from p2p_group_init()
+ * @status: Status value (P2P_SC_SUCCESS if association succeeded)
+ * Returns: P2P IE for (Re)association Response or %NULL on failure
+ *
+ * The caller is responsible for freeing the returned buffer with
+ * wpabuf_free().
+ */
+struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status);
+
+/**
+ * p2p_group_notif_disassoc - Notification of P2P client disassociation from GO
+ * @group: P2P group context from p2p_group_init()
+ * @addr: Interface address of the P2P client
+ */
+void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr);
+
+/**
+ * p2p_group_notif_formation_done - Notification of completed group formation
+ * @group: P2P group context from p2p_group_init()
+ */
+void p2p_group_notif_formation_done(struct p2p_group *group);
+
+/**
+ * p2p_group_notif_noa - Notification of NoA change
+ * @group: P2P group context from p2p_group_init()
+ * @noa: Notice of Absence attribute payload, %NULL if none
+ * @noa_len: Length of noa buffer in octets
+ * Returns: 0 on success, -1 on failure
+ *
+ * Notify the P2P group management about a new NoA contents. This will be
+ * inserted into the P2P IEs in Beacon and Probe Response frames with rest of
+ * the group information.
+ */
+int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
+			size_t noa_len);
+
+/**
+ * p2p_group_match_dev_type - Match device types in group with requested type
+ * @group: P2P group context from p2p_group_init()
+ * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
+ * Returns: 1 on match, 0 on mismatch
+ *
+ * This function can be used to match the Requested Device Type attribute in
+ * WPS IE with the device types of a group member for deciding whether a GO
+ * should reply to a Probe Request frame. Match will be reported if the WPS IE
+ * is not requested any specific device type.
+ */
+int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps);
+
+/**
+ * p2p_group_go_discover - Send GO Discoverability Request to a group client
+ * @group: P2P group context from p2p_group_init()
+ * Returns: 0 on success (frame scheduled); -1 if client was not found
+ */
+int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
+			  const u8 *searching_dev, int rx_freq);
+
+
+/* Generic helper functions */
+
+/**
+ * p2p_ie_text - Build text format description of P2P IE
+ * @p2p_ie: P2P IE
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end);
+
+/**
+ * p2p_scan_result_text - Build text format description of P2P IE
+ * @ies: Information elements from scan results
+ * @ies_len: ies buffer length in octets
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on failure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end);
+
+/**
+ * p2p_assoc_req_ie - Build P2P IE for (Re)Association Request frame
+ * @p2p: P2P module context from p2p_init()
+ * @bssid: BSSID
+ * @buf: Buffer for writing the P2P IE
+ * @len: Maximum buf length in octets
+ * @p2p_group: Whether this is for association with a P2P GO
+ * @p2p_ie: Reassembled P2P IE data from scan results or %NULL if none
+ * Returns: Number of octets written into buf or -1 on failure
+ */
+int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
+		     size_t len, int p2p_group, struct wpabuf *p2p_ie);
+
+/**
+ * p2p_scan_ie - Build P2P IE for Probe Request
+ * @p2p: P2P module context from p2p_init()
+ * @ies: Buffer for writing P2P IE
+ */
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies);
+
+/**
+ * p2p_go_params - Generate random P2P group parameters
+ * @p2p: P2P module context from p2p_init()
+ * @params: Buffer for parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params);
+
+/**
+ * p2p_get_group_capab - Get Group Capability from P2P IE data
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: Group Capability
+ */
+u8 p2p_get_group_capab(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_cross_connect_disallowed - Does WLAN AP disallows cross connection
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: 0 if cross connection is allow, 1 if not
+ */
+int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data
+ * @p2p_ie: P2P IE(s) contents
+ * Returns: Pointer to P2P Device Address or %NULL if not included
+ */
+const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie);
+
+/**
+ * p2p_get_peer_info - Get P2P peer information in text format
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer or %NULL to indicate the first peer
+ * @next: Whether to select the peer entry following the one indicated by addr
+ * @buf: Buffer for returning text
+ * @buflen: Maximum buffer length
+ * Returns: Number of octets written to the buffer or -1 on failure
+ */
+int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
+		      char *buf, size_t buflen);
+
+/**
+ * p2p_set_client_discoverability - Set client discoverability capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether client discoverability will be enabled
+ *
+ * This function can be used to disable (and re-enable) client discoverability.
+ * This capability is enabled by default and should not be disabled in normal
+ * use cases, i.e., this is mainly for testing purposes.
+ */
+void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled);
+
+/**
+ * p2p_set_manageD_oper - Set managed P2P Device operations capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether managed P2P Device operations will be enabled
+ */
+void p2p_set_managed_oper(struct p2p_data *p2p, int enabled);
+
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel);
+
+int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len);
+
+int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
+			   u8 *iface_addr);
+int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
+			   u8 *dev_addr);
+
+void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr);
+
+/**
+ * p2p_set_cross_connect - Set cross connection capability
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether cross connection will be enabled
+ */
+void p2p_set_cross_connect(struct p2p_data *p2p, int enabled);
+
+int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr);
+
+int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
+		   const u8 *ies, size_t ies_len);
+
+/**
+ * p2p_set_intra_bss_dist - Set intra BSS distribution
+ * @p2p: P2P module context from p2p_init()
+ * @enabled: Whether intra BSS distribution will be enabled
+ */
+void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled);
+
+/**
+ * p2p_supported_freq - Check whether channel is supported for P2P
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * Returns: 0 if channel not usable for P2P, 1 if usable for P2P
+ */
+int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq);
+
+void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan);
+
+/**
+ * p2p_set_best_channels - Update best channel information
+ * @p2p: P2P module context from p2p_init()
+ * @freq_24: Frequency (MHz) of best channel in 2.4 GHz band
+ * @freq_5: Frequency (MHz) of best channel in 5 GHz band
+ * @freq_overall: Frequency (MHz) of best channel overall
+ */
+void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5,
+			   int freq_overall);
+
+const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p);
+
+/**
+ * p2p_get_group_num_members - Get number of members in group
+ * @group: P2P group context from p2p_group_init()
+ * Returns: Number of members in the group
+ */
+unsigned int p2p_get_group_num_members(struct p2p_group *group);
+
+/**
+ * p2p_iterate_group_members - Iterate group members
+ * @group: P2P group context from p2p_group_init()
+ * @next: iteration pointer, must be a pointer to a void * that is set to %NULL
+ *	on the first call and not modified later
+ * Returns: A P2P Interface Address for each call and %NULL for no more members
+ */
+const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next);
+
+/**
+ * p2p_get_peer_found - Get P2P peer info structure of a found peer
+ * @p2p: P2P module context from p2p_init()
+ * @addr: P2P Device Address of the peer or %NULL to indicate the first peer
+ * @next: Whether to select the peer entry following the one indicated by addr
+ * Returns: The first P2P peer info available or %NULL if no such peer exists
+ */
+const struct p2p_peer_info *
+p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next);
+
+/**
+ * p2p_remove_wps_vendor_extensions - Remove WPS vendor extensions
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p);
+
+/**
+ * p2p_add_wps_vendor_extension - Add a WPS vendor extension
+ * @p2p: P2P module context from p2p_init()
+ * @vendor_ext: The vendor extensions to add
+ * Returns: 0 on success, -1 on failure
+ *
+ * The wpabuf structures in the array are owned by the P2P
+ * module after this call.
+ */
+int p2p_add_wps_vendor_extension(struct p2p_data *p2p,
+				 const struct wpabuf *vendor_ext);
+
+#endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
new file mode 100644
index 0000000..c34db91
--- /dev/null
+++ b/src/p2p/p2p_build.c
@@ -0,0 +1,431 @@
+/*
+ * P2P - IE builder
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+
+
+void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token)
+{
+	wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, P2P_OUI_TYPE);
+
+	wpabuf_put_u8(buf, subtype); /* OUI Subtype */
+	wpabuf_put_u8(buf, dialog_token);
+	wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
+}
+
+
+void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
+				   u8 dialog_token)
+{
+	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, P2P_OUI_TYPE);
+
+	wpabuf_put_u8(buf, subtype); /* OUI Subtype */
+	wpabuf_put_u8(buf, dialog_token);
+	wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
+}
+
+
+u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf)
+{
+	u8 *len;
+
+	/* P2P IE header */
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	len = wpabuf_put(buf, 1); /* IE length to be filled */
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, P2P_OUI_TYPE);
+	wpa_printf(MSG_DEBUG, "P2P: * P2P IE header");
+	return len;
+}
+
+
+void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len)
+{
+	/* Update P2P IE Length */
+	*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
+}
+
+
+void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab)
+{
+	/* P2P Capability */
+	wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY);
+	wpabuf_put_le16(buf, 2);
+	wpabuf_put_u8(buf, dev_capab); /* Device Capabilities */
+	wpabuf_put_u8(buf, group_capab); /* Group Capabilities */
+	wpa_printf(MSG_DEBUG, "P2P: * Capability dev=%02x group=%02x",
+		   dev_capab, group_capab);
+}
+
+
+void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent)
+{
+	/* Group Owner Intent */
+	wpabuf_put_u8(buf, P2P_ATTR_GROUP_OWNER_INTENT);
+	wpabuf_put_le16(buf, 1);
+	wpabuf_put_u8(buf, go_intent);
+	wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u Tie breaker %u",
+		   go_intent >> 1, go_intent & 0x01);
+}
+
+
+void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
+				u8 reg_class, u8 channel)
+{
+	/* Listen Channel */
+	wpabuf_put_u8(buf, P2P_ATTR_LISTEN_CHANNEL);
+	wpabuf_put_le16(buf, 5);
+	wpabuf_put_data(buf, country, 3);
+	wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
+	wpabuf_put_u8(buf, channel); /* Channel Number */
+	wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Regulatory Class %u "
+		   "Channel %u", reg_class, channel);
+}
+
+
+void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
+				   u8 reg_class, u8 channel)
+{
+	/* Operating Channel */
+	wpabuf_put_u8(buf, P2P_ATTR_OPERATING_CHANNEL);
+	wpabuf_put_le16(buf, 5);
+	wpabuf_put_data(buf, country, 3);
+	wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
+	wpabuf_put_u8(buf, channel); /* Channel Number */
+	wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: Regulatory Class %u "
+		   "Channel %u", reg_class, channel);
+}
+
+
+void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
+			      struct p2p_channels *chan)
+{
+	u8 *len;
+	size_t i;
+
+	/* Channel List */
+	wpabuf_put_u8(buf, P2P_ATTR_CHANNEL_LIST);
+	len = wpabuf_put(buf, 2); /* IE length to be filled */
+	wpabuf_put_data(buf, country, 3); /* Country String */
+
+	for (i = 0; i < chan->reg_classes; i++) {
+		struct p2p_reg_class *c = &chan->reg_class[i];
+		wpabuf_put_u8(buf, c->reg_class);
+		wpabuf_put_u8(buf, c->channels);
+		wpabuf_put_data(buf, c->channel, c->channels);
+	}
+
+	/* Update attribute length */
+	WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+	wpa_printf(MSG_DEBUG, "P2P: * Channel List");
+}
+
+
+void p2p_buf_add_status(struct wpabuf *buf, u8 status)
+{
+	/* Status */
+	wpabuf_put_u8(buf, P2P_ATTR_STATUS);
+	wpabuf_put_le16(buf, 1);
+	wpabuf_put_u8(buf, status);
+	wpa_printf(MSG_DEBUG, "P2P: * Status: %d", status);
+}
+
+
+void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
+			     struct p2p_device *peer)
+{
+	u8 *len;
+	u16 methods;
+	size_t nlen, i;
+
+	/* P2P Device Info */
+	wpabuf_put_u8(buf, P2P_ATTR_DEVICE_INFO);
+	len = wpabuf_put(buf, 2); /* IE length to be filled */
+
+	/* P2P Device address */
+	wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
+
+	/* Config Methods */
+	methods = 0;
+	if (peer && peer->wps_method != WPS_NOT_READY) {
+		if (peer->wps_method == WPS_PBC)
+			methods |= WPS_CONFIG_PUSHBUTTON;
+		else if (peer->wps_method == WPS_PIN_LABEL)
+			methods |= WPS_CONFIG_LABEL;
+		else if (peer->wps_method == WPS_PIN_DISPLAY ||
+			 peer->wps_method == WPS_PIN_KEYPAD)
+			methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+	} else {
+		methods |= WPS_CONFIG_PUSHBUTTON;
+		methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+	}
+	wpabuf_put_be16(buf, methods);
+
+	/* Primary Device Type */
+	wpabuf_put_data(buf, p2p->cfg->pri_dev_type,
+			sizeof(p2p->cfg->pri_dev_type));
+
+	/* Number of Secondary Device Types */
+	wpabuf_put_u8(buf, p2p->cfg->num_sec_dev_types);
+
+	/* Secondary Device Type List */
+	for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
+		wpabuf_put_data(buf, p2p->cfg->sec_dev_type[i],
+				WPS_DEV_TYPE_LEN);
+
+	/* Device Name */
+	nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0;
+	wpabuf_put_be16(buf, ATTR_DEV_NAME);
+	wpabuf_put_be16(buf, nlen);
+	wpabuf_put_data(buf, p2p->cfg->dev_name, nlen);
+
+	/* Update attribute length */
+	WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+	wpa_printf(MSG_DEBUG, "P2P: * Device Info");
+}
+
+
+void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr)
+{
+	/* P2P Device ID */
+	wpabuf_put_u8(buf, P2P_ATTR_DEVICE_ID);
+	wpabuf_put_le16(buf, ETH_ALEN);
+	wpabuf_put_data(buf, dev_addr, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "P2P: * Device ID: " MACSTR, MAC2STR(dev_addr));
+}
+
+
+void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
+				u8 client_timeout)
+{
+	/* Configuration Timeout */
+	wpabuf_put_u8(buf, P2P_ATTR_CONFIGURATION_TIMEOUT);
+	wpabuf_put_le16(buf, 2);
+	wpabuf_put_u8(buf, go_timeout);
+	wpabuf_put_u8(buf, client_timeout);
+	wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout: GO %d (*10ms)  "
+		   "client %d (*10ms)", go_timeout, client_timeout);
+}
+
+
+void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr)
+{
+	/* Intended P2P Interface Address */
+	wpabuf_put_u8(buf, P2P_ATTR_INTENDED_INTERFACE_ADDR);
+	wpabuf_put_le16(buf, ETH_ALEN);
+	wpabuf_put_data(buf, interface_addr, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address " MACSTR,
+		   MAC2STR(interface_addr));
+}
+
+
+void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid)
+{
+	/* P2P Group BSSID */
+	wpabuf_put_u8(buf, P2P_ATTR_GROUP_BSSID);
+	wpabuf_put_le16(buf, ETH_ALEN);
+	wpabuf_put_data(buf, bssid, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID " MACSTR,
+		   MAC2STR(bssid));
+}
+
+
+void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
+			  const u8 *ssid, size_t ssid_len)
+{
+	/* P2P Group ID */
+	wpabuf_put_u8(buf, P2P_ATTR_GROUP_ID);
+	wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
+	wpabuf_put_data(buf, dev_addr, ETH_ALEN);
+	wpabuf_put_data(buf, ssid, ssid_len);
+	wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
+		   MAC2STR(dev_addr));
+}
+
+
+void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags)
+{
+	/* Invitation Flags */
+	wpabuf_put_u8(buf, P2P_ATTR_INVITATION_FLAGS);
+	wpabuf_put_le16(buf, 1);
+	wpabuf_put_u8(buf, flags);
+	wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", flags);
+}
+
+
+static void p2p_buf_add_noa_desc(struct wpabuf *buf, struct p2p_noa_desc *desc)
+{
+	if (desc == NULL)
+		return;
+
+	wpabuf_put_u8(buf, desc->count_type);
+	wpabuf_put_le32(buf, desc->duration);
+	wpabuf_put_le32(buf, desc->interval);
+	wpabuf_put_le32(buf, desc->start_time);
+}
+
+
+void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
+		     struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2)
+{
+	/* Notice of Absence */
+	wpabuf_put_u8(buf, P2P_ATTR_NOTICE_OF_ABSENCE);
+	wpabuf_put_le16(buf, 2 + (desc1 ? 13 : 0) + (desc2 ? 13 : 0));
+	wpabuf_put_u8(buf, noa_index);
+	wpabuf_put_u8(buf, (opp_ps ? 0x80 : 0) | (ctwindow & 0x7f));
+	p2p_buf_add_noa_desc(buf, desc1);
+	p2p_buf_add_noa_desc(buf, desc2);
+	wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
+}
+
+
+void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
+				   u16 interval)
+{
+	/* Extended Listen Timing */
+	wpabuf_put_u8(buf, P2P_ATTR_EXT_LISTEN_TIMING);
+	wpabuf_put_le16(buf, 4);
+	wpabuf_put_le16(buf, period);
+	wpabuf_put_le16(buf, interval);
+	wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing (period %u msec  "
+		   "interval %u msec)", period, interval);
+}
+
+
+void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p)
+{
+	/* P2P Interface */
+	wpabuf_put_u8(buf, P2P_ATTR_INTERFACE);
+	wpabuf_put_le16(buf, ETH_ALEN + 1 + ETH_ALEN);
+	/* P2P Device address */
+	wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
+	/*
+	 * FIX: Fetch interface address list from driver. Do not include
+	 * the P2P Device address if it is never used as interface address.
+	 */
+	/* P2P Interface Address Count */
+	wpabuf_put_u8(buf, 1);
+	wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
+}
+
+
+static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
+			       const char *val)
+{
+	size_t len;
+
+	wpabuf_put_be16(buf, attr);
+	len = val ? os_strlen(val) : 0;
+#ifndef CONFIG_WPS_STRICT
+	if (len == 0) {
+		/*
+		 * Some deployed WPS implementations fail to parse zeor-length
+		 * attributes. As a workaround, send a space character if the
+		 * device attribute string is empty.
+		 */
+		wpabuf_put_be16(buf, 1);
+		wpabuf_put_u8(buf, ' ');
+		return;
+	}
+#endif /* CONFIG_WPS_STRICT */
+	wpabuf_put_be16(buf, len);
+	if (val)
+		wpabuf_put_data(buf, val, len);
+}
+
+
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
+		      int all_attr)
+{
+	u8 *len;
+	int i;
+
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	len = wpabuf_put(buf, 1);
+	wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
+
+	wps_build_version(buf);
+
+	if (all_attr) {
+		wpabuf_put_be16(buf, ATTR_WPS_STATE);
+		wpabuf_put_be16(buf, 1);
+		wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
+	}
+
+	/* Device Password ID */
+	wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID);
+	wpabuf_put_be16(buf, 2);
+	wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d", pw_id);
+	wpabuf_put_be16(buf, pw_id);
+
+	if (all_attr) {
+		wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
+		wpabuf_put_be16(buf, 1);
+		wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO);
+
+		wps_build_uuid_e(buf, p2p->cfg->uuid);
+		p2p_add_wps_string(buf, ATTR_MANUFACTURER,
+				   p2p->cfg->manufacturer);
+		p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name);
+		p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
+				   p2p->cfg->model_number);
+		p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
+				   p2p->cfg->serial_number);
+
+		wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE);
+		wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN);
+		wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN);
+
+		p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name);
+
+		wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
+		wpabuf_put_be16(buf, 2);
+		wpabuf_put_be16(buf, p2p->cfg->config_methods);
+	}
+
+	wps_build_wfa_ext(buf, 0, NULL, 0);
+
+	if (all_attr && p2p->cfg->num_sec_dev_types) {
+		wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST);
+		wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN *
+				p2p->cfg->num_sec_dev_types);
+		wpabuf_put_data(buf, p2p->cfg->sec_dev_type,
+				WPS_DEV_TYPE_LEN *
+				p2p->cfg->num_sec_dev_types);
+	}
+
+	/* Add the WPS vendor extensions */
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		if (p2p->wps_vendor_ext[i] == NULL)
+			break;
+		if (wpabuf_tailroom(buf) <
+		    4 + wpabuf_len(p2p->wps_vendor_ext[i]))
+			continue;
+		wpabuf_put_be16(buf, ATTR_VENDOR_EXT);
+		wpabuf_put_be16(buf, wpabuf_len(p2p->wps_vendor_ext[i]));
+		wpabuf_put_buf(buf, p2p->wps_vendor_ext[i]);
+	}
+
+	p2p_buf_update_ie_hdr(buf, len);
+}
diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c
new file mode 100644
index 0000000..47cc0fd
--- /dev/null
+++ b/src/p2p/p2p_dev_disc.c
@@ -0,0 +1,365 @@
+/*
+ * Wi-Fi Direct - P2P Device Discoverability procedure
+ * Copyright (c) 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p,
+					      struct p2p_device *go,
+					      const u8 *dev_id)
+{
+	struct wpabuf *buf;
+	u8 *len;
+
+	buf = wpabuf_alloc(100);
+	if (buf == NULL)
+		return NULL;
+
+	go->dialog_token++;
+	if (go->dialog_token == 0)
+		go->dialog_token = 1;
+	p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_REQ, go->dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	p2p_buf_add_device_id(buf, dev_id);
+	p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid,
+			     go->oper_ssid_len);
+	p2p_buf_update_ie_hdr(buf, len);
+
+	return buf;
+}
+
+
+void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Device Discoverability Request TX callback: success=%d",
+		success);
+
+	if (!success) {
+		/*
+		 * Use P2P find, if needed, to find the other device or to
+		 * retry device discoverability.
+		 */
+		p2p_set_state(p2p, P2P_CONNECT);
+		p2p_set_timeout(p2p, 0, 100000);
+		return;
+	}
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: GO acknowledged Device Discoverability Request - wait "
+		"for response");
+	/*
+	 * TODO: is the remain-on-channel from Action frame TX long enough for
+	 * most cases or should we try to increase its duration and/or start
+	 * another remain-on-channel if needed once the previous one expires?
+	 */
+}
+
+
+int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev)
+{
+	struct p2p_device *go;
+	struct wpabuf *req;
+
+	go = p2p_get_device(p2p, dev->member_in_go_dev);
+	if (go == NULL || dev->oper_freq <= 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Could not find peer entry for GO and frequency "
+			"to send Device Discoverability Request");
+		return -1;
+	}
+
+	req = p2p_build_dev_disc_req(p2p, go, dev->info.p2p_device_addr);
+	if (req == NULL)
+		return -1;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Sending Device Discoverability Request to GO " MACSTR
+		" for client " MACSTR,
+		MAC2STR(go->info.p2p_device_addr),
+		MAC2STR(dev->info.p2p_device_addr));
+
+	p2p->pending_client_disc_go = go;
+	os_memcpy(p2p->pending_client_disc_addr, dev->info.p2p_device_addr,
+		  ETH_ALEN);
+	p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST;
+	if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr,
+			    p2p->cfg->dev_addr, go->info.p2p_device_addr,
+			    wpabuf_head(req), wpabuf_len(req), 1000) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+		wpabuf_free(req);
+		/* TODO: how to recover from failure? */
+		return -1;
+	}
+
+	wpabuf_free(req);
+
+	return 0;
+}
+
+
+static struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status)
+{
+	struct wpabuf *buf;
+	u8 *len;
+
+	buf = wpabuf_alloc(100);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_RESP, dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	p2p_buf_add_status(buf, status);
+	p2p_buf_update_ie_hdr(buf, len);
+
+	return buf;
+}
+
+
+void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Device Discoverability Response TX callback: success=%d",
+		success);
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+}
+
+
+static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token,
+				   const u8 *addr, int freq, u8 status)
+{
+	struct wpabuf *resp;
+
+	resp = p2p_build_dev_disc_resp(dialog_token, status);
+	if (resp == NULL)
+		return;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Sending Device Discoverability Response to " MACSTR
+		" (status %u freq %d)",
+		MAC2STR(addr), status, freq);
+
+	p2p->pending_action_state = P2P_PENDING_DEV_DISC_RESPONSE;
+	if (p2p_send_action(p2p, freq, addr, p2p->cfg->dev_addr,
+			    p2p->cfg->dev_addr,
+			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+	}
+
+	wpabuf_free(resp);
+}
+
+
+void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
+			      const u8 *data, size_t len, int rx_freq)
+{
+	struct p2p_message msg;
+	size_t g;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received Device Discoverability Request from " MACSTR
+		" (freq=%d)", MAC2STR(sa), rx_freq);
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	if (msg.dialog_token == 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Invalid Dialog Token 0 (must be nonzero) in "
+			"Device Discoverability Request");
+		p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
+				       P2P_SC_FAIL_INVALID_PARAMS);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (msg.device_id == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: P2P Device ID attribute missing from Device "
+			"Discoverability Request");
+		p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
+				       P2P_SC_FAIL_INVALID_PARAMS);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	for (g = 0; g < p2p->num_groups; g++) {
+		if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa,
+					  rx_freq) == 0) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Scheduled "
+				"GO Discoverability Request for the target "
+				"device");
+			/*
+			 * P2P group code will use a callback to indicate TX
+			 * status, so that we can reply to the request once the
+			 * target client has acknowledged the request or it has
+			 * timed out.
+			 */
+			p2p->pending_dev_disc_dialog_token = msg.dialog_token;
+			os_memcpy(p2p->pending_dev_disc_addr, sa, ETH_ALEN);
+			p2p->pending_dev_disc_freq = rx_freq;
+			p2p_parse_free(&msg);
+			return;
+		}
+	}
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Requested client "
+		"was not found in any group or did not support client "
+		"discoverability");
+	p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
+			       P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE);
+	p2p_parse_free(&msg);
+}
+
+
+void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa,
+			       const u8 *data, size_t len)
+{
+	struct p2p_message msg;
+	struct p2p_device *go;
+	u8 status;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received Device Discoverability Response from " MACSTR,
+		MAC2STR(sa));
+
+	go = p2p->pending_client_disc_go;
+	if (go == NULL ||
+	    os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore unexpected "
+			"Device Discoverability Response");
+		return;
+	}
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	if (msg.status == NULL) {
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (msg.dialog_token != go->dialog_token) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore Device "
+			"Discoverability Response with unexpected dialog "
+			"token %u (expected %u)",
+			msg.dialog_token, go->dialog_token);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	status = *msg.status;
+	p2p_parse_free(&msg);
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Device Discoverability Response status %u", status);
+
+	if (p2p->go_neg_peer == NULL ||
+	    os_memcmp(p2p->pending_client_disc_addr,
+		      p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 ||
+	    os_memcmp(p2p->go_neg_peer->member_in_go_dev,
+		      go->info.p2p_device_addr, ETH_ALEN) != 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending "
+			"operation with the client discoverability peer "
+			"anymore");
+		return;
+	}
+
+	if (status == 0) {
+		/*
+		 * Peer is expected to be awake for at least 100 TU; try to
+		 * connect immediately.
+		 */
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Client discoverability request succeeded");
+		if (p2p->state == P2P_CONNECT) {
+			/*
+			 * Change state to force the timeout to start in
+			 * P2P_CONNECT again without going through the short
+			 * Listen state.
+			 */
+			p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+			p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		}
+		p2p_set_timeout(p2p, 0, 0);
+	} else {
+		/*
+		 * Client discoverability request failed; try to connect from
+		 * timeout.
+		 */
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Client discoverability request failed");
+		p2p_set_timeout(p2p, 0, 500000);
+	}
+
+}
+
+
+void p2p_go_disc_req_cb(struct p2p_data *p2p, int success)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: GO Discoverability Request TX callback: success=%d",
+		success);
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
+	if (p2p->pending_dev_disc_dialog_token == 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending Device "
+			"Discoverability Request");
+		return;
+	}
+
+	p2p_send_dev_disc_resp(p2p, p2p->pending_dev_disc_dialog_token,
+			       p2p->pending_dev_disc_addr,
+			       p2p->pending_dev_disc_freq,
+			       success ? P2P_SC_SUCCESS :
+			       P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE);
+
+	p2p->pending_dev_disc_dialog_token = 0;
+}
+
+
+void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq)
+{
+	unsigned int tu;
+	struct wpabuf *ies;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received GO Discoverability Request - remain awake for "
+		"100 TU");
+
+	ies = p2p_build_probe_resp_ies(p2p);
+	if (ies == NULL)
+		return;
+
+	/* Remain awake 100 TU on operating channel */
+	p2p->pending_client_disc_freq = rx_freq;
+	tu = 100;
+	if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000,
+		    ies) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to start listen mode for client "
+			"discoverability");
+	}
+	wpabuf_free(ies);
+}
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
new file mode 100644
index 0000000..1c96486
--- /dev/null
+++ b/src/p2p/p2p_go_neg.c
@@ -0,0 +1,1127 @@
+/*
+ * Wi-Fi Direct - P2P Group Owner Negotiation
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static int p2p_go_det(u8 own_intent, u8 peer_value)
+{
+	u8 peer_intent = peer_value >> 1;
+	if (own_intent == peer_intent) {
+		if (own_intent == P2P_MAX_GO_INTENT)
+			return -1; /* both devices want to become GO */
+
+		/* Use tie breaker bit to determine GO */
+		return (peer_value & 0x01) ? 0 : 1;
+	}
+
+	return own_intent > peer_intent;
+}
+
+
+int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
+			    struct p2p_device *dev,
+			    const u8 *channel_list, size_t channel_list_len)
+{
+	const u8 *pos, *end;
+	struct p2p_channels *ch;
+	size_t channels;
+	struct p2p_channels intersection;
+
+	ch = &dev->channels;
+	os_memset(ch, 0, sizeof(*ch));
+	pos = channel_list;
+	end = channel_list + channel_list_len;
+
+	if (end - pos < 3)
+		return -1;
+	os_memcpy(dev->country, pos, 3);
+	wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3);
+	if (pos[2] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+			"P2P: Mismatching country (ours=%c%c peer's=%c%c)",
+			p2p->cfg->country[0], p2p->cfg->country[1],
+			pos[0], pos[1]);
+		return -1;
+	}
+	pos += 3;
+
+	while (pos + 2 < end) {
+		struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes];
+		cl->reg_class = *pos++;
+		if (pos + 1 + pos[0] > end) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+				"P2P: Invalid peer Channel List");
+			return -1;
+		}
+		channels = *pos++;
+		cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ?
+			P2P_MAX_REG_CLASS_CHANNELS : channels;
+		os_memcpy(cl->channel, pos, cl->channels);
+		pos += channels;
+		ch->reg_classes++;
+		if (ch->reg_classes == P2P_MAX_REG_CLASSES)
+			break;
+	}
+
+	p2p_channels_intersect(own, &dev->channels, &intersection);
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own reg_classes %d "
+		"peer reg_classes %d intersection reg_classes %d",
+		(int) own->reg_classes,
+		(int) dev->channels.reg_classes,
+		(int) intersection.reg_classes);
+	if (intersection.reg_classes == 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_INFO,
+			"P2P: No common channels found");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int p2p_peer_channels(struct p2p_data *p2p, struct p2p_device *dev,
+			     const u8 *channel_list, size_t channel_list_len)
+{
+	return p2p_peer_channels_check(p2p, &p2p->channels, dev,
+				       channel_list, channel_list_len);
+}
+
+
+static u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
+{
+	switch (wps_method) {
+	case WPS_PIN_LABEL:
+		return DEV_PW_DEFAULT;
+	case WPS_PIN_DISPLAY:
+		return DEV_PW_REGISTRAR_SPECIFIED;
+	case WPS_PIN_KEYPAD:
+		return DEV_PW_USER_SPECIFIED;
+	case WPS_PBC:
+		return DEV_PW_PUSHBUTTON;
+	default:
+		return DEV_PW_DEFAULT;
+	}
+}
+
+
+static const char * p2p_wps_method_str(enum p2p_wps_method wps_method)
+{
+	switch (wps_method) {
+	case WPS_PIN_LABEL:
+		return "Label";
+	case WPS_PIN_DISPLAY:
+		return "Display";
+	case WPS_PIN_KEYPAD:
+		return "Keypad";
+	case WPS_PBC:
+		return "PBC";
+	default:
+		return "??";
+	}
+}
+
+
+static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
+					    struct p2p_device *peer)
+{
+	struct wpabuf *buf;
+	u8 *len;
+	u8 group_capab;
+
+	buf = wpabuf_alloc(1000);
+	if (buf == NULL)
+		return NULL;
+
+	peer->dialog_token++;
+	if (peer->dialog_token == 0)
+		peer->dialog_token = 1;
+	p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	group_capab = 0;
+	if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP)
+		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+	if (p2p->cross_connect)
+		group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+	if (p2p->cfg->p2p_intra_bss)
+		group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+	p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+	p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) |
+			      p2p->next_tie_breaker);
+	p2p->next_tie_breaker = !p2p->next_tie_breaker;
+	p2p_buf_add_config_timeout(buf, 100, 20);
+	p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
+				   p2p->cfg->channel);
+	if (p2p->ext_listen_interval)
+		p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
+					      p2p->ext_listen_interval);
+	p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+	p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
+	p2p_buf_add_device_info(buf, p2p, peer);
+	p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+				      p2p->op_reg_class, p2p->op_channel);
+	p2p_buf_update_ie_hdr(buf, len);
+
+	/* WPS IE with Device Password ID attribute */
+	p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0);
+
+	return buf;
+}
+
+
+int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
+{
+	struct wpabuf *req;
+	int freq;
+
+	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (freq <= 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Listen/Operating frequency known for the "
+			"peer " MACSTR " to send GO Negotiation Request",
+			MAC2STR(dev->info.p2p_device_addr));
+		return -1;
+	}
+
+	req = p2p_build_go_neg_req(p2p, dev);
+	if (req == NULL)
+		return -1;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Sending GO Negotiation Request");
+	p2p_set_state(p2p, P2P_CONNECT);
+	p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST;
+	p2p->go_neg_peer = dev;
+	dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
+	dev->connect_reqs++;
+	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
+			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
+			    wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+		/* Use P2P find to recover and retry */
+		p2p_set_timeout(p2p, 0, 0);
+	}
+
+	wpabuf_free(req);
+
+	return 0;
+}
+
+
+static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
+					     struct p2p_device *peer,
+					     u8 dialog_token, u8 status,
+					     u8 tie_breaker)
+{
+	struct wpabuf *buf;
+	u8 *len;
+	u8 group_capab;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Building GO Negotiation Response");
+	buf = wpabuf_alloc(1000);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	p2p_buf_add_status(buf, status);
+	group_capab = 0;
+	if (peer && peer->go_state == LOCAL_GO) {
+		if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP)
+			group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+		if (p2p->cross_connect)
+			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+		if (p2p->cfg->p2p_intra_bss)
+			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+	}
+	p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+	p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
+	p2p_buf_add_config_timeout(buf, 100, 20);
+	if (peer && peer->go_state == REMOTE_GO) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Omit Operating "
+			"Channel attribute");
+	} else {
+		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+					      p2p->op_reg_class,
+					      p2p->op_channel);
+	}
+	p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+	if (status || peer == NULL) {
+		p2p_buf_add_channel_list(buf, p2p->cfg->country,
+					 &p2p->channels);
+	} else if (peer->go_state == REMOTE_GO) {
+		p2p_buf_add_channel_list(buf, p2p->cfg->country,
+					 &p2p->channels);
+	} else {
+		struct p2p_channels res;
+		p2p_channels_intersect(&p2p->channels, &peer->channels,
+				       &res);
+		p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
+	}
+	p2p_buf_add_device_info(buf, p2p, peer);
+	if (peer && peer->go_state == LOCAL_GO) {
+		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
+				     p2p->ssid_len);
+	}
+	p2p_buf_update_ie_hdr(buf, len);
+
+	/* WPS IE with Device Password ID attribute */
+	p2p_build_wps_ie(p2p, buf,
+			 p2p_wps_method_pw_id(peer ? peer->wps_method :
+					      WPS_NOT_READY), 0);
+
+	return buf;
+}
+
+
+static void p2p_reselect_channel(struct p2p_data *p2p,
+				 struct p2p_channels *intersection)
+{
+	struct p2p_reg_class *cl;
+	int freq;
+	u8 op_reg_class, op_channel;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating "
+		"channel (reg_class %u channel %u) not acceptable to the "
+		"peer", p2p->op_reg_class, p2p->op_channel);
+
+	/* First, try to pick the best channel from another band */
+	freq = p2p_channel_to_freq(p2p->cfg->country, p2p->op_reg_class,
+				   p2p->op_channel);
+	if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 &&
+	    p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5,
+				&op_reg_class, &op_channel) == 0 &&
+	    p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 5 GHz "
+			"channel (reg_class %u channel %u) from intersection",
+			op_reg_class, op_channel);
+		p2p->op_reg_class = op_reg_class;
+		p2p->op_channel = op_channel;
+		return;
+	}
+
+	if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 &&
+	    p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24,
+				&op_reg_class, &op_channel) == 0 &&
+	    p2p_channels_includes(intersection, op_reg_class, op_channel)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 2.4 GHz "
+			"channel (reg_class %u channel %u) from intersection",
+			op_reg_class, op_channel);
+		p2p->op_reg_class = op_reg_class;
+		p2p->op_channel = op_channel;
+		return;
+	}
+
+	/*
+	 * Fall back to whatever is included in the channel intersection since
+	 * no better options seems to be available.
+	 */
+	cl = &intersection->reg_class[0];
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick another channel "
+		"(reg_class %u channel %u) from intersection",
+		cl->reg_class, cl->channel[0]);
+	p2p->op_reg_class = cl->reg_class;
+	p2p->op_channel = cl->channel[0];
+}
+
+
+void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
+			    const u8 *data, size_t len, int rx_freq)
+{
+	struct p2p_device *dev = NULL;
+	struct wpabuf *resp;
+	struct p2p_message msg;
+	u8 status = P2P_SC_FAIL_INVALID_PARAMS;
+	int tie_breaker = 0;
+	int freq;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received GO Negotiation Request from " MACSTR
+		"(freq=%d)", MAC2STR(sa), rx_freq);
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	if (!msg.capability) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Mandatory Capability attribute missing from GO "
+			"Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+		goto fail;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	if (msg.go_intent)
+		tie_breaker = *msg.go_intent & 0x01;
+	else {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Mandatory GO Intent attribute missing from GO "
+			"Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+		goto fail;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	if (!msg.config_timeout) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Mandatory Configuration Timeout attribute "
+			"missing from GO Negotiation Request");
+#ifdef CONFIG_P2P_STRICT
+		goto fail;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	if (!msg.listen_channel) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Listen Channel attribute received");
+		goto fail;
+	}
+	if (!msg.operating_channel) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Operating Channel attribute received");
+		goto fail;
+	}
+	if (!msg.channel_list) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Channel List attribute received");
+		goto fail;
+	}
+	if (!msg.intended_addr) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Intended P2P Interface Address attribute "
+			"received");
+		goto fail;
+	}
+	if (!msg.p2p_device_info) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No P2P Device Info attribute received");
+		goto fail;
+	}
+
+	if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unexpected GO Negotiation Request SA=" MACSTR
+			" != dev_addr=" MACSTR,
+			MAC2STR(sa), MAC2STR(msg.p2p_device_addr));
+		goto fail;
+	}
+
+	dev = p2p_get_device(p2p, sa);
+
+	if (msg.status && *msg.status) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unexpected Status attribute (%d) in GO "
+			"Negotiation Request", *msg.status);
+		goto fail;
+	}
+
+	if (dev == NULL)
+		dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg);
+	else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY)
+		p2p_add_dev_info(p2p, sa, dev, &msg);
+	if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: User has rejected this peer");
+		status = P2P_SC_FAIL_REJECTED_BY_USER;
+	} else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Not ready for GO negotiation with " MACSTR,
+			MAC2STR(sa));
+		status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+		if (dev)
+			dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
+		p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa,
+					msg.dev_password_id);
+	} else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Already in Group Formation with another peer");
+		status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+	} else {
+		int go;
+
+		if (!p2p->go_neg_peer) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting "
+				"GO Negotiation with previously authorized "
+				"peer");
+			if (!(dev->flags & P2P_DEV_FORCE_FREQ)) {
+				wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+					"P2P: Use default channel settings");
+				p2p->op_reg_class = p2p->cfg->op_reg_class;
+				p2p->op_channel = p2p->cfg->op_channel;
+				os_memcpy(&p2p->channels, &p2p->cfg->channels,
+					  sizeof(struct p2p_channels));
+			} else {
+				wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+					"P2P: Use previously configured "
+					"forced channel settings");
+			}
+		}
+
+		dev->flags &= ~P2P_DEV_NOT_YET_READY;
+
+		if (!msg.go_intent) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: No GO Intent attribute received");
+			goto fail;
+		}
+		if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Invalid GO Intent value (%u) received",
+				*msg.go_intent >> 1);
+			goto fail;
+		}
+
+		if (dev->go_neg_req_sent &&
+		    os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Do not reply since peer has higher "
+				"address and GO Neg Request already sent");
+			p2p_parse_free(&msg);
+			return;
+		}
+
+		go = p2p_go_det(p2p->go_intent, *msg.go_intent);
+		if (go < 0) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Incompatible GO Intent");
+			status = P2P_SC_FAIL_BOTH_GO_INTENT_15;
+			goto fail;
+		}
+
+		if (p2p_peer_channels(p2p, dev, msg.channel_list,
+				      msg.channel_list_len) < 0) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: No common channels found");
+			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			goto fail;
+		}
+
+		switch (msg.dev_password_id) {
+		case DEV_PW_DEFAULT:
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: PIN from peer Label");
+			if (dev->wps_method != WPS_PIN_KEYPAD) {
+				wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+					"P2P: We have wps_method=%s -> "
+					"incompatible",
+					p2p_wps_method_str(dev->wps_method));
+				status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+				goto fail;
+			}
+			break;
+		case DEV_PW_REGISTRAR_SPECIFIED:
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: PIN from peer Display");
+			if (dev->wps_method != WPS_PIN_KEYPAD) {
+				wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+					"P2P: We have wps_method=%s -> "
+					"incompatible",
+					p2p_wps_method_str(dev->wps_method));
+				status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+				goto fail;
+			}
+			break;
+		case DEV_PW_USER_SPECIFIED:
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Peer entered PIN on Keypad");
+			if (dev->wps_method != WPS_PIN_LABEL &&
+			    dev->wps_method != WPS_PIN_DISPLAY) {
+				wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+					"P2P: We have wps_method=%s -> "
+					"incompatible",
+					p2p_wps_method_str(dev->wps_method));
+				status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+				goto fail;
+			}
+			break;
+		case DEV_PW_PUSHBUTTON:
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Peer using pushbutton");
+			if (dev->wps_method != WPS_PBC) {
+				wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+					"P2P: We have wps_method=%s -> "
+					"incompatible",
+					p2p_wps_method_str(dev->wps_method));
+				status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+				goto fail;
+			}
+			break;
+		default:
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Unsupported Device Password ID %d",
+				msg.dev_password_id);
+			status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+			goto fail;
+		}
+
+		if (go) {
+			struct p2p_channels intersection;
+			size_t i;
+			p2p_channels_intersect(&p2p->channels, &dev->channels,
+					       &intersection);
+			if (intersection.reg_classes == 0 ||
+			    intersection.reg_class[0].channels == 0) {
+				status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+				wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+					"P2P: No common channels found");
+				goto fail;
+			}
+			for (i = 0; i < intersection.reg_classes; i++) {
+				struct p2p_reg_class *c;
+				c = &intersection.reg_class[i];
+				wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
+					   c->reg_class);
+				wpa_hexdump(MSG_DEBUG, "P2P: channels",
+					    c->channel, c->channels);
+			}
+			if (!p2p_channels_includes(&intersection,
+						   p2p->op_reg_class,
+						   p2p->op_channel))
+				p2p_reselect_channel(p2p, &intersection);
+
+			p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+		}
+
+		dev->go_state = go ? LOCAL_GO : REMOTE_GO;
+		dev->oper_freq = p2p_channel_to_freq((const char *)
+						     msg.operating_channel,
+						     msg.operating_channel[3],
+						     msg.operating_channel[4]);
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating "
+			"channel preference: %d MHz", dev->oper_freq);
+
+		if (msg.config_timeout) {
+			dev->go_timeout = msg.config_timeout[0];
+			dev->client_timeout = msg.config_timeout[1];
+		}
+
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: GO Negotiation with " MACSTR, MAC2STR(sa));
+		if (p2p->state != P2P_IDLE)
+			p2p_stop_find_for_freq(p2p, rx_freq);
+		p2p_set_state(p2p, P2P_GO_NEG);
+		p2p_clear_timeout(p2p);
+		dev->dialog_token = msg.dialog_token;
+		os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+		p2p->go_neg_peer = dev;
+		status = P2P_SC_SUCCESS;
+	}
+
+fail:
+	if (dev)
+		dev->status = status;
+	resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, status,
+				     !tie_breaker);
+	p2p_parse_free(&msg);
+	if (resp == NULL)
+		return;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Sending GO Negotiation Response");
+	if (rx_freq > 0)
+		freq = rx_freq;
+	else
+		freq = p2p_channel_to_freq(p2p->cfg->country,
+					   p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+	if (freq < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unknown regulatory class/channel");
+		wpabuf_free(resp);
+		return;
+	}
+	if (status == P2P_SC_SUCCESS) {
+		p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE;
+		dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM;
+	} else
+		p2p->pending_action_state =
+			P2P_PENDING_GO_NEG_RESPONSE_FAILURE;
+	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+			    p2p->cfg->dev_addr,
+			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+	}
+
+	wpabuf_free(resp);
+}
+
+
+static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
+					     struct p2p_device *peer,
+					     u8 dialog_token, u8 status,
+					     const u8 *resp_chan, int go)
+{
+	struct wpabuf *buf;
+	u8 *len;
+	struct p2p_channels res;
+	u8 group_capab;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Building GO Negotiation Confirm");
+	buf = wpabuf_alloc(1000);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	p2p_buf_add_status(buf, status);
+	group_capab = 0;
+	if (peer->go_state == LOCAL_GO) {
+		if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP)
+			group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+		if (p2p->cross_connect)
+			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+		if (p2p->cfg->p2p_intra_bss)
+			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+	}
+	p2p_buf_add_capability(buf, p2p->dev_capab, group_capab);
+	if (go || resp_chan == NULL)
+		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+					      p2p->op_reg_class,
+					      p2p->op_channel);
+	else
+		p2p_buf_add_operating_channel(buf, (const char *) resp_chan,
+					      resp_chan[3], resp_chan[4]);
+	p2p_channels_intersect(&p2p->channels, &peer->channels, &res);
+	p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
+	if (go) {
+		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
+				     p2p->ssid_len);
+	}
+	p2p_buf_update_ie_hdr(buf, len);
+
+	return buf;
+}
+
+
+void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq)
+{
+	struct p2p_device *dev;
+	struct wpabuf *conf;
+	int go = -1;
+	struct p2p_message msg;
+	u8 status = P2P_SC_SUCCESS;
+	int freq;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received GO Negotiation Response from " MACSTR
+		" (freq=%d)", MAC2STR(sa), rx_freq);
+	dev = p2p_get_device(p2p, sa);
+	if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
+	    dev != p2p->go_neg_peer) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Not ready for GO negotiation with " MACSTR,
+			MAC2STR(sa));
+		return;
+	}
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Was not expecting GO Negotiation Response - "
+			"ignore");
+		p2p_parse_free(&msg);
+		return;
+	}
+	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
+
+	if (msg.dialog_token != dev->dialog_token) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unexpected Dialog Token %u (expected %u)",
+			msg.dialog_token, dev->dialog_token);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (!msg.status) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Status attribute received");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+	if (*msg.status) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: GO Negotiation rejected: status %d",
+			*msg.status);
+		dev->go_neg_req_sent = 0;
+		if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Wait for the peer to become ready for "
+				"GO Negotiation");
+			dev->flags |= P2P_DEV_NOT_YET_READY;
+			dev->wait_count = 0;
+			p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
+			p2p_set_timeout(p2p, 0, 0);
+		} else {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Stop GO Negotiation attempt");
+			p2p_go_neg_failed(p2p, dev, *msg.status);
+		}
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (!msg.capability) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Mandatory Capability attribute missing from GO "
+			"Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	if (!msg.p2p_device_info) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Mandatory P2P Device Info attribute missing "
+			"from GO Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	if (!msg.intended_addr) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Intended P2P Interface Address attribute "
+			"received");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+
+	if (!msg.go_intent) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No GO Intent attribute received");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+	if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Invalid GO Intent value (%u) received",
+			*msg.go_intent >> 1);
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+
+	go = p2p_go_det(p2p->go_intent, *msg.go_intent);
+	if (go < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Incompatible GO Intent");
+		status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+		goto fail;
+	}
+
+	if (!go && msg.group_id) {
+		/* Store SSID for Provisioning step */
+		p2p->ssid_len = msg.group_id_len - ETH_ALEN;
+		os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len);
+	} else if (!go) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Mandatory P2P Group ID attribute missing from "
+			"GO Negotiation Response");
+		p2p->ssid_len = 0;
+#ifdef CONFIG_P2P_STRICT
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	if (!msg.config_timeout) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Mandatory Configuration Timeout attribute "
+			"missing from GO Negotiation Response");
+#ifdef CONFIG_P2P_STRICT
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+#endif /* CONFIG_P2P_STRICT */
+	} else {
+		dev->go_timeout = msg.config_timeout[0];
+		dev->client_timeout = msg.config_timeout[1];
+	}
+
+	if (!msg.operating_channel && !go) {
+		/*
+		 * Note: P2P Client may omit Operating Channel attribute to
+		 * indicate it does not have a preference.
+		 */
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Operating Channel attribute received");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+	if (!msg.channel_list) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Channel List attribute received");
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+
+	if (p2p_peer_channels(p2p, dev, msg.channel_list,
+			      msg.channel_list_len) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No common channels found");
+		status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+		goto fail;
+	}
+
+	if (msg.operating_channel) {
+		dev->oper_freq = p2p_channel_to_freq((const char *)
+						     msg.operating_channel,
+						     msg.operating_channel[3],
+						     msg.operating_channel[4]);
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating "
+			"channel preference: %d MHz", dev->oper_freq);
+	} else
+		dev->oper_freq = 0;
+
+	switch (msg.dev_password_id) {
+	case DEV_PW_DEFAULT:
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: PIN from peer Label");
+		if (dev->wps_method != WPS_PIN_KEYPAD) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: We have wps_method=%s -> "
+				"incompatible",
+				p2p_wps_method_str(dev->wps_method));
+			status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+			goto fail;
+		}
+		break;
+	case DEV_PW_REGISTRAR_SPECIFIED:
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: PIN from peer Display");
+		if (dev->wps_method != WPS_PIN_KEYPAD) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: We have wps_method=%s -> "
+				"incompatible",
+				p2p_wps_method_str(dev->wps_method));
+			status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+			goto fail;
+		}
+		break;
+	case DEV_PW_USER_SPECIFIED:
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Peer entered PIN on Keypad");
+		if (dev->wps_method != WPS_PIN_LABEL &&
+		    dev->wps_method != WPS_PIN_DISPLAY) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: We have wps_method=%s -> "
+				"incompatible",
+				p2p_wps_method_str(dev->wps_method));
+			status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+			goto fail;
+		}
+		break;
+	case DEV_PW_PUSHBUTTON:
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Peer using pushbutton");
+		if (dev->wps_method != WPS_PBC) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: We have wps_method=%s -> "
+				"incompatible",
+				p2p_wps_method_str(dev->wps_method));
+			status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+			goto fail;
+		}
+		break;
+	default:
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported Device Password ID %d",
+			msg.dev_password_id);
+		status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+		goto fail;
+	}
+
+	if (go) {
+		struct p2p_channels intersection;
+		size_t i;
+		p2p_channels_intersect(&p2p->channels, &dev->channels,
+				       &intersection);
+		if (intersection.reg_classes == 0 ||
+		    intersection.reg_class[0].channels == 0) {
+			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: No common channels found");
+			goto fail;
+		}
+		for (i = 0; i < intersection.reg_classes; i++) {
+			struct p2p_reg_class *c;
+			c = &intersection.reg_class[i];
+			wpa_printf(MSG_DEBUG, "P2P: reg_class %u",
+				   c->reg_class);
+			wpa_hexdump(MSG_DEBUG, "P2P: channels",
+				    c->channel, c->channels);
+		}
+		if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+					   p2p->op_channel))
+			p2p_reselect_channel(p2p, &intersection);
+
+		p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+	}
+
+	p2p_set_state(p2p, P2P_GO_NEG);
+	p2p_clear_timeout(p2p);
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: GO Negotiation with " MACSTR, MAC2STR(sa));
+	os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
+
+fail:
+	conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status,
+				     msg.operating_channel, go);
+	p2p_parse_free(&msg);
+	if (conf == NULL)
+		return;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Sending GO Negotiation Confirm");
+	if (status == P2P_SC_SUCCESS) {
+		p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
+		dev->go_state = go ? LOCAL_GO : REMOTE_GO;
+	} else
+		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	if (rx_freq > 0)
+		freq = rx_freq;
+	else
+		freq = dev->listen_freq;
+	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
+			    wpabuf_head(conf), wpabuf_len(conf), 200) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+		p2p_go_neg_failed(p2p, dev, -1);
+	}
+	wpabuf_free(conf);
+}
+
+
+void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len)
+{
+	struct p2p_device *dev;
+	struct p2p_message msg;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received GO Negotiation Confirm from " MACSTR,
+		MAC2STR(sa));
+	dev = p2p_get_device(p2p, sa);
+	if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
+	    dev != p2p->go_neg_peer) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Not ready for GO negotiation with " MACSTR,
+			MAC2STR(sa));
+		return;
+	}
+
+	if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopped waiting "
+			"for TX status on GO Negotiation Response since we "
+			"already received Confirmation");
+		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	}
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Was not expecting GO Negotiation Confirm - "
+			"ignore");
+		return;
+	}
+	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+
+	if (msg.dialog_token != dev->dialog_token) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unexpected Dialog Token %u (expected %u)",
+			msg.dialog_token, dev->dialog_token);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (!msg.status) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Status attribute received");
+		p2p_parse_free(&msg);
+		return;
+	}
+	if (*msg.status) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: GO Negotiation rejected: status %d",
+			*msg.status);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (dev->go_state == REMOTE_GO && msg.group_id) {
+		/* Store SSID for Provisioning step */
+		p2p->ssid_len = msg.group_id_len - ETH_ALEN;
+		os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len);
+	} else if (dev->go_state == REMOTE_GO) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Mandatory P2P Group ID attribute missing from "
+			"GO Negotiation Confirmation");
+		p2p->ssid_len = 0;
+#ifdef CONFIG_P2P_STRICT
+		p2p_parse_free(&msg);
+		return;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	if (!msg.operating_channel) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Mandatory Operating Channel attribute missing "
+			"from GO Negotiation Confirmation");
+#ifdef CONFIG_P2P_STRICT
+		p2p_parse_free(&msg);
+		return;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	if (!msg.channel_list) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Mandatory Operating Channel attribute missing "
+			"from GO Negotiation Confirmation");
+#ifdef CONFIG_P2P_STRICT
+		p2p_parse_free(&msg);
+		return;
+#endif /* CONFIG_P2P_STRICT */
+	}
+
+	p2p_parse_free(&msg);
+
+	if (dev->go_state == UNKNOWN_GO) {
+		/*
+		 * This should not happen since GO negotiation has already
+		 * been completed.
+		 */
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unexpected GO Neg state - do not know which end "
+			"becomes GO");
+		return;
+	}
+
+	p2p_go_complete(p2p, dev);
+}
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
new file mode 100644
index 0000000..14a475d
--- /dev/null
+++ b/src/p2p/p2p_group.c
@@ -0,0 +1,673 @@
+/*
+ * Wi-Fi Direct - P2P group operations
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wps/wps_defs.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+struct p2p_group_member {
+	struct p2p_group_member *next;
+	u8 addr[ETH_ALEN]; /* P2P Interface Address */
+	u8 dev_addr[ETH_ALEN]; /* P2P Device Address */
+	struct wpabuf *p2p_ie;
+	struct wpabuf *client_info;
+	u8 dev_capab;
+};
+
+/**
+ * struct p2p_group - Internal P2P module per-group data
+ */
+struct p2p_group {
+	struct p2p_data *p2p;
+	struct p2p_group_config *cfg;
+	struct p2p_group_member *members;
+	unsigned int num_members;
+	int group_formation;
+	int beacon_update;
+	struct wpabuf *noa;
+};
+
+
+static void p2p_group_update_ies(struct p2p_group *group);
+
+
+struct p2p_group * p2p_group_init(struct p2p_data *p2p,
+				  struct p2p_group_config *config)
+{
+	struct p2p_group *group, **groups;
+
+	group = os_zalloc(sizeof(*group));
+	if (group == NULL)
+		return NULL;
+
+	groups = os_realloc(p2p->groups, (p2p->num_groups + 1) *
+			    sizeof(struct p2p_group *));
+	if (groups == NULL) {
+		os_free(group);
+		return NULL;
+	}
+	groups[p2p->num_groups++] = group;
+	p2p->groups = groups;
+
+	group->p2p = p2p;
+	group->cfg = config;
+	group->group_formation = 1;
+	group->beacon_update = 1;
+	p2p_group_update_ies(group);
+	group->cfg->idle_update(group->cfg->cb_ctx, 1);
+
+	return group;
+}
+
+
+static void p2p_group_free_member(struct p2p_group_member *m)
+{
+	wpabuf_free(m->p2p_ie);
+	wpabuf_free(m->client_info);
+	os_free(m);
+}
+
+
+static void p2p_group_free_members(struct p2p_group *group)
+{
+	struct p2p_group_member *m, *prev;
+	m = group->members;
+	group->members = NULL;
+	group->num_members = 0;
+	while (m) {
+		prev = m;
+		m = m->next;
+		p2p_group_free_member(prev);
+	}
+}
+
+
+void p2p_group_deinit(struct p2p_group *group)
+{
+	size_t g;
+	struct p2p_data *p2p;
+
+	if (group == NULL)
+		return;
+
+	p2p = group->p2p;
+
+	for (g = 0; g < p2p->num_groups; g++) {
+		if (p2p->groups[g] == group) {
+			while (g + 1 < p2p->num_groups) {
+				p2p->groups[g] = p2p->groups[g + 1];
+				g++;
+			}
+			p2p->num_groups--;
+			break;
+		}
+	}
+
+	p2p_group_free_members(group);
+	os_free(group->cfg);
+	wpabuf_free(group->noa);
+	os_free(group);
+}
+
+
+static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m)
+{
+	if (m->client_info == NULL)
+		return;
+	if (wpabuf_tailroom(ie) < wpabuf_len(m->client_info) + 1)
+		return;
+	wpabuf_put_buf(ie, m->client_info);
+}
+
+
+static void p2p_group_add_common_ies(struct p2p_group *group,
+				     struct wpabuf *ie)
+{
+	u8 dev_capab = 0, group_capab = 0;
+
+	/* P2P Capability */
+	dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+	dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
+	group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
+	if (group->cfg->persistent_group)
+		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+	if (group->p2p->cfg->p2p_intra_bss)
+		group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+	if (group->group_formation)
+		group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION;
+	if (group->p2p->cross_connect)
+		group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+	if (group->num_members >= group->cfg->max_clients)
+		group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT;
+	p2p_buf_add_capability(ie, dev_capab, group_capab);
+}
+
+
+static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa)
+{
+	if (noa == NULL)
+		return;
+	/* Notice of Absence */
+	wpabuf_put_u8(ie, P2P_ATTR_NOTICE_OF_ABSENCE);
+	wpabuf_put_le16(ie, wpabuf_len(noa));
+	wpabuf_put_buf(ie, noa);
+}
+
+
+static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
+{
+	struct wpabuf *ie;
+	u8 *len;
+
+	ie = wpabuf_alloc(257);
+	if (ie == NULL)
+		return NULL;
+
+	len = p2p_buf_add_ie_hdr(ie);
+	p2p_group_add_common_ies(group, ie);
+	p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr);
+	p2p_group_add_noa(ie, group->noa);
+	p2p_buf_update_ie_hdr(ie, len);
+
+	return ie;
+}
+
+
+static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
+{
+	u8 *group_info;
+	struct wpabuf *ie;
+	struct p2p_group_member *m;
+	u8 *len;
+
+	ie = wpabuf_alloc(257);
+	if (ie == NULL)
+		return NULL;
+
+	len = p2p_buf_add_ie_hdr(ie);
+
+	p2p_group_add_common_ies(group, ie);
+	p2p_group_add_noa(ie, group->noa);
+
+	/* P2P Device Info */
+	p2p_buf_add_device_info(ie, group->p2p, NULL);
+
+	/* P2P Group Info */
+	group_info = wpabuf_put(ie, 0);
+	wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO);
+	wpabuf_put_le16(ie, 0); /* Length to be filled */
+	for (m = group->members; m; m = m->next)
+		p2p_client_info(ie, m);
+	WPA_PUT_LE16(group_info + 1,
+		     (u8 *) wpabuf_put(ie, 0) - group_info - 3);
+
+	p2p_buf_update_ie_hdr(ie, len);
+	return ie;
+}
+
+
+static void p2p_group_update_ies(struct p2p_group *group)
+{
+	struct wpabuf *beacon_ie;
+	struct wpabuf *probe_resp_ie;
+
+	probe_resp_ie = p2p_group_build_probe_resp_ie(group);
+	if (probe_resp_ie == NULL)
+		return;
+	wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Probe Response P2P IE",
+			probe_resp_ie);
+
+	if (group->beacon_update) {
+		beacon_ie = p2p_group_build_beacon_ie(group);
+		if (beacon_ie)
+			group->beacon_update = 0;
+		wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Beacon P2P IE",
+				beacon_ie);
+	} else
+		beacon_ie = NULL;
+
+	group->cfg->ie_update(group->cfg->cb_ctx, beacon_ie, probe_resp_ie);
+}
+
+
+/**
+ * p2p_build_client_info - Build P2P Client Info Descriptor
+ * @addr: MAC address of the peer device
+ * @p2p_ie: P2P IE from (Re)Association Request
+ * @dev_capab: Buffer for returning Device Capability
+ * @dev_addr: Buffer for returning P2P Device Address
+ * Returns: P2P Client Info Descriptor or %NULL on failure
+ *
+ * This function builds P2P Client Info Descriptor based on the information
+ * available from (Re)Association Request frame. Group owner can use this to
+ * build the P2P Group Info attribute for Probe Response frames.
+ */
+static struct wpabuf * p2p_build_client_info(const u8 *addr,
+					     struct wpabuf *p2p_ie,
+					     u8 *dev_capab, u8 *dev_addr)
+{
+	const u8 *spos;
+	struct p2p_message msg;
+	u8 *len_pos;
+	struct wpabuf *buf;
+
+	if (p2p_ie == NULL)
+		return NULL;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p_ie, &msg) ||
+	    msg.capability == NULL || msg.p2p_device_info == NULL)
+		return NULL;
+
+	buf = wpabuf_alloc(ETH_ALEN + 1 + 1 + msg.p2p_device_info_len);
+	if (buf == NULL)
+		return NULL;
+
+	*dev_capab = msg.capability[0];
+	os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+
+	spos = msg.p2p_device_info; /* P2P Device address */
+
+	/* P2P Client Info Descriptor */
+	/* Length to be set */
+	len_pos = wpabuf_put(buf, 1);
+	/* P2P Device address */
+	wpabuf_put_data(buf, spos, ETH_ALEN);
+	/* P2P Interface address */
+	wpabuf_put_data(buf, addr, ETH_ALEN);
+	/* Device Capability Bitmap */
+	wpabuf_put_u8(buf, msg.capability[0]);
+	/*
+	 * Config Methods, Primary Device Type, Number of Secondary Device
+	 * Types, Secondary Device Type List, Device Name copied from
+	 * Device Info
+	 */
+	wpabuf_put_data(buf, spos + ETH_ALEN,
+			msg.p2p_device_info_len - ETH_ALEN);
+
+	*len_pos = wpabuf_len(buf) - 1;
+
+
+	return buf;
+}
+
+
+int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
+			  const u8 *ie, size_t len)
+{
+	struct p2p_group_member *m;
+
+	if (group == NULL)
+		return -1;
+
+	m = os_zalloc(sizeof(*m));
+	if (m == NULL)
+		return -1;
+	os_memcpy(m->addr, addr, ETH_ALEN);
+	m->p2p_ie = ieee802_11_vendor_ie_concat(ie, len, P2P_IE_VENDOR_TYPE);
+	if (m->p2p_ie) {
+		m->client_info = p2p_build_client_info(addr, m->p2p_ie,
+						       &m->dev_capab,
+						       m->dev_addr);
+	}
+
+	m->next = group->members;
+	group->members = m;
+	group->num_members++;
+	wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR
+		" to group (p2p=%d client_info=%d); num_members=%u/%u",
+		MAC2STR(addr), m->p2p_ie ? 1 : 0, m->client_info ? 1 : 0,
+		group->num_members, group->cfg->max_clients);
+	if (group->num_members == group->cfg->max_clients)
+		group->beacon_update = 1;
+	p2p_group_update_ies(group);
+	if (group->num_members == 1)
+		group->cfg->idle_update(group->cfg->cb_ctx, 0);
+
+	return 0;
+}
+
+
+struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
+{
+	struct wpabuf *resp;
+	u8 *rlen;
+
+	/*
+	 * (Re)Association Response - P2P IE
+	 * Status attribute (shall be present when association request is
+	 *	denied)
+	 * Extended Listen Timing (may be present)
+	 */
+	resp = wpabuf_alloc(20);
+	if (resp == NULL)
+		return NULL;
+	rlen = p2p_buf_add_ie_hdr(resp);
+	if (status != P2P_SC_SUCCESS)
+		p2p_buf_add_status(resp, status);
+	p2p_buf_update_ie_hdr(resp, rlen);
+
+	return resp;
+}
+
+
+void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr)
+{
+	struct p2p_group_member *m, *prev;
+
+	if (group == NULL)
+		return;
+
+	m = group->members;
+	prev = NULL;
+	while (m) {
+		if (os_memcmp(m->addr, addr, ETH_ALEN) == 0)
+			break;
+		prev = m;
+		m = m->next;
+	}
+
+	if (m) {
+		if (prev)
+			prev->next = m->next;
+		else
+			group->members = m->next;
+		p2p_group_free_member(m);
+		group->num_members--;
+		wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Remove "
+			"client " MACSTR " from group; num_members=%u/%u",
+			MAC2STR(addr), group->num_members,
+			group->cfg->max_clients);
+		if (group->num_members == group->cfg->max_clients - 1)
+			group->beacon_update = 1;
+		p2p_group_update_ies(group);
+		if (group->num_members == 0)
+			group->cfg->idle_update(group->cfg->cb_ctx, 1);
+	}
+}
+
+
+/**
+ * p2p_match_dev_type_member - Match client device type with requested type
+ * @m: Group member
+ * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
+ * Returns: 1 on match, 0 on mismatch
+ *
+ * This function can be used to match the Requested Device Type attribute in
+ * WPS IE with the device types of a group member for deciding whether a GO
+ * should reply to a Probe Request frame.
+ */
+static int p2p_match_dev_type_member(struct p2p_group_member *m,
+				     struct wpabuf *wps)
+{
+	const u8 *pos, *end;
+	struct wps_parse_attr attr;
+	u8 num_sec;
+
+	if (m->client_info == NULL || wps == NULL)
+		return 0;
+
+	pos = wpabuf_head(m->client_info);
+	end = pos + wpabuf_len(m->client_info);
+
+	pos += 1 + 2 * ETH_ALEN + 1 + 2;
+	if (end - pos < WPS_DEV_TYPE_LEN + 1)
+		return 0;
+
+	if (wps_parse_msg(wps, &attr))
+		return 1; /* assume no Requested Device Type attributes */
+
+	if (attr.num_req_dev_type == 0)
+		return 1; /* no Requested Device Type attributes -> match */
+
+	if (dev_type_list_match(pos, attr.req_dev_type, attr.num_req_dev_type))
+		return 1; /* Match with client Primary Device Type */
+
+	pos += WPS_DEV_TYPE_LEN;
+	num_sec = *pos++;
+	if (end - pos < num_sec * WPS_DEV_TYPE_LEN)
+		return 0;
+	while (num_sec > 0) {
+		num_sec--;
+		if (dev_type_list_match(pos, attr.req_dev_type,
+					attr.num_req_dev_type))
+			return 1; /* Match with client Secondary Device Type */
+		pos += WPS_DEV_TYPE_LEN;
+	}
+
+	/* No matching device type found */
+	return 0;
+}
+
+
+int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps)
+{
+	struct p2p_group_member *m;
+
+	if (p2p_match_dev_type(group->p2p, wps))
+		return 1; /* Match with own device type */
+
+	for (m = group->members; m; m = m->next) {
+		if (p2p_match_dev_type_member(m, wps))
+			return 1; /* Match with group client device type */
+	}
+
+	/* No match with Requested Device Type */
+	return 0;
+}
+
+
+void p2p_group_notif_formation_done(struct p2p_group *group)
+{
+	if (group == NULL)
+		return;
+	group->group_formation = 0;
+	group->beacon_update = 1;
+	p2p_group_update_ies(group);
+}
+
+
+int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
+			size_t noa_len)
+{
+	if (noa == NULL) {
+		wpabuf_free(group->noa);
+		group->noa = NULL;
+	} else {
+		if (group->noa) {
+			if (wpabuf_size(group->noa) >= noa_len) {
+				group->noa->size = 0;
+				wpabuf_put_data(group->noa, noa, noa_len);
+			} else {
+				wpabuf_free(group->noa);
+				group->noa = NULL;
+			}
+		}
+
+		if (!group->noa) {
+			group->noa = wpabuf_alloc_copy(noa, noa_len);
+			if (group->noa == NULL)
+				return -1;
+		}
+	}
+
+	group->beacon_update = 1;
+	p2p_group_update_ies(group);
+	return 0;
+}
+
+
+static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group,
+						      const u8 *dev_id)
+{
+	struct p2p_group_member *m;
+
+	for (m = group->members; m; m = m->next) {
+		if (os_memcmp(dev_id, m->dev_addr, ETH_ALEN) == 0)
+			return m;
+	}
+
+	return NULL;
+}
+
+
+static struct p2p_group_member * p2p_group_get_client_iface(
+	struct p2p_group *group, const u8 *interface_addr)
+{
+	struct p2p_group_member *m;
+
+	for (m = group->members; m; m = m->next) {
+		if (os_memcmp(interface_addr, m->addr, ETH_ALEN) == 0)
+			return m;
+	}
+
+	return NULL;
+}
+
+
+static struct wpabuf * p2p_build_go_disc_req(void)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(100);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_action_hdr(buf, P2P_GO_DISC_REQ, 0);
+
+	return buf;
+}
+
+
+int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
+			  const u8 *searching_dev, int rx_freq)
+{
+	struct p2p_group_member *m;
+	struct wpabuf *req;
+	struct p2p_data *p2p = group->p2p;
+	int freq;
+
+	m = p2p_group_get_client(group, dev_id);
+	if (m == NULL || m->client_info == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: Requested client was not in this "
+			   "group " MACSTR,
+			   MAC2STR(group->cfg->interface_addr));
+		return -1;
+	}
+
+	if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+		wpa_printf(MSG_DEBUG, "P2P: Requested client does not support "
+			   "client discoverability");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Schedule GO Discoverability Request to be "
+		   "sent to " MACSTR, MAC2STR(dev_id));
+
+	req = p2p_build_go_disc_req();
+	if (req == NULL)
+		return -1;
+
+	/* TODO: Should really use group operating frequency here */
+	freq = rx_freq;
+
+	p2p->pending_action_state = P2P_PENDING_GO_DISC_REQ;
+	if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr,
+				  group->cfg->interface_addr,
+				  group->cfg->interface_addr,
+				  wpabuf_head(req), wpabuf_len(req), 200) < 0)
+	{
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+	}
+
+	wpabuf_free(req);
+
+	return 0;
+}
+
+
+const u8 * p2p_group_get_interface_addr(struct p2p_group *group)
+{
+	return group->cfg->interface_addr;
+}
+
+
+u8 p2p_group_presence_req(struct p2p_group *group,
+			  const u8 *client_interface_addr,
+			  const u8 *noa, size_t noa_len)
+{
+	struct p2p_group_member *m;
+	u8 curr_noa[50];
+	int curr_noa_len;
+
+	m = p2p_group_get_client_iface(group, client_interface_addr);
+	if (m == NULL || m->client_info == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: Client was not in this group");
+		return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "P2P: Presence Request NoA", noa, noa_len);
+
+	if (group->p2p->cfg->get_noa)
+		curr_noa_len = group->p2p->cfg->get_noa(
+			group->p2p->cfg->cb_ctx, group->cfg->interface_addr,
+			curr_noa, sizeof(curr_noa));
+	else
+		curr_noa_len = -1;
+	if (curr_noa_len < 0)
+		wpa_printf(MSG_DEBUG, "P2P: Failed to fetch current NoA");
+	else if (curr_noa_len == 0)
+		wpa_printf(MSG_DEBUG, "P2P: No NoA being advertized");
+	else
+		wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa,
+			    curr_noa_len);
+
+	/* TODO: properly process request and store copy */
+	if (curr_noa_len > 0)
+		return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+
+	return P2P_SC_SUCCESS;
+}
+
+
+unsigned int p2p_get_group_num_members(struct p2p_group *group)
+{
+	return group->num_members;
+}
+
+
+const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next)
+{
+	struct p2p_group_member *iter = *next;
+
+	if (!iter)
+		iter = group->members;
+	else
+		iter = iter->next;
+
+	*next = iter;
+
+	if (!iter)
+		return NULL;
+
+	return iter->addr;
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
new file mode 100644
index 0000000..68b1194
--- /dev/null
+++ b/src/p2p/p2p_i.h
@@ -0,0 +1,638 @@
+/*
+ * P2P - Internal definitions for 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.
+ */
+
+#ifndef P2P_I_H
+#define P2P_I_H
+
+#include "utils/list.h"
+#include "p2p.h"
+
+/* TODO: add removal of expired P2P device entries */
+
+enum p2p_go_state {
+	UNKNOWN_GO,
+	LOCAL_GO,
+	REMOTE_GO
+};
+
+/**
+ * struct p2p_device - P2P Device data (internal to P2P module)
+ */
+struct p2p_device {
+	struct dl_list list;
+	struct os_time last_seen;
+	int listen_freq;
+	int level;
+	enum p2p_wps_method wps_method;
+
+	struct p2p_peer_info info;
+
+	/*
+	 * If the peer was discovered based on an interface address (e.g., GO
+	 * from Beacon/Probe Response), the interface address is stored here.
+	 * p2p_device_addr must still be set in such a case to the unique
+	 * identifier for the P2P Device.
+	 */
+	u8 interface_addr[ETH_ALEN];
+
+	/*
+	 * P2P Device Address of the GO in whose group this P2P Device is a
+	 * client.
+	 */
+	u8 member_in_go_dev[ETH_ALEN];
+
+	/*
+	 * P2P Interface Address of the GO in whose group this P2P Device is a
+	 * client.
+	 */
+	u8 member_in_go_iface[ETH_ALEN];
+
+	int go_neg_req_sent;
+	enum p2p_go_state go_state;
+	u8 dialog_token;
+	u8 intended_addr[ETH_ALEN];
+
+	char country[3];
+	struct p2p_channels channels;
+	int oper_freq;
+	u8 oper_ssid[32];
+	size_t oper_ssid_len;
+
+	/**
+	 * req_config_methods - Pending provisioning discovery methods
+	 */
+	u16 req_config_methods;
+
+#define P2P_DEV_PROBE_REQ_ONLY BIT(0)
+#define P2P_DEV_REPORTED BIT(1)
+#define P2P_DEV_NOT_YET_READY BIT(2)
+#define P2P_DEV_SD_INFO BIT(3)
+#define P2P_DEV_SD_SCHEDULE BIT(4)
+#define P2P_DEV_PD_PEER_DISPLAY BIT(5)
+#define P2P_DEV_PD_PEER_KEYPAD BIT(6)
+#define P2P_DEV_USER_REJECTED BIT(7)
+#define P2P_DEV_PEER_WAITING_RESPONSE BIT(8)
+#define P2P_DEV_PREFER_PERSISTENT_GROUP BIT(9)
+#define P2P_DEV_WAIT_GO_NEG_RESPONSE BIT(10)
+#define P2P_DEV_WAIT_GO_NEG_CONFIRM BIT(11)
+#define P2P_DEV_GROUP_CLIENT_ONLY BIT(12)
+#define P2P_DEV_FORCE_FREQ BIT(13)
+#define P2P_DEV_PD_FOR_JOIN BIT(14)
+#define P2P_DEV_REPORTED_ONCE BIT(15)
+	unsigned int flags;
+
+	int status; /* enum p2p_status_code */
+	unsigned int wait_count;
+	unsigned int connect_reqs;
+	unsigned int invitation_reqs;
+
+	u16 ext_listen_period;
+	u16 ext_listen_interval;
+
+	u8 go_timeout;
+	u8 client_timeout;
+};
+
+struct p2p_sd_query {
+	struct p2p_sd_query *next;
+	u8 peer[ETH_ALEN];
+	int for_all_peers;
+	struct wpabuf *tlvs;
+};
+
+struct p2p_pending_action_tx {
+	unsigned int freq;
+	u8 dst[ETH_ALEN];
+	u8 src[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+	size_t len;
+	unsigned int wait_time;
+	/* Followed by len octets of the frame */
+};
+
+/**
+ * struct p2p_data - P2P module data (internal to P2P module)
+ */
+struct p2p_data {
+	/**
+	 * cfg - P2P module configuration
+	 *
+	 * This is included in the same memory allocation with the
+	 * struct p2p_data and as such, must not be freed separately.
+	 */
+	struct p2p_config *cfg;
+
+	/**
+	 * state - The current P2P state
+	 */
+	enum p2p_state {
+		/**
+		 * P2P_IDLE - Idle
+		 */
+		P2P_IDLE,
+
+		/**
+		 * P2P_SEARCH - Search (Device Discovery)
+		 */
+		P2P_SEARCH,
+
+		/**
+		 * P2P_CONNECT - Trying to start GO Negotiation
+		 */
+		P2P_CONNECT,
+
+		/**
+		 * P2P_CONNECT_LISTEN - Listen during GO Negotiation start
+		 */
+		P2P_CONNECT_LISTEN,
+
+		/**
+		 * P2P_GO_NEG - In GO Negotiation
+		 */
+		P2P_GO_NEG,
+
+		/**
+		 * P2P_LISTEN_ONLY - Listen only
+		 */
+		P2P_LISTEN_ONLY,
+
+		/**
+		 * P2P_WAIT_PEER_CONNECT - Waiting peer in List for GO Neg
+		 */
+		P2P_WAIT_PEER_CONNECT,
+
+		/**
+		 * P2P_WAIT_PEER_IDLE - Waiting peer idle for GO Neg
+		 */
+		P2P_WAIT_PEER_IDLE,
+
+		/**
+		 * P2P_SD_DURING_FIND - Service Discovery during find
+		 */
+		P2P_SD_DURING_FIND,
+
+		/**
+		 * P2P_PROVISIONING - Provisioning (during group formation)
+		 */
+		P2P_PROVISIONING,
+
+		/**
+		 * P2P_PD_DURING_FIND - Provision Discovery during find
+		 */
+		P2P_PD_DURING_FIND,
+
+		/**
+		 * P2P_INVITE - Trying to start Invite
+		 */
+		P2P_INVITE,
+
+		/**
+		 * P2P_INVITE_LISTEN - Listen during Invite
+		 */
+		P2P_INVITE_LISTEN,
+	} state;
+
+	/**
+	 * min_disc_int - minDiscoverableInterval
+	 */
+	int min_disc_int;
+
+	/**
+	 * max_disc_int - maxDiscoverableInterval
+	 */
+	int max_disc_int;
+
+	/**
+	 * devices - List of known P2P Device peers
+	 */
+	struct dl_list devices;
+
+	/**
+	 * go_neg_peer - Pointer to GO Negotiation peer
+	 */
+	struct p2p_device *go_neg_peer;
+
+	/**
+	 * invite_peer - Pointer to Invite peer
+	 */
+	struct p2p_device *invite_peer;
+
+	const u8 *invite_go_dev_addr;
+	u8 invite_go_dev_addr_buf[ETH_ALEN];
+
+	/**
+	 * sd_peer - Pointer to Service Discovery peer
+	 */
+	struct p2p_device *sd_peer;
+
+	/**
+	 * sd_query - Pointer to Service Discovery query
+	 */
+	struct p2p_sd_query *sd_query;
+
+	/* GO Negotiation data */
+
+	/**
+	 * intended_addr - Local Intended P2P Interface Address
+	 *
+	 * This address is used during group owner negotiation as the Intended
+	 * P2P Interface Address and the group interface will be created with
+	 * address as the local address in case of successfully completed
+	 * negotiation.
+	 */
+	u8 intended_addr[ETH_ALEN];
+
+	/**
+	 * go_intent - Local GO Intent to be used during GO Negotiation
+	 */
+	u8 go_intent;
+
+	/**
+	 * next_tie_breaker - Next tie-breaker value to use in GO Negotiation
+	 */
+	u8 next_tie_breaker;
+
+	/**
+	 * ssid - Selected SSID for GO Negotiation (if local end will be GO)
+	 */
+	u8 ssid[32];
+
+	/**
+	 * ssid_len - ssid length in octets
+	 */
+	size_t ssid_len;
+
+	/**
+	 * Regulatory class for own operational channel
+	 */
+	u8 op_reg_class;
+
+	/**
+	 * op_channel - Own operational channel
+	 */
+	u8 op_channel;
+
+	/**
+	 * channels - Own supported regulatory classes and channels
+	 *
+	 * List of supposerted channels per regulatory class. The regulatory
+	 * classes are defined in IEEE Std 802.11-2007 Annex J and the
+	 * numbering of the clases depends on the configured country code.
+	 */
+	struct p2p_channels channels;
+
+	enum p2p_pending_action_state {
+		P2P_NO_PENDING_ACTION,
+		P2P_PENDING_GO_NEG_REQUEST,
+		P2P_PENDING_GO_NEG_RESPONSE,
+		P2P_PENDING_GO_NEG_RESPONSE_FAILURE,
+		P2P_PENDING_GO_NEG_CONFIRM,
+		P2P_PENDING_SD,
+		P2P_PENDING_PD,
+		P2P_PENDING_INVITATION_REQUEST,
+		P2P_PENDING_INVITATION_RESPONSE,
+		P2P_PENDING_DEV_DISC_REQUEST,
+		P2P_PENDING_DEV_DISC_RESPONSE,
+		P2P_PENDING_GO_DISC_REQ
+	} pending_action_state;
+
+	unsigned int pending_listen_freq;
+	unsigned int pending_listen_sec;
+	unsigned int pending_listen_usec;
+
+	u8 dev_capab;
+
+	int in_listen;
+	int drv_in_listen;
+
+	/**
+	 * sd_queries - Pending service discovery queries
+	 */
+	struct p2p_sd_query *sd_queries;
+
+	/**
+	 * srv_update_indic - Service Update Indicator for local services
+	 */
+	u16 srv_update_indic;
+
+	struct wpabuf *sd_resp; /* Fragmented SD response */
+	u8 sd_resp_addr[ETH_ALEN];
+	u8 sd_resp_dialog_token;
+	size_t sd_resp_pos; /* Offset in sd_resp */
+	u8 sd_frag_id;
+
+	struct wpabuf *sd_rx_resp; /* Reassembled SD response */
+	u16 sd_rx_update_indic;
+
+	/* P2P Invitation data */
+	enum p2p_invite_role inv_role;
+	u8 inv_bssid[ETH_ALEN];
+	int inv_bssid_set;
+	u8 inv_ssid[32];
+	size_t inv_ssid_len;
+	u8 inv_sa[ETH_ALEN];
+	u8 inv_group_bssid[ETH_ALEN];
+	u8 *inv_group_bssid_ptr;
+	u8 inv_go_dev_addr[ETH_ALEN];
+	u8 inv_status;
+	int inv_op_freq;
+	int inv_persistent;
+
+	enum p2p_discovery_type find_type;
+	u8 last_prog_scan_class;
+	u8 last_prog_scan_chan;
+	int p2p_scan_running;
+	enum p2p_after_scan {
+		P2P_AFTER_SCAN_NOTHING,
+		P2P_AFTER_SCAN_LISTEN,
+		P2P_AFTER_SCAN_CONNECT
+	} start_after_scan;
+	u8 after_scan_peer[ETH_ALEN];
+	struct p2p_pending_action_tx *after_scan_tx;
+
+	/* Requested device types for find/search */
+	unsigned int num_req_dev_types;
+	u8 *req_dev_types;
+
+	struct p2p_group **groups;
+	size_t num_groups;
+
+	struct p2p_device *pending_client_disc_go;
+	u8 pending_client_disc_addr[ETH_ALEN];
+	u8 pending_dev_disc_dialog_token;
+	u8 pending_dev_disc_addr[ETH_ALEN];
+	int pending_dev_disc_freq;
+	unsigned int pending_client_disc_freq;
+
+	int ext_listen_only;
+	unsigned int ext_listen_period;
+	unsigned int ext_listen_interval;
+	unsigned int ext_listen_interval_sec;
+	unsigned int ext_listen_interval_usec;
+
+	u8 peer_filter[ETH_ALEN];
+
+	int cross_connect;
+
+	int best_freq_24;
+	int best_freq_5;
+	int best_freq_overall;
+
+	/**
+	 * wps_vendor_ext - WPS Vendor Extensions to add
+	 */
+	struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+};
+
+/**
+ * struct p2p_message - Parsed P2P message (or P2P IE)
+ */
+struct p2p_message {
+	struct wpabuf *p2p_attributes;
+	struct wpabuf *wps_attributes;
+
+	u8 dialog_token;
+
+	const u8 *capability;
+	const u8 *go_intent;
+	const u8 *status;
+	const u8 *listen_channel;
+	const u8 *operating_channel;
+	const u8 *channel_list;
+	u8 channel_list_len;
+	const u8 *config_timeout;
+	const u8 *intended_addr;
+	const u8 *group_bssid;
+	const u8 *invitation_flags;
+
+	const u8 *group_info;
+	size_t group_info_len;
+
+	const u8 *group_id;
+	size_t group_id_len;
+
+	const u8 *device_id;
+
+	const u8 *manageability;
+
+	const u8 *noa;
+	size_t noa_len;
+
+	const u8 *ext_listen_timing;
+
+	const u8 *minor_reason_code;
+
+	/* P2P Device Info */
+	const u8 *p2p_device_info;
+	size_t p2p_device_info_len;
+	const u8 *p2p_device_addr;
+	const u8 *pri_dev_type;
+	u8 num_sec_dev_types;
+	char device_name[33];
+	u16 config_methods;
+
+	/* WPS IE */
+	u16 dev_password_id;
+	u16 wps_config_methods;
+	const u8 *wps_pri_dev_type;
+	const u8 *wps_sec_dev_type_list;
+	size_t wps_sec_dev_type_list_len;
+	const u8 *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+	size_t wps_vendor_ext_len[P2P_MAX_WPS_VENDOR_EXT];
+	const u8 *manufacturer;
+	size_t manufacturer_len;
+	const u8 *model_name;
+	size_t model_name_len;
+	const u8 *model_number;
+	size_t model_number_len;
+	const u8 *serial_number;
+	size_t serial_number_len;
+
+	/* DS Parameter Set IE */
+	const u8 *ds_params;
+
+	/* SSID IE */
+	const u8 *ssid;
+};
+
+
+#define P2P_MAX_GROUP_ENTRIES 50
+
+struct p2p_group_info {
+	unsigned int num_clients;
+	struct p2p_client_info {
+		const u8 *p2p_device_addr;
+		const u8 *p2p_interface_addr;
+		u8 dev_capab;
+		u16 config_methods;
+		const u8 *pri_dev_type;
+		u8 num_sec_dev_types;
+		const u8 *sec_dev_types;
+		const char *dev_name;
+		size_t dev_name_len;
+	} client[P2P_MAX_GROUP_ENTRIES];
+};
+
+
+/* p2p_utils.c */
+int p2p_random(char *buf, size_t len);
+int p2p_channel_to_freq(const char *country, int reg_class, int channel);
+int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class,
+			u8 *channel);
+void p2p_channels_intersect(const struct p2p_channels *a,
+			    const struct p2p_channels *b,
+			    struct p2p_channels *res);
+int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
+			  u8 channel);
+
+/* p2p_parse.c */
+int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg);
+int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg);
+int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg);
+void p2p_parse_free(struct p2p_message *msg);
+int p2p_attr_text(struct wpabuf *data, char *buf, char *end);
+int p2p_group_info_parse(const u8 *gi, size_t gi_len,
+			 struct p2p_group_info *info);
+
+/* p2p_build.c */
+
+struct p2p_noa_desc {
+	u8 count_type;
+	u32 duration;
+	u32 interval;
+	u32 start_time;
+};
+
+/* p2p_group.c */
+const u8 * p2p_group_get_interface_addr(struct p2p_group *group);
+u8 p2p_group_presence_req(struct p2p_group *group,
+			  const u8 *client_interface_addr,
+			  const u8 *noa, size_t noa_len);
+
+
+void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
+void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
+				   u8 dialog_token);
+u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf);
+void p2p_buf_add_status(struct wpabuf *buf, u8 status);
+void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
+			     struct p2p_device *peer);
+void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr);
+void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len);
+void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab);
+void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent);
+void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
+				u8 reg_class, u8 channel);
+void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
+				   u8 reg_class, u8 channel);
+void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
+			      struct p2p_channels *chan);
+void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
+				u8 client_timeout);
+void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr);
+void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid);
+void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
+			  const u8 *ssid, size_t ssid_len);
+void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags);
+void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
+		     struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2);
+void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
+				   u16 interval);
+void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
+		      int all_attr);
+
+/* p2p_sd.c */
+struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
+					 struct p2p_device *dev);
+void p2p_free_sd_queries(struct p2p_data *p2p);
+void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
+			    const u8 *data, size_t len, int rx_freq);
+void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq);
+void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq);
+void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa,
+			      const u8 *data, size_t len, int rx_freq);
+int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev);
+
+/* p2p_go_neg.c */
+int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
+			    struct p2p_device *dev,
+			    const u8 *channel_list, size_t channel_list_len);
+void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
+			    const u8 *data, size_t len, int rx_freq);
+void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq);
+void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len);
+int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev);
+
+/* p2p_pd.c */
+void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
+			       const u8 *data, size_t len, int rx_freq);
+void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
+				const u8 *data, size_t len);
+int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
+			   int join);
+
+/* p2p_invitation.c */
+void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
+				const u8 *data, size_t len, int rx_freq);
+void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
+				 const u8 *data, size_t len);
+int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
+		    const u8 *go_dev_addr);
+void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
+void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
+
+/* p2p_dev_disc.c */
+void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
+			      const u8 *data, size_t len, int rx_freq);
+void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success);
+int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev);
+void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success);
+void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa,
+			       const u8 *data, size_t len);
+void p2p_go_disc_req_cb(struct p2p_data *p2p, int success);
+void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq);
+
+/* p2p.c */
+void p2p_set_state(struct p2p_data *p2p, int new_state);
+void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec,
+		     unsigned int usec);
+void p2p_clear_timeout(struct p2p_data *p2p);
+void p2p_continue_find(struct p2p_data *p2p);
+struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
+						const u8 *addr,
+						struct p2p_message *msg);
+void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
+		      struct p2p_device *dev, struct p2p_message *msg);
+struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
+struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
+					     const u8 *addr);
+void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
+		       int status);
+void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer);
+int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps);
+int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
+			size_t num_req_dev_type);
+struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p);
+void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len);
+int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
+		    const u8 *src, const u8 *bssid, const u8 *buf,
+		    size_t len, unsigned int wait_time);
+
+#endif /* P2P_I_H */
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
new file mode 100644
index 0000000..bb2767d
--- /dev/null
+++ b/src/p2p/p2p_invitation.c
@@ -0,0 +1,489 @@
+/*
+ * Wi-Fi Direct - P2P Invitation procedure
+ * Copyright (c) 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
+						struct p2p_device *peer,
+						const u8 *go_dev_addr)
+{
+	struct wpabuf *buf;
+	u8 *len;
+	const u8 *dev_addr;
+
+	buf = wpabuf_alloc(1000);
+	if (buf == NULL)
+		return NULL;
+
+	peer->dialog_token++;
+	if (peer->dialog_token == 0)
+		peer->dialog_token = 1;
+	p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ,
+				      peer->dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent)
+		p2p_buf_add_config_timeout(buf, 0, 0);
+	else
+		p2p_buf_add_config_timeout(buf, 100, 20);
+	p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ?
+				     P2P_INVITATION_FLAGS_TYPE : 0);
+	p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+				      p2p->op_reg_class, p2p->op_channel);
+	if (p2p->inv_bssid_set)
+		p2p_buf_add_group_bssid(buf, p2p->inv_bssid);
+	p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
+	if (go_dev_addr)
+		dev_addr = go_dev_addr;
+	else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT)
+		dev_addr = peer->info.p2p_device_addr;
+	else
+		dev_addr = p2p->cfg->dev_addr;
+	p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len);
+	p2p_buf_add_device_info(buf, p2p, peer);
+	p2p_buf_update_ie_hdr(buf, len);
+
+	return buf;
+}
+
+
+static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p,
+						 struct p2p_device *peer,
+						 u8 dialog_token, u8 status,
+						 const u8 *group_bssid,
+						 u8 reg_class, u8 channel,
+						 struct p2p_channels *channels)
+{
+	struct wpabuf *buf;
+	u8 *len;
+
+	buf = wpabuf_alloc(1000);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP,
+				      dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	p2p_buf_add_status(buf, status);
+	p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */
+	if (reg_class && channel)
+		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+					      reg_class, channel);
+	if (group_bssid)
+		p2p_buf_add_group_bssid(buf, group_bssid);
+	if (channels)
+		p2p_buf_add_channel_list(buf, p2p->cfg->country, channels);
+	p2p_buf_update_ie_hdr(buf, len);
+
+	return buf;
+}
+
+
+void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
+				const u8 *data, size_t len, int rx_freq)
+{
+	struct p2p_device *dev;
+	struct p2p_message msg;
+	struct wpabuf *resp = NULL;
+	u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+	int freq;
+	int go = 0;
+	u8 group_bssid[ETH_ALEN], *bssid;
+	int op_freq = 0;
+	u8 reg_class = 0, channel = 0;
+	struct p2p_channels intersection, *channels = NULL;
+	int persistent;
+
+	os_memset(group_bssid, 0, sizeof(group_bssid));
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received Invitation Request from " MACSTR " (freq=%d)",
+		MAC2STR(sa), rx_freq);
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	dev = p2p_get_device(p2p, sa);
+	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Invitation Request from unknown peer "
+			MACSTR, MAC2STR(sa));
+
+		if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Invitation Request add device failed "
+				MACSTR, MAC2STR(sa));
+			status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+			goto fail;
+		}
+
+		dev = p2p_get_device(p2p, sa);
+		if (dev == NULL) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Reject Invitation Request from unknown "
+				"peer " MACSTR, MAC2STR(sa));
+			status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+			goto fail;
+		}
+	}
+
+	if (!msg.group_id || !msg.channel_list) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Mandatory attribute missing in Invitation "
+			"Request from " MACSTR, MAC2STR(sa));
+		status = P2P_SC_FAIL_INVALID_PARAMS;
+		goto fail;
+	}
+
+	if (msg.invitation_flags)
+		persistent = *msg.invitation_flags & P2P_INVITATION_FLAGS_TYPE;
+	else {
+		/* Invitation Flags is a mandatory attribute starting from P2P
+		 * spec 1.06. As a backwards compatibility mechanism, assume
+		 * the request was for a persistent group if the attribute is
+		 * missing.
+		 */
+		wpa_printf(MSG_DEBUG, "P2P: Mandatory Invitation Flags "
+			   "attribute missing from Invitation Request");
+		persistent = 1;
+	}
+
+	if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev,
+				    msg.channel_list, msg.channel_list_len) <
+	    0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No common channels found");
+		status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+		goto fail;
+	}
+
+	if (p2p->cfg->invitation_process) {
+		status = p2p->cfg->invitation_process(
+			p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id,
+			msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN,
+			&go, group_bssid, &op_freq, persistent);
+	}
+
+	if (op_freq) {
+		if (p2p_freq_to_channel(p2p->cfg->country, op_freq,
+					&reg_class, &channel) < 0) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Unknown forced freq %d MHz from "
+				"invitation_process()", op_freq);
+			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			goto fail;
+		}
+
+		p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+				       &intersection);
+		if (!p2p_channels_includes(&intersection, reg_class, channel))
+		{
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: forced freq %d MHz not in the supported "
+				"channels interaction", op_freq);
+			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			goto fail;
+		}
+
+		if (status == P2P_SC_SUCCESS)
+			channels = &intersection;
+	} else {
+		op_freq = p2p_channel_to_freq(p2p->cfg->country,
+					      p2p->cfg->op_reg_class,
+					      p2p->cfg->op_channel);
+		if (op_freq < 0) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Unknown operational channel "
+				"(country=%c%c reg_class=%u channel=%u)",
+				p2p->cfg->country[0], p2p->cfg->country[1],
+				p2p->cfg->op_reg_class, p2p->cfg->op_channel);
+			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			goto fail;
+		}
+
+		p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+				       &intersection);
+		if (status == P2P_SC_SUCCESS) {
+			reg_class = p2p->cfg->op_reg_class;
+			channel = p2p->cfg->op_channel;
+			channels = &intersection;
+		}
+	}
+
+fail:
+	if (go && status == P2P_SC_SUCCESS && !is_zero_ether_addr(group_bssid))
+		bssid = group_bssid;
+	else
+		bssid = NULL;
+	resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status,
+					 bssid, reg_class, channel, channels);
+
+	if (resp == NULL)
+		goto out;
+
+	if (rx_freq > 0)
+		freq = rx_freq;
+	else
+		freq = p2p_channel_to_freq(p2p->cfg->country,
+					   p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+	if (freq < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unknown regulatory class/channel");
+		goto out;
+	}
+
+	/*
+	 * Store copy of invitation data to be used when processing TX status
+	 * callback for the Acton frame.
+	 */
+	os_memcpy(p2p->inv_sa, sa, ETH_ALEN);
+	if (msg.group_bssid) {
+		os_memcpy(p2p->inv_group_bssid, msg.group_bssid, ETH_ALEN);
+		p2p->inv_group_bssid_ptr = p2p->inv_group_bssid;
+	} else
+		p2p->inv_group_bssid_ptr = NULL;
+	if (msg.group_id_len - ETH_ALEN <= 32) {
+		os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN,
+			  msg.group_id_len - ETH_ALEN);
+		p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN;
+	}
+	os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN);
+	p2p->inv_status = status;
+	p2p->inv_op_freq = op_freq;
+
+	p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE;
+	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+			    p2p->cfg->dev_addr,
+			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+	}
+
+out:
+	wpabuf_free(resp);
+	p2p_parse_free(&msg);
+}
+
+
+void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
+				 const u8 *data, size_t len)
+{
+	struct p2p_device *dev;
+	struct p2p_message msg;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received Invitation Response from " MACSTR,
+		MAC2STR(sa));
+
+	dev = p2p_get_device(p2p, sa);
+	if (dev == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Ignore Invitation Response from unknown peer "
+			MACSTR, MAC2STR(sa));
+		return;
+	}
+
+	if (dev != p2p->invite_peer) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Ignore unexpected Invitation Response from peer "
+			MACSTR, MAC2STR(sa));
+		return;
+	}
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	if (!msg.status) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Mandatory Status attribute missing in "
+			"Invitation Response from " MACSTR, MAC2STR(sa));
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (p2p->cfg->invitation_result)
+		p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
+					    msg.group_bssid);
+
+	p2p_parse_free(&msg);
+
+	p2p_clear_timeout(p2p);
+	p2p_set_state(p2p, P2P_IDLE);
+	p2p->invite_peer = NULL;
+}
+
+
+int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
+		    const u8 *go_dev_addr)
+{
+	struct wpabuf *req;
+	int freq;
+
+	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (freq <= 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Listen/Operating frequency known for the "
+			"peer " MACSTR " to send Invitation Request",
+			MAC2STR(dev->info.p2p_device_addr));
+		return -1;
+	}
+
+	req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
+	if (req == NULL)
+		return -1;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Sending Invitation Request");
+	p2p_set_state(p2p, P2P_INVITE);
+	p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST;
+	p2p->invite_peer = dev;
+	dev->invitation_reqs++;
+	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
+			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
+			    wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+		/* Use P2P find to recover and retry */
+		p2p_set_timeout(p2p, 0, 0);
+	}
+
+	wpabuf_free(req);
+
+	return 0;
+}
+
+
+void p2p_invitation_req_cb(struct p2p_data *p2p, int success)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Invitation Request TX callback: success=%d", success);
+
+	if (p2p->invite_peer == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No pending Invite");
+		return;
+	}
+
+	/*
+	 * Use P2P find, if needed, to find the other device from its listen
+	 * channel.
+	 */
+	p2p_set_state(p2p, P2P_INVITE);
+	p2p_set_timeout(p2p, 0, 100000);
+}
+
+
+void p2p_invitation_resp_cb(struct p2p_data *p2p, int success)
+{
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Invitation Response TX callback: success=%d", success);
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
+	if (success && p2p->cfg->invitation_received) {
+		p2p->cfg->invitation_received(p2p->cfg->cb_ctx,
+					      p2p->inv_sa,
+					      p2p->inv_group_bssid_ptr,
+					      p2p->inv_ssid, p2p->inv_ssid_len,
+					      p2p->inv_go_dev_addr,
+					      p2p->inv_status,
+					      p2p->inv_op_freq);
+	}
+}
+
+
+int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
+	       const u8 *bssid, const u8 *ssid, size_t ssid_len,
+	       unsigned int force_freq, const u8 *go_dev_addr,
+	       int persistent_group)
+{
+	struct p2p_device *dev;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Request to invite peer " MACSTR " role=%d persistent=%d "
+		"force_freq=%u",
+		MAC2STR(peer), role, persistent_group, force_freq);
+	if (bssid)
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Invitation for BSSID " MACSTR, MAC2STR(bssid));
+	if (go_dev_addr) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Invitation for GO Device Address " MACSTR,
+			MAC2STR(go_dev_addr));
+		os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN);
+		p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf;
+	} else
+		p2p->invite_go_dev_addr = NULL;
+	wpa_hexdump_ascii(MSG_DEBUG, "P2P: Invitation for SSID",
+			  ssid, ssid_len);
+
+	dev = p2p_get_device(p2p, peer);
+	if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Cannot invite unknown P2P Device " MACSTR,
+			MAC2STR(peer));
+		return -1;
+	}
+
+	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+		if (!(dev->info.dev_capab &
+		      P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Cannot invite a P2P Device " MACSTR
+				" that is in a group and is not discoverable",
+				MAC2STR(peer));
+		}
+		/* TODO: use device discoverability request through GO */
+	}
+
+	dev->invitation_reqs = 0;
+
+	if (force_freq) {
+		if (p2p_freq_to_channel(p2p->cfg->country, force_freq,
+					&p2p->op_reg_class, &p2p->op_channel) <
+		    0) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Unsupported frequency %u MHz",
+				force_freq);
+			return -1;
+		}
+		p2p->channels.reg_classes = 1;
+		p2p->channels.reg_class[0].channels = 1;
+		p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
+		p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
+	} else {
+		p2p->op_reg_class = p2p->cfg->op_reg_class;
+		p2p->op_channel = p2p->cfg->op_channel;
+		os_memcpy(&p2p->channels, &p2p->cfg->channels,
+			  sizeof(struct p2p_channels));
+	}
+
+	if (p2p->state != P2P_IDLE)
+		p2p_stop_find(p2p);
+
+	p2p->inv_role = role;
+	p2p->inv_bssid_set = bssid != NULL;
+	if (bssid)
+		os_memcpy(p2p->inv_bssid, bssid, ETH_ALEN);
+	os_memcpy(p2p->inv_ssid, ssid, ssid_len);
+	p2p->inv_ssid_len = ssid_len;
+	p2p->inv_persistent = persistent_group;
+	return p2p_invite_send(p2p, dev, go_dev_addr);
+}
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
new file mode 100644
index 0000000..5c5445a
--- /dev/null
+++ b/src/p2p/p2p_parse.c
@@ -0,0 +1,718 @@
+/*
+ * P2P - IE parser
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wps/wps_i.h"
+#include "p2p_i.h"
+
+
+static int p2p_parse_attribute(u8 id, const u8 *data, u16 len,
+			       struct p2p_message *msg)
+{
+	const u8 *pos;
+	size_t i, nlen;
+	char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+	switch (id) {
+	case P2P_ATTR_CAPABILITY:
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Capability "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->capability = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Device Capability %02x "
+			   "Group Capability %02x",
+			   data[0], data[1]);
+		break;
+	case P2P_ATTR_DEVICE_ID:
+		if (len < ETH_ALEN) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Device ID "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->device_id = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Device ID " MACSTR,
+			   MAC2STR(msg->device_id));
+		break;
+	case P2P_ATTR_GROUP_OWNER_INTENT:
+		if (len < 1) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short GO Intent "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->go_intent = data;
+		wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u "
+			   "Tie breaker %u", data[0] >> 1, data[0] & 0x01);
+		break;
+	case P2P_ATTR_STATUS:
+		if (len < 1) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Status "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->status = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Status: %d", data[0]);
+		break;
+	case P2P_ATTR_LISTEN_CHANNEL:
+		if (len == 0) {
+			wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Ignore "
+				   "null channel");
+			break;
+		}
+		if (len < 5) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Listen Channel "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->listen_channel = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: "
+			   "Country %c%c(0x%02x) Regulatory "
+			   "Class %d Channel Number %d", data[0], data[1],
+			   data[2], data[3], data[4]);
+		break;
+	case P2P_ATTR_OPERATING_CHANNEL:
+		if (len == 0) {
+			wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: "
+				   "Ignore null channel");
+			break;
+		}
+		if (len < 5) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Operating "
+				   "Channel attribute (length %d)", len);
+			return -1;
+		}
+		msg->operating_channel = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: "
+			   "Country %c%c(0x%02x) Regulatory "
+			   "Class %d Channel Number %d", data[0], data[1],
+			   data[2], data[3], data[4]);
+		break;
+	case P2P_ATTR_CHANNEL_LIST:
+		if (len < 3) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Channel List "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->channel_list = data;
+		msg->channel_list_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * Channel List: Country String "
+			   "'%c%c(0x%02x)'", data[0], data[1], data[2]);
+		wpa_hexdump(MSG_MSGDUMP, "P2P: Channel List",
+			    msg->channel_list, msg->channel_list_len);
+		break;
+	case P2P_ATTR_GROUP_INFO:
+		msg->group_info = data;
+		msg->group_info_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * Group Info");
+		break;
+	case P2P_ATTR_DEVICE_INFO:
+		if (len < ETH_ALEN + 2 + 8 + 1) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Device Info "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->p2p_device_info = data;
+		msg->p2p_device_info_len = len;
+		pos = data;
+		msg->p2p_device_addr = pos;
+		pos += ETH_ALEN;
+		msg->config_methods = WPA_GET_BE16(pos);
+		pos += 2;
+		msg->pri_dev_type = pos;
+		pos += 8;
+		msg->num_sec_dev_types = *pos++;
+		if (msg->num_sec_dev_types * 8 > data + len - pos) {
+			wpa_printf(MSG_DEBUG, "P2P: Device Info underflow");
+			return -1;
+		}
+		pos += msg->num_sec_dev_types * 8;
+		if (data + len - pos < 4) {
+			wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
+				   "length %d", (int) (data + len - pos));
+			return -1;
+		}
+		if (WPA_GET_BE16(pos) != ATTR_DEV_NAME) {
+			wpa_hexdump(MSG_DEBUG, "P2P: Unexpected Device Name "
+				    "header", pos, 4);
+			return -1;
+		}
+		pos += 2;
+		nlen = WPA_GET_BE16(pos);
+		pos += 2;
+		if (data + len - pos < (int) nlen || nlen > 32) {
+			wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
+				   "length %d (buf len %d)", (int) nlen,
+				   (int) (data + len - pos));
+			return -1;
+		}
+		os_memcpy(msg->device_name, pos, nlen);
+		msg->device_name[nlen] = '\0';
+		for (i = 0; i < nlen; i++) {
+			if (msg->device_name[i] == '\0')
+				break;
+			if (msg->device_name[i] > 0 &&
+			    msg->device_name[i] < 32)
+				msg->device_name[i] = '_';
+		}
+		wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR
+			   " primary device type %s device name '%s' "
+			   "config methods 0x%x",
+			   MAC2STR(msg->p2p_device_addr),
+			   wps_dev_type_bin2str(msg->pri_dev_type, devtype,
+						sizeof(devtype)),
+			   msg->device_name, msg->config_methods);
+		break;
+	case P2P_ATTR_CONFIGURATION_TIMEOUT:
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Configuration "
+				   "Timeout attribute (length %d)", len);
+			return -1;
+		}
+		msg->config_timeout = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout");
+		break;
+	case P2P_ATTR_INTENDED_INTERFACE_ADDR:
+		if (len < ETH_ALEN) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Intended P2P "
+				   "Interface Address attribute (length %d)",
+				   len);
+			return -1;
+		}
+		msg->intended_addr = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address: "
+			   MACSTR, MAC2STR(msg->intended_addr));
+		break;
+	case P2P_ATTR_GROUP_BSSID:
+		if (len < ETH_ALEN) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short P2P Group BSSID "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->group_bssid = data;
+		wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID: " MACSTR,
+			   MAC2STR(msg->group_bssid));
+		break;
+	case P2P_ATTR_GROUP_ID:
+		if (len < ETH_ALEN || len > ETH_ALEN + 32) {
+			wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID "
+				   "attribute length %d", len);
+			return -1;
+		}
+		msg->group_id = data;
+		msg->group_id_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID: Device Address "
+			   MACSTR, MAC2STR(msg->group_id));
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: * P2P Group ID: SSID",
+				  msg->group_id + ETH_ALEN,
+				  msg->group_id_len - ETH_ALEN);
+		break;
+	case P2P_ATTR_INVITATION_FLAGS:
+		if (len < 1) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Invitation "
+				   "Flag attribute (length %d)", len);
+			return -1;
+		}
+		msg->invitation_flags = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x",
+			   data[0]);
+		break;
+	case P2P_ATTR_MANAGEABILITY:
+		if (len < 1) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Manageability "
+				   "attribute (length %d)", len);
+			return -1;
+		}
+		msg->manageability = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Manageability: bitmap 0x%x",
+			   data[0]);
+		break;
+	case P2P_ATTR_NOTICE_OF_ABSENCE:
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Notice of "
+				   "Absence attribute (length %d)", len);
+			return -1;
+		}
+		msg->noa = data;
+		msg->noa_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
+		break;
+	case P2P_ATTR_EXT_LISTEN_TIMING:
+		if (len < 4) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Extended Listen "
+				   "Timing attribute (length %d)", len);
+			return -1;
+		}
+		msg->ext_listen_timing = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing "
+			   "(period %u msec  interval %u msec)",
+			   WPA_GET_LE16(msg->ext_listen_timing),
+			   WPA_GET_LE16(msg->ext_listen_timing + 2));
+		break;
+	case P2P_ATTR_MINOR_REASON_CODE:
+		if (len < 1) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short Minor Reason "
+				   "Code attribute (length %d)", len);
+			return -1;
+		}
+		msg->minor_reason_code = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u",
+			   *msg->minor_reason_code);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
+			   "(length %d)", id, len);
+		break;
+	}
+
+	return 0;
+}
+
+
+/**
+ * p2p_parse_p2p_ie - Parse P2P IE
+ * @buf: Concatenated P2P IE(s) payload
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: Caller is responsible for clearing the msg data structure before
+ * calling this function.
+ */
+int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg)
+{
+	const u8 *pos = wpabuf_head_u8(buf);
+	const u8 *end = pos + wpabuf_len(buf);
+
+	wpa_printf(MSG_DEBUG, "P2P: Parsing P2P IE");
+
+	while (pos < end) {
+		u16 attr_len;
+		if (pos + 2 >= end) {
+			wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute");
+			return -1;
+		}
+		attr_len = WPA_GET_LE16(pos + 1);
+		wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u",
+			   pos[0], attr_len);
+		if (pos + 3 + attr_len > end) {
+			wpa_printf(MSG_DEBUG, "P2P: Attribute underflow "
+				   "(len=%u left=%d)",
+				   attr_len, (int) (end - pos - 3));
+			wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos);
+			return -1;
+		}
+		if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg))
+			return -1;
+		pos += 3 + attr_len;
+	}
+
+	return 0;
+}
+
+
+static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg)
+{
+	struct wps_parse_attr attr;
+	int i;
+
+	wpa_printf(MSG_DEBUG, "P2P: Parsing WPS IE");
+	if (wps_parse_msg(buf, &attr))
+		return -1;
+	if (attr.dev_name && attr.dev_name_len < sizeof(msg->device_name) &&
+	    !msg->device_name[0])
+		os_memcpy(msg->device_name, attr.dev_name, attr.dev_name_len);
+	if (attr.config_methods) {
+		msg->wps_config_methods =
+			WPA_GET_BE16(attr.config_methods);
+		wpa_printf(MSG_DEBUG, "P2P: Config Methods (WPS): 0x%x",
+			   msg->wps_config_methods);
+	}
+	if (attr.dev_password_id) {
+		msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id);
+		wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d",
+			   msg->dev_password_id);
+	}
+	if (attr.primary_dev_type) {
+		char devtype[WPS_DEV_TYPE_BUFSIZE];
+		msg->wps_pri_dev_type = attr.primary_dev_type;
+		wpa_printf(MSG_DEBUG, "P2P: Primary Device Type (WPS): %s",
+			   wps_dev_type_bin2str(msg->wps_pri_dev_type, devtype,
+						sizeof(devtype)));
+	}
+	if (attr.sec_dev_type_list) {
+		msg->wps_sec_dev_type_list = attr.sec_dev_type_list;
+		msg->wps_sec_dev_type_list_len = attr.sec_dev_type_list_len;
+	}
+
+	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+		msg->wps_vendor_ext[i] = attr.vendor_ext[i];
+		msg->wps_vendor_ext_len[i] = attr.vendor_ext_len[i];
+	}
+
+	msg->manufacturer = attr.manufacturer;
+	msg->manufacturer_len = attr.manufacturer_len;
+	msg->model_name = attr.model_name;
+	msg->model_name_len = attr.model_name_len;
+	msg->model_number = attr.model_number;
+	msg->model_number_len = attr.model_number_len;
+	msg->serial_number = attr.serial_number;
+	msg->serial_number_len = attr.serial_number_len;
+
+	return 0;
+}
+
+
+/**
+ * p2p_parse_ies - Parse P2P message IEs (both WPS and P2P IE)
+ * @data: IEs from the message
+ * @len: Length of data buffer in octets
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: Caller is responsible for clearing the msg data structure before
+ * calling this function.
+ *
+ * Note: Caller must free temporary memory allocations by calling
+ * p2p_parse_free() when the parsed data is not needed anymore.
+ */
+int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg)
+{
+	struct ieee802_11_elems elems;
+
+	ieee802_11_parse_elems(data, len, &elems, 0);
+	if (elems.ds_params && elems.ds_params_len >= 1)
+		msg->ds_params = elems.ds_params;
+	if (elems.ssid)
+		msg->ssid = elems.ssid - 2;
+
+	msg->wps_attributes = ieee802_11_vendor_ie_concat(data, len,
+							  WPS_DEV_OUI_WFA);
+	if (msg->wps_attributes &&
+	    p2p_parse_wps_ie(msg->wps_attributes, msg)) {
+		p2p_parse_free(msg);
+		return -1;
+	}
+
+	msg->p2p_attributes = ieee802_11_vendor_ie_concat(data, len,
+							  P2P_IE_VENDOR_TYPE);
+	if (msg->p2p_attributes &&
+	    p2p_parse_p2p_ie(msg->p2p_attributes, msg)) {
+		wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data");
+		if (msg->p2p_attributes)
+			wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data",
+					msg->p2p_attributes);
+		p2p_parse_free(msg);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * p2p_parse - Parse a P2P Action frame contents
+ * @data: Action frame payload after Category and Code fields
+ * @len: Length of data buffer in octets
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: Caller must free temporary memory allocations by calling
+ * p2p_parse_free() when the parsed data is not needed anymore.
+ */
+int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg)
+{
+	os_memset(msg, 0, sizeof(*msg));
+	wpa_printf(MSG_DEBUG, "P2P: Parsing the received message");
+	if (len < 1) {
+		wpa_printf(MSG_DEBUG, "P2P: No Dialog Token in the message");
+		return -1;
+	}
+	msg->dialog_token = data[0];
+	wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", msg->dialog_token);
+
+	return p2p_parse_ies(data + 1, len - 1, msg);
+}
+
+
+/**
+ * p2p_parse_free - Free temporary data from P2P parsing
+ * @msg: Parsed attributes
+ */
+void p2p_parse_free(struct p2p_message *msg)
+{
+	wpabuf_free(msg->p2p_attributes);
+	msg->p2p_attributes = NULL;
+	wpabuf_free(msg->wps_attributes);
+	msg->wps_attributes = NULL;
+}
+
+
+int p2p_group_info_parse(const u8 *gi, size_t gi_len,
+			 struct p2p_group_info *info)
+{
+	const u8 *g, *gend;
+
+	os_memset(info, 0, sizeof(*info));
+	if (gi == NULL)
+		return 0;
+
+	g = gi;
+	gend = gi + gi_len;
+	while (g < gend) {
+		struct p2p_client_info *cli;
+		const u8 *t, *cend;
+		int count;
+
+		cli = &info->client[info->num_clients];
+		cend = g + 1 + g[0];
+		if (cend > gend)
+			return -1; /* invalid data */
+		/* g at start of P2P Client Info Descriptor */
+		/* t at Device Capability Bitmap */
+		t = g + 1 + 2 * ETH_ALEN;
+		if (t > cend)
+			return -1; /* invalid data */
+		cli->p2p_device_addr = g + 1;
+		cli->p2p_interface_addr = g + 1 + ETH_ALEN;
+		cli->dev_capab = t[0];
+
+		if (t + 1 + 2 + 8 + 1 > cend)
+			return -1; /* invalid data */
+
+		cli->config_methods = WPA_GET_BE16(&t[1]);
+		cli->pri_dev_type = &t[3];
+
+		t += 1 + 2 + 8;
+		/* t at Number of Secondary Device Types */
+		cli->num_sec_dev_types = *t++;
+		if (t + 8 * cli->num_sec_dev_types > cend)
+			return -1; /* invalid data */
+		cli->sec_dev_types = t;
+		t += 8 * cli->num_sec_dev_types;
+
+		/* t at Device Name in WPS TLV format */
+		if (t + 2 + 2 > cend)
+			return -1; /* invalid data */
+		if (WPA_GET_BE16(t) != ATTR_DEV_NAME)
+			return -1; /* invalid Device Name TLV */
+		t += 2;
+		count = WPA_GET_BE16(t);
+		t += 2;
+		if (count > cend - t)
+			return -1; /* invalid Device Name TLV */
+		if (count >= 32)
+			count = 32;
+		cli->dev_name = (const char *) t;
+		cli->dev_name_len = count;
+
+		g = cend;
+
+		info->num_clients++;
+		if (info->num_clients == P2P_MAX_GROUP_ENTRIES)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
+			       char *end)
+{
+	char *pos = buf;
+	int ret;
+	struct p2p_group_info info;
+	unsigned int i;
+
+	if (p2p_group_info_parse(gi, gi_len, &info) < 0)
+		return 0;
+
+	for (i = 0; i < info.num_clients; i++) {
+		struct p2p_client_info *cli;
+		char name[33];
+		char devtype[WPS_DEV_TYPE_BUFSIZE];
+		u8 s;
+		int count;
+
+		cli = &info.client[i];
+		ret = os_snprintf(pos, end - pos, "p2p_group_client: "
+				  "dev=" MACSTR " iface=" MACSTR,
+				  MAC2STR(cli->p2p_device_addr),
+				  MAC2STR(cli->p2p_interface_addr));
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+
+		ret = os_snprintf(pos, end - pos,
+				  " dev_capab=0x%x config_methods=0x%x "
+				  "dev_type=%s",
+				  cli->dev_capab, cli->config_methods,
+				  wps_dev_type_bin2str(cli->pri_dev_type,
+						       devtype,
+						       sizeof(devtype)));
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+
+		for (s = 0; s < cli->num_sec_dev_types; s++) {
+			ret = os_snprintf(pos, end - pos, " dev_type=%s",
+					  wps_dev_type_bin2str(
+						  &cli->sec_dev_types[s * 8],
+						  devtype, sizeof(devtype)));
+			if (ret < 0 || ret >= end - pos)
+				return pos - buf;
+			pos += ret;
+		}
+
+		os_memcpy(name, cli->dev_name, cli->dev_name_len);
+		name[cli->dev_name_len] = '\0';
+		count = (int) cli->dev_name_len - 1;
+		while (count >= 0) {
+			if (name[count] > 0 && name[count] < 32)
+				name[count] = '_';
+			count--;
+		}
+
+		ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name);
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
+/**
+ * p2p_attr_text - Build text format description of P2P IE attributes
+ * @data: P2P IE contents
+ * @buf: Buffer for returning text
+ * @end: Pointer to the end of the buf area
+ * Returns: Number of octets written to the buffer or -1 on faikure
+ *
+ * This function can be used to parse P2P IE contents into text format
+ * field=value lines.
+ */
+int p2p_attr_text(struct wpabuf *data, char *buf, char *end)
+{
+	struct p2p_message msg;
+	char *pos = buf;
+	int ret;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(data, &msg))
+		return -1;
+
+	if (msg.capability) {
+		ret = os_snprintf(pos, end - pos,
+				  "p2p_dev_capab=0x%x\n"
+				  "p2p_group_capab=0x%x\n",
+				  msg.capability[0], msg.capability[1]);
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (msg.pri_dev_type) {
+		char devtype[WPS_DEV_TYPE_BUFSIZE];
+		ret = os_snprintf(pos, end - pos,
+				  "p2p_primary_device_type=%s\n",
+				  wps_dev_type_bin2str(msg.pri_dev_type,
+						       devtype,
+						       sizeof(devtype)));
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+	}
+
+	ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n",
+			  msg.device_name);
+	if (ret < 0 || ret >= end - pos)
+		return pos - buf;
+	pos += ret;
+
+	if (msg.p2p_device_addr) {
+		ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR
+				  "\n",
+				  MAC2STR(msg.p2p_device_addr));
+		if (ret < 0 || ret >= end - pos)
+			return pos - buf;
+		pos += ret;
+	}
+
+	ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n",
+			  msg.config_methods);
+	if (ret < 0 || ret >= end - pos)
+		return pos - buf;
+	pos += ret;
+
+	ret = p2p_group_info_text(msg.group_info, msg.group_info_len,
+				  pos, end);
+	if (ret < 0)
+		return pos - buf;
+	pos += ret;
+
+	return pos - buf;
+}
+
+
+int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie)
+{
+	struct p2p_message msg;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p_ie, &msg))
+		return 0;
+
+	if (!msg.manageability)
+		return 0;
+
+	return !(msg.manageability[0] & P2P_MAN_CROSS_CONNECTION_PERMITTED);
+}
+
+
+u8 p2p_get_group_capab(const struct wpabuf *p2p_ie)
+{
+	struct p2p_message msg;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p_ie, &msg))
+		return 0;
+
+	if (!msg.capability)
+		return 0;
+
+	return msg.capability[1];
+}
+
+
+const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie)
+{
+	struct p2p_message msg;
+
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p_ie, &msg))
+		return NULL;
+
+	if (msg.p2p_device_addr)
+		return msg.p2p_device_addr;
+	if (msg.device_id)
+		return msg.device_id;
+
+	return NULL;
+}
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
new file mode 100644
index 0000000..e064216
--- /dev/null
+++ b/src/p2p/p2p_pd.c
@@ -0,0 +1,347 @@
+/*
+ * Wi-Fi Direct - P2P provision discovery
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
+					    u16 config_methods)
+{
+	u8 *len;
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	len = wpabuf_put(buf, 1);
+	wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
+
+	/* Config Methods */
+	wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
+	wpabuf_put_be16(buf, 2);
+	wpabuf_put_be16(buf, config_methods);
+
+	p2p_buf_update_ie_hdr(buf, len);
+}
+
+
+static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
+					       u8 dialog_token,
+					       u16 config_methods,
+					       struct p2p_device *go)
+{
+	struct wpabuf *buf;
+	u8 *len;
+
+	buf = wpabuf_alloc(1000);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
+
+	len = p2p_buf_add_ie_hdr(buf);
+	p2p_buf_add_capability(buf, p2p->dev_capab, 0);
+	p2p_buf_add_device_info(buf, p2p, NULL);
+	if (go) {
+		p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
+				     go->oper_ssid, go->oper_ssid_len);
+	}
+	p2p_buf_update_ie_hdr(buf, len);
+
+	/* WPS IE with Config Methods attribute */
+	p2p_build_wps_ie_config_methods(buf, config_methods);
+
+	return buf;
+}
+
+
+static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
+						u8 dialog_token,
+						u16 config_methods)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(100);
+	if (buf == NULL)
+		return NULL;
+
+	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
+
+	/* WPS IE with Config Methods attribute */
+	p2p_build_wps_ie_config_methods(buf, config_methods);
+
+	return buf;
+}
+
+
+void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
+			       const u8 *data, size_t len, int rx_freq)
+{
+	struct p2p_message msg;
+	struct p2p_device *dev;
+	int freq;
+	int reject = 1;
+	struct wpabuf *resp;
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received Provision Discovery Request from " MACSTR
+		" with config methods 0x%x (freq=%d)",
+		MAC2STR(sa), msg.wps_config_methods, rx_freq);
+
+	dev = p2p_get_device(p2p, sa);
+	if (dev == NULL || !(dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Provision Discovery Request from "
+			"unknown peer " MACSTR, MAC2STR(sa));
+		if (p2p_add_device(p2p, sa, rx_freq, 0, data + 1, len - 1)) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			        "P2P: Provision Discovery Request add device "
+				"failed " MACSTR, MAC2STR(sa));
+		}
+	}
+
+	if (!(msg.wps_config_methods &
+	      (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
+	       WPS_CONFIG_PUSHBUTTON))) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unsupported "
+			"Config Methods in Provision Discovery Request");
+		goto out;
+	}
+
+	if (dev)
+		dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+				P2P_DEV_PD_PEER_KEYPAD);
+	if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+			" requested us to show a PIN on display", MAC2STR(sa));
+		if (dev)
+			dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+			" requested us to write its PIN using keypad",
+			MAC2STR(sa));
+		if (dev)
+			dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+	}
+
+	reject = 0;
+
+out:
+	resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
+					reject ? 0 : msg.wps_config_methods);
+	if (resp == NULL) {
+		p2p_parse_free(&msg);
+		return;
+	}
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Sending Provision Discovery Response");
+	if (rx_freq > 0)
+		freq = rx_freq;
+	else
+		freq = p2p_channel_to_freq(p2p->cfg->country,
+					   p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+	if (freq < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unknown regulatory class/channel");
+		wpabuf_free(resp);
+		p2p_parse_free(&msg);
+		return;
+	}
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+			    p2p->cfg->dev_addr,
+			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+	}
+
+	wpabuf_free(resp);
+
+	if (!reject && p2p->cfg->prov_disc_req) {
+		const u8 *dev_addr = sa;
+		if (msg.p2p_device_addr)
+			dev_addr = msg.p2p_device_addr;
+		p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
+					msg.wps_config_methods,
+					dev_addr, msg.pri_dev_type,
+					msg.device_name, msg.config_methods,
+					msg.capability ? msg.capability[0] : 0,
+					msg.capability ? msg.capability[1] :
+					0);
+
+	}
+	p2p_parse_free(&msg);
+}
+
+
+void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
+				const u8 *data, size_t len)
+{
+	struct p2p_message msg;
+	struct p2p_device *dev;
+	u16 report_config_methods = 0;
+
+	if (p2p_parse(data, len, &msg))
+		return;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received Provisioning Discovery Response from " MACSTR
+		" with config methods 0x%x",
+		MAC2STR(sa), msg.wps_config_methods);
+
+	dev = p2p_get_device(p2p, sa);
+	if (dev == NULL || !dev->req_config_methods) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Ignore Provisioning Discovery Response from "
+			MACSTR " with no pending request", MAC2STR(sa));
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (dev->dialog_token != msg.dialog_token) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Ignore Provisioning Discovery Response with "
+			"unexpected Dialog Token %u (expected %u)",
+			msg.dialog_token, dev->dialog_token);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (msg.wps_config_methods != dev->req_config_methods) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected "
+			"our Provisioning Discovery Request");
+		p2p_parse_free(&msg);
+		goto out;
+	}
+
+	report_config_methods = dev->req_config_methods;
+	dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+			P2P_DEV_PD_PEER_KEYPAD);
+	if (dev->req_config_methods & WPS_CONFIG_DISPLAY) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+			" accepted to show a PIN on display", MAC2STR(sa));
+		dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR
+			" accepted to write our PIN using keypad",
+			MAC2STR(sa));
+		dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+	}
+	p2p_parse_free(&msg);
+
+out:
+	dev->req_config_methods = 0;
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	if (p2p->cfg->prov_disc_resp)
+		p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
+					 report_config_methods);
+}
+
+
+int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
+			   int join)
+{
+	struct wpabuf *req;
+	int freq;
+
+	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (freq <= 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Listen/Operating frequency known for the "
+			"peer " MACSTR " to send Provision Discovery Request",
+			MAC2STR(dev->info.p2p_device_addr));
+		return -1;
+	}
+
+	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+		if (!(dev->info.dev_capab &
+		      P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Cannot use PD with P2P Device " MACSTR
+				" that is in a group and is not discoverable",
+				MAC2STR(dev->info.p2p_device_addr));
+			return -1;
+		}
+		/* TODO: use device discoverability request through GO */
+	}
+
+	dev->dialog_token++;
+	if (dev->dialog_token == 0)
+		dev->dialog_token = 1;
+	req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
+				      dev->req_config_methods,
+				      join ? dev : NULL);
+	if (req == NULL)
+		return -1;
+
+	p2p->pending_action_state = P2P_PENDING_PD;
+	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
+			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
+			    wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+		wpabuf_free(req);
+		return -1;
+	}
+
+	wpabuf_free(req);
+	return 0;
+}
+
+
+int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+		      u16 config_methods, int join)
+{
+	struct p2p_device *dev;
+
+	dev = p2p_get_device(p2p, peer_addr);
+	if (dev == NULL)
+		dev = p2p_get_device_interface(p2p, peer_addr);
+	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision "
+			"Discovery Request destination " MACSTR
+			" not yet known", MAC2STR(peer_addr));
+		return -1;
+	}
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery "
+		"Request with " MACSTR " (config methods 0x%x)",
+		MAC2STR(peer_addr), config_methods);
+	if (config_methods == 0)
+		return -1;
+
+	dev->req_config_methods = config_methods;
+	if (join)
+		dev->flags |= P2P_DEV_PD_FOR_JOIN;
+	else
+		dev->flags &= ~P2P_DEV_PD_FOR_JOIN;
+
+	if (p2p->go_neg_peer ||
+	    (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
+	     p2p->state != P2P_LISTEN_ONLY)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Busy with other "
+			"operations; postpone Provision Discovery Request "
+			"with " MACSTR " (config methods 0x%x)",
+			MAC2STR(peer_addr), config_methods);
+		return 0;
+	}
+
+	return p2p_send_prov_disc_req(p2p, dev, join);
+}
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
new file mode 100644
index 0000000..926fc03
--- /dev/null
+++ b/src/p2p/p2p_sd.c
@@ -0,0 +1,951 @@
+/*
+ * Wi-Fi Direct - P2P service discovery
+ * Copyright (c) 2009, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p_i.h"
+#include "p2p.h"
+
+
+struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
+					 struct p2p_device *dev)
+{
+	struct p2p_sd_query *q;
+
+	if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY))
+		return 0; /* peer does not support SD */
+
+	for (q = p2p->sd_queries; q; q = q->next) {
+		if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO))
+			return q;
+		if (!q->for_all_peers &&
+		    os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) ==
+		    0)
+			return q;
+	}
+
+	return NULL;
+}
+
+
+static int p2p_unlink_sd_query(struct p2p_data *p2p,
+			       struct p2p_sd_query *query)
+{
+	struct p2p_sd_query *q, *prev;
+	q = p2p->sd_queries;
+	prev = NULL;
+	while (q) {
+		if (q == query) {
+			if (prev)
+				prev->next = q->next;
+			else
+				p2p->sd_queries = q->next;
+			if (p2p->sd_query == query)
+				p2p->sd_query = NULL;
+			return 1;
+		}
+		prev = q;
+		q = q->next;
+	}
+	return 0;
+}
+
+
+static void p2p_free_sd_query(struct p2p_sd_query *q)
+{
+	if (q == NULL)
+		return;
+	wpabuf_free(q->tlvs);
+	os_free(q);
+}
+
+
+void p2p_free_sd_queries(struct p2p_data *p2p)
+{
+	struct p2p_sd_query *q, *prev;
+	q = p2p->sd_queries;
+	p2p->sd_queries = NULL;
+	while (q) {
+		prev = q;
+		q = q->next;
+		p2p_free_sd_query(prev);
+	}
+}
+
+
+static struct wpabuf * p2p_build_sd_query(u16 update_indic,
+					  struct wpabuf *tlvs)
+{
+	struct wpabuf *buf;
+	u8 *len_pos, *len_pos2;
+
+	buf = wpabuf_alloc(1000 + wpabuf_len(tlvs));
+	if (buf == NULL)
+		return NULL;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_REQ);
+	wpabuf_put_u8(buf, 0); /* Dialog Token */
+
+	/* Advertisement Protocol IE */
+	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(buf, 2); /* Length */
+	wpabuf_put_u8(buf, 0); /* QueryRespLenLimit | PAME-BI */
+	wpabuf_put_u8(buf, NATIVE_QUERY_PROTOCOL); /* Advertisement Protocol */
+
+	/* Query Request */
+	len_pos = wpabuf_put(buf, 2); /* Length (to be filled) */
+
+	/* NQP Query Request Frame */
+	wpabuf_put_le16(buf, NQP_VENDOR_SPECIFIC); /* Info ID */
+	len_pos2 = wpabuf_put(buf, 2); /* Length (to be filled) */
+	wpabuf_put_be24(buf, OUI_WFA);
+	wpabuf_put_u8(buf, P2P_OUI_TYPE);
+	wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */
+	wpabuf_put_buf(buf, tlvs);
+
+	WPA_PUT_LE16(len_pos2, (u8 *) wpabuf_put(buf, 0) - len_pos2 - 2);
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
+
+	return buf;
+}
+
+
+static struct wpabuf * p2p_build_gas_comeback_req(u8 dialog_token)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(3);
+	if (buf == NULL)
+		return NULL;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(buf, WLAN_PA_GAS_COMEBACK_REQ);
+	wpabuf_put_u8(buf, dialog_token);
+
+	return buf;
+}
+
+
+static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst,
+				      u8 dialog_token, int freq)
+{
+	struct wpabuf *req;
+
+	req = p2p_build_gas_comeback_req(dialog_token);
+	if (req == NULL)
+		return;
+
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, dst,
+			    wpabuf_head(req), wpabuf_len(req), 200) < 0)
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+
+	wpabuf_free(req);
+}
+
+
+static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code,
+					     u16 comeback_delay,
+					     u16 update_indic,
+					     const struct wpabuf *tlvs)
+{
+	struct wpabuf *buf;
+	u8 *len_pos, *len_pos2;
+
+	buf = wpabuf_alloc(1000 + (tlvs ? wpabuf_len(tlvs) : 0));
+	if (buf == NULL)
+		return NULL;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP);
+	wpabuf_put_u8(buf, dialog_token);
+	wpabuf_put_le16(buf, status_code);
+	wpabuf_put_le16(buf, comeback_delay);
+
+	/* Advertisement Protocol IE */
+	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(buf, 2); /* Length */
+	wpabuf_put_u8(buf, 0x7f); /* QueryRespLenLimit | PAME-BI */
+	wpabuf_put_u8(buf, NATIVE_QUERY_PROTOCOL); /* Advertisement Protocol */
+
+	/* Query Response */
+	len_pos = wpabuf_put(buf, 2); /* Length (to be filled) */
+
+	if (tlvs) {
+		/* NQP Query Response Frame */
+		wpabuf_put_le16(buf, NQP_VENDOR_SPECIFIC); /* Info ID */
+		len_pos2 = wpabuf_put(buf, 2); /* Length (to be filled) */
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, P2P_OUI_TYPE);
+		 /* Service Update Indicator */
+		wpabuf_put_le16(buf, update_indic);
+		wpabuf_put_buf(buf, tlvs);
+
+		WPA_PUT_LE16(len_pos2,
+			     (u8 *) wpabuf_put(buf, 0) - len_pos2 - 2);
+	}
+
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
+
+	return buf;
+}
+
+
+static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token,
+						   u16 status_code,
+						   u16 update_indic,
+						   const u8 *data, size_t len,
+						   u8 frag_id, u8 more,
+						   u16 total_len)
+{
+	struct wpabuf *buf;
+	u8 *len_pos;
+
+	buf = wpabuf_alloc(1000 + len);
+	if (buf == NULL)
+		return NULL;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(buf, WLAN_PA_GAS_COMEBACK_RESP);
+	wpabuf_put_u8(buf, dialog_token);
+	wpabuf_put_le16(buf, status_code);
+	wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0));
+	wpabuf_put_le16(buf, 0); /* Comeback Delay */
+
+	/* Advertisement Protocol IE */
+	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+	wpabuf_put_u8(buf, 2); /* Length */
+	wpabuf_put_u8(buf, 0x7f); /* QueryRespLenLimit | PAME-BI */
+	wpabuf_put_u8(buf, NATIVE_QUERY_PROTOCOL); /* Advertisement Protocol */
+
+	/* Query Response */
+	len_pos = wpabuf_put(buf, 2); /* Length (to be filled) */
+
+	if (frag_id == 0) {
+		/* NQP Query Response Frame */
+		wpabuf_put_le16(buf, NQP_VENDOR_SPECIFIC); /* Info ID */
+		wpabuf_put_le16(buf, 3 + 1 + 2 + total_len);
+		wpabuf_put_be24(buf, OUI_WFA);
+		wpabuf_put_u8(buf, P2P_OUI_TYPE);
+		/* Service Update Indicator */
+		wpabuf_put_le16(buf, update_indic);
+	}
+
+	wpabuf_put_data(buf, data, len);
+
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
+
+	return buf;
+}
+
+
+int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev)
+{
+	struct wpabuf *req;
+	int ret = 0;
+	struct p2p_sd_query *query;
+	int freq;
+
+	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (freq <= 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: No Listen/Operating frequency known for the "
+			"peer " MACSTR " to send SD Request",
+			MAC2STR(dev->info.p2p_device_addr));
+		return -1;
+	}
+
+	query = p2p_pending_sd_req(p2p, dev);
+	if (query == NULL)
+		return -1;
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Start Service Discovery with " MACSTR,
+		MAC2STR(dev->info.p2p_device_addr));
+
+	req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs);
+	if (req == NULL)
+		return -1;
+
+	p2p->sd_peer = dev;
+	p2p->sd_query = query;
+	p2p->pending_action_state = P2P_PENDING_SD;
+
+	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
+			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
+			    wpabuf_head(req), wpabuf_len(req), 5000) < 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+		ret = -1;
+	}
+
+	wpabuf_free(req);
+
+	return ret;
+}
+
+
+void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
+			    const u8 *data, size_t len, int rx_freq)
+{
+	const u8 *pos = data;
+	const u8 *end = data + len;
+	const u8 *next;
+	u8 dialog_token;
+	u16 slen;
+	int freq;
+	u16 update_indic;
+
+
+	if (p2p->cfg->sd_request == NULL)
+		return;
+
+	if (rx_freq > 0)
+		freq = rx_freq;
+	else
+		freq = p2p_channel_to_freq(p2p->cfg->country,
+					   p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+	if (freq < 0)
+		return;
+
+	if (len < 1 + 2)
+		return;
+
+	dialog_token = *pos++;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: GAS Initial Request from " MACSTR " (dialog token %u, "
+		"freq %d)",
+		MAC2STR(sa), dialog_token, rx_freq);
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unexpected IE in GAS Initial Request: %u", *pos);
+		return;
+	}
+	pos++;
+
+	slen = *pos++;
+	next = pos + slen;
+	if (next > end || slen < 2) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Invalid IE in GAS Initial Request");
+		return;
+	}
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+	if (*pos != NATIVE_QUERY_PROTOCOL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported GAS advertisement protocol id %u",
+			*pos);
+		return;
+	}
+
+	pos = next;
+	/* Query Request */
+	if (pos + 2 > end)
+		return;
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (pos + slen > end)
+		return;
+	end = pos + slen;
+
+	/* NQP Query Request */
+	if (pos + 4 > end)
+		return;
+	if (WPA_GET_LE16(pos) != NQP_VENDOR_SPECIFIC) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported NQP Info ID %u", WPA_GET_LE16(pos));
+		return;
+	}
+	pos += 2;
+
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (pos + slen > end || slen < 3 + 1) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Invalid NQP Query Request length");
+		return;
+	}
+
+	if (WPA_GET_BE24(pos) != OUI_WFA) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported NQP OUI %06x", WPA_GET_BE24(pos));
+		return;
+	}
+	pos += 3;
+
+	if (*pos != P2P_OUI_TYPE) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported NQP vendor type %u", *pos);
+		return;
+	}
+	pos++;
+
+	if (pos + 2 > end)
+		return;
+	update_indic = WPA_GET_LE16(pos);
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Service Update Indicator: %u", update_indic);
+	pos += 2;
+
+	p2p->cfg->sd_request(p2p->cfg->cb_ctx, freq, sa, dialog_token,
+			     update_indic, pos, end - pos);
+	/* the response will be indicated with a call to p2p_sd_response() */
+}
+
+
+void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
+		     u8 dialog_token, const struct wpabuf *resp_tlvs)
+{
+	struct wpabuf *resp;
+
+	/* TODO: fix the length limit to match with the maximum frame length */
+	if (wpabuf_len(resp_tlvs) > 1400) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response long "
+			"enough to require fragmentation");
+		if (p2p->sd_resp) {
+			/*
+			 * TODO: Could consider storing the fragmented response
+			 * separately for each peer to avoid having to drop old
+			 * one if there is more than one pending SD query.
+			 * Though, that would eat more memory, so there are
+			 * also benefits to just using a single buffer.
+			 */
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop "
+				"previous SD response");
+			wpabuf_free(p2p->sd_resp);
+		}
+		os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN);
+		p2p->sd_resp_dialog_token = dialog_token;
+		p2p->sd_resp = wpabuf_dup(resp_tlvs);
+		p2p->sd_resp_pos = 0;
+		p2p->sd_frag_id = 0;
+		resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS,
+					     1, p2p->srv_update_indic, NULL);
+	} else {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response fits "
+			"in initial response");
+		resp = p2p_build_sd_response(dialog_token,
+					     WLAN_STATUS_SUCCESS, 0,
+					     p2p->srv_update_indic, resp_tlvs);
+	}
+	if (resp == NULL)
+		return;
+
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr,
+			    p2p->cfg->dev_addr,
+			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+
+	wpabuf_free(resp);
+}
+
+
+void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq)
+{
+	const u8 *pos = data;
+	const u8 *end = data + len;
+	const u8 *next;
+	u8 dialog_token;
+	u16 status_code;
+	u16 comeback_delay;
+	u16 slen;
+	u16 update_indic;
+
+	if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+	    os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Ignore unexpected GAS Initial Response from "
+			MACSTR, MAC2STR(sa));
+		return;
+	}
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	p2p_clear_timeout(p2p);
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received GAS Initial Response from " MACSTR " (len=%d)",
+		MAC2STR(sa), (int) len);
+
+	if (len < 5 + 2) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Too short GAS Initial Response frame");
+		return;
+	}
+
+	dialog_token = *pos++;
+	/* TODO: check dialog_token match */
+	status_code = WPA_GET_LE16(pos);
+	pos += 2;
+	comeback_delay = WPA_GET_LE16(pos);
+	pos += 2;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: dialog_token=%u status_code=%u comeback_delay=%u",
+		dialog_token, status_code, comeback_delay);
+	if (status_code) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Service Discovery failed: status code %u",
+			status_code);
+		return;
+	}
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unexpected IE in GAS Initial Response: %u",
+			*pos);
+		return;
+	}
+	pos++;
+
+	slen = *pos++;
+	next = pos + slen;
+	if (next > end || slen < 2) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Invalid IE in GAS Initial Response");
+		return;
+	}
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+	if (*pos != NATIVE_QUERY_PROTOCOL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported GAS advertisement protocol id %u",
+			*pos);
+		return;
+	}
+
+	pos = next;
+	/* Query Response */
+	if (pos + 2 > end) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query "
+			"Response");
+		return;
+	}
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d",
+		slen);
+	if (pos + slen > end) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query "
+			"Response data");
+		return;
+	}
+	end = pos + slen;
+
+	if (comeback_delay) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Fragmented "
+			"response - request fragments");
+		if (p2p->sd_rx_resp) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop "
+				"old SD reassembly buffer");
+			wpabuf_free(p2p->sd_rx_resp);
+			p2p->sd_rx_resp = NULL;
+		}
+		p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq);
+		return;
+	}
+
+	/* NQP Query Response */
+	if (pos + 4 > end)
+		return;
+	if (WPA_GET_LE16(pos) != NQP_VENDOR_SPECIFIC) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported NQP Info ID %u", WPA_GET_LE16(pos));
+		return;
+	}
+	pos += 2;
+
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	if (pos + slen > end || slen < 3 + 1) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Invalid NQP Query Response length");
+		return;
+	}
+
+	if (WPA_GET_BE24(pos) != OUI_WFA) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported NQP OUI %06x", WPA_GET_BE24(pos));
+		return;
+	}
+	pos += 3;
+
+	if (*pos != P2P_OUI_TYPE) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported NQP vendor type %u", *pos);
+		return;
+	}
+	pos++;
+
+	if (pos + 2 > end)
+		return;
+	update_indic = WPA_GET_LE16(pos);
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Service Update Indicator: %u", update_indic);
+	pos += 2;
+
+	p2p->sd_peer->flags |= P2P_DEV_SD_INFO;
+	p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+	p2p->sd_peer = NULL;
+
+	if (p2p->sd_query) {
+		if (!p2p->sd_query->for_all_peers) {
+			struct p2p_sd_query *q;
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Remove completed SD query %p",
+				p2p->sd_query);
+			q = p2p->sd_query;
+			p2p_unlink_sd_query(p2p, p2p->sd_query);
+			p2p_free_sd_query(q);
+		}
+		p2p->sd_query = NULL;
+	}
+
+	if (p2p->cfg->sd_response)
+		p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, update_indic,
+				      pos, end - pos);
+	p2p_continue_find(p2p);
+}
+
+
+void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
+			     const u8 *data, size_t len, int rx_freq)
+{
+	struct wpabuf *resp;
+	u8 dialog_token;
+	size_t frag_len;
+	int more = 0;
+
+	wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len);
+	if (len < 1)
+		return;
+	dialog_token = *data;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dialog Token: %u",
+		dialog_token);
+	if (dialog_token != p2p->sd_resp_dialog_token) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+			"response fragment for dialog token %u", dialog_token);
+		return;
+	}
+
+	if (p2p->sd_resp == NULL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+			"response fragment available");
+		return;
+	}
+	if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD "
+			"response fragment for " MACSTR, MAC2STR(sa));
+		return;
+	}
+
+	frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos;
+	if (frag_len > 1400) {
+		frag_len = 1400;
+		more = 1;
+	}
+	resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS,
+					   p2p->srv_update_indic,
+					   wpabuf_head_u8(p2p->sd_resp) +
+					   p2p->sd_resp_pos, frag_len,
+					   p2p->sd_frag_id, more,
+					   wpabuf_len(p2p->sd_resp));
+	if (resp == NULL)
+		return;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send GAS Comeback "
+		"Response (frag_id %d more=%d frag_len=%d)",
+		p2p->sd_frag_id, more, (int) frag_len);
+	p2p->sd_frag_id++;
+	p2p->sd_resp_pos += frag_len;
+
+	if (more) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: %d more bytes "
+			"remain to be sent",
+			(int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos));
+	} else {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: All fragments of "
+			"SD response sent");
+		wpabuf_free(p2p->sd_resp);
+		p2p->sd_resp = NULL;
+	}
+
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr,
+			    p2p->cfg->dev_addr,
+			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Failed to send Action frame");
+
+	wpabuf_free(resp);
+}
+
+
+void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa,
+			      const u8 *data, size_t len, int rx_freq)
+{
+	const u8 *pos = data;
+	const u8 *end = data + len;
+	const u8 *next;
+	u8 dialog_token;
+	u16 status_code;
+	u8 frag_id;
+	u8 more_frags;
+	u16 comeback_delay;
+	u16 slen;
+
+	wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len);
+
+	if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL ||
+	    os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Ignore unexpected GAS Comeback Response from "
+			MACSTR, MAC2STR(sa));
+		return;
+	}
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	p2p_clear_timeout(p2p);
+
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Received GAS Comeback Response from " MACSTR " (len=%d)",
+		MAC2STR(sa), (int) len);
+
+	if (len < 6 + 2) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Too short GAS Comeback Response frame");
+		return;
+	}
+
+	dialog_token = *pos++;
+	/* TODO: check dialog_token match */
+	status_code = WPA_GET_LE16(pos);
+	pos += 2;
+	frag_id = *pos & 0x7f;
+	more_frags = (*pos & 0x80) >> 7;
+	pos++;
+	comeback_delay = WPA_GET_LE16(pos);
+	pos += 2;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: dialog_token=%u status_code=%u frag_id=%d more_frags=%d "
+		"comeback_delay=%u",
+		dialog_token, status_code, frag_id, more_frags,
+		comeback_delay);
+	/* TODO: check frag_id match */
+	if (status_code) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Service Discovery failed: status code %u",
+			status_code);
+		return;
+	}
+
+	if (*pos != WLAN_EID_ADV_PROTO) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unexpected IE in GAS Comeback Response: %u",
+			*pos);
+		return;
+	}
+	pos++;
+
+	slen = *pos++;
+	next = pos + slen;
+	if (next > end || slen < 2) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Invalid IE in GAS Comeback Response");
+		return;
+	}
+	pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+	if (*pos != NATIVE_QUERY_PROTOCOL) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported GAS advertisement protocol id %u",
+			*pos);
+		return;
+	}
+
+	pos = next;
+	/* Query Response */
+	if (pos + 2 > end) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query "
+			"Response");
+		return;
+	}
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d",
+		slen);
+	if (pos + slen > end) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query "
+			"Response data");
+		return;
+	}
+	if (slen == 0) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No Query Response "
+			"data");
+		return;
+	}
+	end = pos + slen;
+
+	if (p2p->sd_rx_resp) {
+		 /*
+		  * NQP header is only included in the first fragment; rest of
+		  * the fragments start with continue TLVs.
+		  */
+		goto skip_nqp_header;
+	}
+
+	/* NQP Query Response */
+	if (pos + 4 > end)
+		return;
+	if (WPA_GET_LE16(pos) != NQP_VENDOR_SPECIFIC) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported NQP Info ID %u", WPA_GET_LE16(pos));
+		return;
+	}
+	pos += 2;
+
+	slen = WPA_GET_LE16(pos);
+	pos += 2;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: NQP Query Response "
+		"length: %u", slen);
+	if (slen < 3 + 1) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Invalid NQP Query Response length");
+		return;
+	}
+	if (pos + 4 > end)
+		return;
+
+	if (WPA_GET_BE24(pos) != OUI_WFA) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported NQP OUI %06x", WPA_GET_BE24(pos));
+		return;
+	}
+	pos += 3;
+
+	if (*pos != P2P_OUI_TYPE) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Unsupported NQP vendor type %u", *pos);
+		return;
+	}
+	pos++;
+
+	if (pos + 2 > end)
+		return;
+	p2p->sd_rx_update_indic = WPA_GET_LE16(pos);
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+		"P2P: Service Update Indicator: %u", p2p->sd_rx_update_indic);
+	pos += 2;
+
+skip_nqp_header:
+	if (wpabuf_resize(&p2p->sd_rx_resp, end - pos) < 0)
+		return;
+	wpabuf_put_data(p2p->sd_rx_resp, pos, end - pos);
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Current SD reassembly "
+		"buffer length: %u",
+		(unsigned int) wpabuf_len(p2p->sd_rx_resp));
+
+	if (more_frags) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: More fragments "
+			"remains");
+		/* TODO: what would be a good size limit? */
+		if (wpabuf_len(p2p->sd_rx_resp) > 64000) {
+			wpabuf_free(p2p->sd_rx_resp);
+			p2p->sd_rx_resp = NULL;
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too long "
+				"SD response - drop it");
+			return;
+		}
+		p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq);
+		return;
+	}
+
+	p2p->sd_peer->flags |= P2P_DEV_SD_INFO;
+	p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
+	p2p->sd_peer = NULL;
+
+	if (p2p->sd_query) {
+		if (!p2p->sd_query->for_all_peers) {
+			struct p2p_sd_query *q;
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Remove completed SD query %p",
+				p2p->sd_query);
+			q = p2p->sd_query;
+			p2p_unlink_sd_query(p2p, p2p->sd_query);
+			p2p_free_sd_query(q);
+		}
+		p2p->sd_query = NULL;
+	}
+
+	if (p2p->cfg->sd_response)
+		p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa,
+				      p2p->sd_rx_update_indic,
+				      wpabuf_head(p2p->sd_rx_resp),
+				      wpabuf_len(p2p->sd_rx_resp));
+	wpabuf_free(p2p->sd_rx_resp);
+	p2p->sd_rx_resp = NULL;
+
+	p2p_continue_find(p2p);
+}
+
+
+void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
+		      const struct wpabuf *tlvs)
+{
+	struct p2p_sd_query *q;
+
+	q = os_zalloc(sizeof(*q));
+	if (q == NULL)
+		return NULL;
+
+	if (dst)
+		os_memcpy(q->peer, dst, ETH_ALEN);
+	else
+		q->for_all_peers = 1;
+
+	q->tlvs = wpabuf_dup(tlvs);
+	if (q->tlvs == NULL) {
+		p2p_free_sd_query(q);
+		return NULL;
+	}
+
+	q->next = p2p->sd_queries;
+	p2p->sd_queries = q;
+	wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q);
+
+	return q;
+}
+
+
+void p2p_sd_service_update(struct p2p_data *p2p)
+{
+	p2p->srv_update_indic++;
+}
+
+
+int p2p_sd_cancel_request(struct p2p_data *p2p, void *req)
+{
+	if (p2p_unlink_sd_query(p2p, req)) {
+		wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+			"P2P: Cancel pending SD query %p", req);
+		p2p_free_sd_query(req);
+		return 0;
+	}
+	return -1;
+}
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
new file mode 100644
index 0000000..da4b6ed
--- /dev/null
+++ b/src/p2p/p2p_utils.c
@@ -0,0 +1,271 @@
+/*
+ * P2P - generic helper functions
+ * Copyright (c) 2009, 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "p2p_i.h"
+
+
+/**
+ * p2p_random - Generate random string for SSID and passphrase
+ * @buf: Buffer for returning the result
+ * @len: Number of octets to write to the buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function generates a random string using the following character set:
+ * 'A'-'Z', 'a'-'z', '0'-'9'.
+ */
+int p2p_random(char *buf, size_t len)
+{
+	u8 val;
+	size_t i;
+	u8 letters = 'Z' - 'A' + 1;
+	u8 numbers = 10;
+
+	if (os_get_random((unsigned char *) buf, len))
+		return -1;
+	/* Character set: 'A'-'Z', 'a'-'z', '0'-'9' */
+	for (i = 0; i < len; i++) {
+		val = buf[i];
+		val %= 2 * letters + numbers;
+		if (val < letters)
+			buf[i] = 'A' + val;
+		else if (val < 2 * letters)
+			buf[i] = 'a' + (val - letters);
+		else
+			buf[i] = '0' + (val - 2 * letters);
+	}
+
+	return 0;
+}
+
+
+static int p2p_channel_to_freq_j4(int reg_class, int channel)
+{
+	/* Table J-4 in P802.11REVmb/D4.0 - Global operating classes */
+	/* TODO: more regulatory classes */
+	switch (reg_class) {
+	case 81:
+		/* channels 1..13 */
+		if (channel < 1 || channel > 13)
+			return -1;
+		return 2407 + 5 * channel;
+	case 82:
+		/* channel 14 */
+		if (channel != 14)
+			return -1;
+		return 2414 + 5 * channel;
+	case 83: /* channels 1..9; 40 MHz */
+	case 84: /* channels 5..13; 40 MHz */
+		if (channel < 1 || channel > 13)
+			return -1;
+		return 2407 + 5 * channel;
+	case 115: /* channels 36,40,44,48; indoor only */
+	case 118: /* channels 52,56,60,64; dfs */
+		if (channel < 36 || channel > 64)
+			return -1;
+		return 5000 + 5 * channel;
+	case 124: /* channels 149,153,157,161 */
+	case 125: /* channels 149,153,157,161,165,169 */
+		if (channel < 149 || channel > 161)
+			return -1;
+		return 5000 + 5 * channel;
+	case 116: /* channels 36,44; 40 MHz; indoor only */
+	case 117: /* channels 40,48; 40 MHz; indoor only */
+	case 119: /* channels 52,60; 40 MHz; dfs */
+	case 120: /* channels 56,64; 40 MHz; dfs */
+		if (channel < 36 || channel > 64)
+			return -1;
+		return 5000 + 5 * channel;
+	case 126: /* channels 149,157; 40 MHz */
+	case 127: /* channels 153,161; 40 MHz */
+		if (channel < 149 || channel > 161)
+			return -1;
+		return 5000 + 5 * channel;
+	}
+	return -1;
+}
+
+
+/**
+ * p2p_channel_to_freq - Convert channel info to frequency
+ * @country: Country code
+ * @reg_class: Regulatory class
+ * @channel: Channel number
+ * Returns: Frequency in MHz or -1 if the specified channel is unknown
+ */
+int p2p_channel_to_freq(const char *country, int reg_class, int channel)
+{
+	if (country[2] == 0x04)
+		return p2p_channel_to_freq_j4(reg_class, channel);
+
+	/* These are mainly for backwards compatibility; to be removed */
+	switch (reg_class) {
+	case 1: /* US/1, EU/1, JP/1 = 5 GHz, channels 36,40,44,48 */
+		if (channel < 36 || channel > 48)
+			return -1;
+		return 5000 + 5 * channel;
+	case 3: /* US/3 = 5 GHz, channels 149,153,157,161 */
+	case 5: /* US/5 = 5 GHz, channels 149,153,157,161 */
+		if (channel < 149 || channel > 161)
+			return -1;
+		return 5000 + 5 * channel;
+	case 4: /* EU/4 = 2.407 GHz, channels 1..13 */
+	case 12: /* US/12 = 2.407 GHz, channels 1..11 */
+	case 30: /* JP/30 = 2.407 GHz, channels 1..13 */
+		if (channel < 1 || channel > 13)
+			return -1;
+		return 2407 + 5 * channel;
+	case 31: /* JP/31 = 2.414 GHz, channel 14 */
+		if (channel != 14)
+			return -1;
+		return 2414 + 5 * channel;
+	}
+
+	return -1;
+}
+
+
+/**
+ * p2p_freq_to_channel - Convert frequency into channel info
+ * @country: Country code
+ * @reg_class: Buffer for returning regulatory class
+ * @channel: Buffer for returning channel number
+ * Returns: 0 on success, -1 if the specified frequency is unknown
+ */
+int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class,
+			u8 *channel)
+{
+	/* TODO: more operating classes */
+	if (freq >= 2412 && freq <= 2472) {
+		*reg_class = 81; /* 2.407 GHz, channels 1..13 */
+		*channel = (freq - 2407) / 5;
+		return 0;
+	}
+
+	if (freq == 2484) {
+		*reg_class = 82; /* channel 14 */
+		*channel = 14;
+		return 0;
+	}
+
+	if (freq >= 5180 && freq <= 5240) {
+		*reg_class = 115; /* 5 GHz, channels 36..48 */
+		*channel = (freq - 5000) / 5;
+		return 0;
+	}
+
+	if (freq >= 5745 && freq <= 5805) {
+		*reg_class = 124; /* 5 GHz, channels 149..161 */
+		*channel = (freq - 5000) / 5;
+		return 0;
+	}
+
+	return -1;
+}
+
+
+static void p2p_reg_class_intersect(const struct p2p_reg_class *a,
+				    const struct p2p_reg_class *b,
+				    struct p2p_reg_class *res)
+{
+	size_t i, j;
+
+	res->reg_class = a->reg_class;
+
+	for (i = 0; i < a->channels; i++) {
+		for (j = 0; j < b->channels; j++) {
+			if (a->channel[i] != b->channel[j])
+				continue;
+			res->channel[res->channels] = a->channel[i];
+			res->channels++;
+			if (res->channels == P2P_MAX_REG_CLASS_CHANNELS)
+				return;
+		}
+	}
+}
+
+
+/**
+ * p2p_channels_intersect - Intersection of supported channel lists
+ * @a: First set of supported channels
+ * @b: Second set of supported channels
+ * @res: Data structure for returning the intersection of support channels
+ *
+ * This function can be used to find a common set of supported channels. Both
+ * input channels sets are assumed to use the same country code. If different
+ * country codes are used, the regulatory class numbers may not be matched
+ * correctly and results are undefined.
+ */
+void p2p_channels_intersect(const struct p2p_channels *a,
+			    const struct p2p_channels *b,
+			    struct p2p_channels *res)
+{
+	size_t i, j;
+
+	os_memset(res, 0, sizeof(*res));
+
+	for (i = 0; i < a->reg_classes; i++) {
+		const struct p2p_reg_class *a_reg = &a->reg_class[i];
+		for (j = 0; j < b->reg_classes; j++) {
+			const struct p2p_reg_class *b_reg = &b->reg_class[j];
+			if (a_reg->reg_class != b_reg->reg_class)
+				continue;
+			p2p_reg_class_intersect(
+				a_reg, b_reg,
+				&res->reg_class[res->reg_classes]);
+			if (res->reg_class[res->reg_classes].channels) {
+				res->reg_classes++;
+				if (res->reg_classes == P2P_MAX_REG_CLASSES)
+					return;
+			}
+		}
+	}
+}
+
+
+/**
+ * p2p_channels_includes - Check whether a channel is included in the list
+ * @channels: List of supported channels
+ * @reg_class: Regulatory class of the channel to search
+ * @channel: Channel number of the channel to search
+ * Returns: 1 if channel was found or 0 if not
+ */
+int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
+			  u8 channel)
+{
+	size_t i, j;
+	for (i = 0; i < channels->reg_classes; i++) {
+		const struct p2p_reg_class *reg = &channels->reg_class[i];
+		if (reg->reg_class != reg_class)
+			continue;
+		for (j = 0; j < reg->channels; j++) {
+			if (reg->channel[j] == channel)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+
+int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq)
+{
+	u8 op_reg_class, op_channel;
+	if (p2p_freq_to_channel(p2p->cfg->country, freq,
+				&op_reg_class, &op_channel) < 0)
+		return 0;
+	return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+				     op_channel);
+}