P2P: Add initial version of P2P Module
authorJouni Malinen <jouni.malinen@atheros.com>
Sun, 18 Jul 2010 21:30:25 +0000 (14:30 -0700)
committerJouni Malinen <j@w1.fi>
Thu, 9 Sep 2010 14:17:17 +0000 (07:17 -0700)
26 files changed:
src/Makefile
src/ap/p2p_hostapd.c [new file with mode: 0644]
src/ap/p2p_hostapd.h [new file with mode: 0644]
src/common/wpa_ctrl.h
src/p2p/Makefile [new file with mode: 0644]
src/p2p/p2p.c [new file with mode: 0644]
src/p2p/p2p.h [new file with mode: 0644]
src/p2p/p2p_build.c [new file with mode: 0644]
src/p2p/p2p_dev_disc.c [new file with mode: 0644]
src/p2p/p2p_go_neg.c [new file with mode: 0644]
src/p2p/p2p_group.c [new file with mode: 0644]
src/p2p/p2p_i.h [new file with mode: 0644]
src/p2p/p2p_invitation.c [new file with mode: 0644]
src/p2p/p2p_parse.c [new file with mode: 0644]
src/p2p/p2p_pd.c [new file with mode: 0644]
src/p2p/p2p_sd.c [new file with mode: 0644]
src/p2p/p2p_utils.c [new file with mode: 0644]
src/utils/wpabuf.h
wpa_supplicant/Makefile
wpa_supplicant/ap.c
wpa_supplicant/ap.h
wpa_supplicant/p2p_supplicant.c [new file with mode: 0644]
wpa_supplicant/p2p_supplicant.h [new file with mode: 0644]
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h
wpa_supplicant/wps_supplicant.c

index f47da7b..d73a175 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet radius rsn_supp tls utils wps
+SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p radius rsn_supp tls utils wps
 
 all:
        for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
diff --git a/src/ap/p2p_hostapd.c b/src/ap/p2p_hostapd.c
new file mode 100644 (file)
index 0000000..7f99830
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * hostapd / P2P integration
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "p2p/p2p.h"
+#include "sta_info.h"
+#include "p2p_hostapd.h"
+
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+                           char *buf, size_t buflen)
+{
+       if (sta->p2p_ie == NULL)
+               return 0;
+
+       return p2p_ie_text(sta->p2p_ie, buf, buf + buflen);
+}
diff --git a/src/ap/p2p_hostapd.h b/src/ap/p2p_hostapd.h
new file mode 100644 (file)
index 0000000..31aed15
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * hostapd / P2P integration
+ * 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.
+ */
+
+#ifndef P2P_HOSTAPD_H
+#define P2P_HOSTAPD_H
+
+#ifdef CONFIG_P2P
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+                           char *buf, size_t buflen);
+
+#else /* CONFIG_P2P */
+
+int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
+                           char *buf, size_t buflen)
+{
+       return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+#endif /* P2P_HOSTAPD_H */
index 5780484..88412ba 100644 (file)
@@ -90,6 +90,32 @@ extern "C" {
 #define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE "
 #define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
 
+/** P2P device found */
+#define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND "
+/** A P2P device requested GO negotiation, but we were not ready to start the
+ * negotiation */
+#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST "
+#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS "
+#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE "
+#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS "
+#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE "
+#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED "
+#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED "
+/* parameters: <peer address> <PIN> */
+#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ "
+/* parameters: <peer address> */
+#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP "
+/* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */
+#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
+/* parameters: <src addr> <update indicator> <TLVs> */
+#define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP "
+#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
+#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
+
 /* hostapd control interface - fixed message prefixes */
 #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
 #define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
diff --git a/src/p2p/Makefile b/src/p2p/Makefile
new file mode 100644 (file)
index 0000000..cffba62
--- /dev/null
@@ -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 (file)
index 0000000..6fb1a0c
--- /dev/null
@@ -0,0 +1,2900 @@
+/*
+ * 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);
+
+
+/**
+ * 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->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->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->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->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->device_name, cli->dev_name, cli->dev_name_len);
+       dev->device_name[cli->dev_name_len] = '\0';
+       dev->dev_capab = cli->dev_capab;
+       dev->config_methods = cli->config_methods;
+       os_memcpy(dev->pri_dev_type, cli->pri_dev_type, 8);
+}
+
+
+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->p2p_device_addr,
+                               dev->p2p_device_addr, dev->pri_dev_type,
+                               dev->device_name, dev->config_methods,
+                               dev->dev_capab, 0);
+               }
+
+               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;
+}
+
+
+/**
+ * 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.
+ */
+static 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;
+
+       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;
+       }
+
+       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->p2p_device_addr), dev->listen_freq, freq,
+                       msg.ds_params ? *msg.ds_params : -1);
+       }
+       dev->listen_freq = freq;
+       dev->level = level;
+
+       if (msg.pri_dev_type)
+               os_memcpy(dev->pri_dev_type, msg.pri_dev_type,
+                         sizeof(dev->pri_dev_type));
+       os_memcpy(dev->device_name, msg.device_name, sizeof(dev->device_name));
+       dev->config_methods = msg.config_methods ? msg.config_methods :
+               msg.wps_config_methods;
+       if (msg.capability) {
+               dev->dev_capab = msg.capability[0];
+               dev->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);
+       }
+
+       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->p2p_device_addr,
+                           dev->pri_dev_type, dev->device_name,
+                           dev->config_methods, dev->dev_capab,
+                           dev->group_capab);
+       dev->flags |= P2P_DEV_REPORTED;
+
+       return 0;
+}
+
+
+static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
+{
+       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;
+
+       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) < 0) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Scan request failed");
+               p2p_continue_find(p2p);
+       }
+}
+
+
+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;
+
+       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;
+}
+
+
+/*
+ * 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
+
+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);
+}
+
+
+int p2p_find(struct p2p_data *p2p, unsigned int timeout,
+            enum p2p_discovery_type type)
+{
+       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->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);
+               break;
+       case P2P_FIND_ONLY_SOCIAL:
+               res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0);
+               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(struct p2p_data *p2p)
+{
+       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->start_after_scan = P2P_AFTER_SCAN_NOTHING;
+       p2p->go_neg_peer = NULL;
+       p2p->sd_peer = NULL;
+       p2p->invite_peer = NULL;
+       p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+}
+
+
+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 (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));
+       }
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Own preference for operation channel: "
+               "Regulatory Class %u Channel %u%s",
+               p2p->op_reg_class, p2p->op_channel,
+               force_freq ? " (forced)" : "");
+
+       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->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->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);
+
+       dev->wps_method = wps_method;
+       dev->status = P2P_SC_SUCCESS;
+       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 (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));
+       }
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Own preference for operation channel: "
+               "Regulatory Class %u Channel %u%s",
+               p2p->op_reg_class, p2p->op_channel,
+               force_freq ? " (forced)" : "");
+
+       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;
+
+       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);
+
+       if (msg->pri_dev_type)
+               os_memcpy(dev->pri_dev_type, msg->pri_dev_type,
+                         sizeof(dev->pri_dev_type));
+       os_memcpy(dev->device_name, msg->device_name,
+                 sizeof(dev->device_name));
+       dev->config_methods = msg->config_methods ? msg->config_methods :
+               msg->wps_config_methods;
+       if (msg->capability) {
+               dev->dev_capab = msg->capability[0];
+               dev->group_capab = msg->capability[1];
+       }
+       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->p2p_device_addr),
+                               dev->listen_freq, freq);
+                       dev->listen_freq = freq;
+               }
+       }
+       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 (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->p2p_device_addr), dev->dev_capab,
+                       dev->group_capab, dev->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->p2p_device_addr,
+                           dev->pri_dev_type, dev->device_name,
+                           dev->config_methods, dev->dev_capab,
+                           dev->group_capab);
+}
+
+
+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->p2p_device_addr),
+               go ? "local end" : "peer");
+
+       os_memset(&res, 0, sizeof(res));
+       res.role_go = go;
+       os_memcpy(res.peer_device_addr, peer->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;
+
+       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;
+               }
+       }
+
+       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);
+               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.capability) {
+               dev->dev_capab = msg.capability[0];
+               dev->group_capab = msg.capability[1];
+       }
+
+       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]);
+       }
+
+       os_memcpy(dev->device_name, msg.device_name, sizeof(dev->device_name));
+
+       if (msg.wps_pri_dev_type)
+               os_memcpy(dev->pri_dev_type, msg.wps_pri_dev_type,
+                         sizeof(dev->pri_dev_type));
+
+       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->p2p_device_addr), dev->dev_capab,
+               dev->group_capab, dev->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;
+
+       /* TODO: add more info into WPS IE; maybe get from WPS module? */
+       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->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->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 *tmp;
+       u8 *lpos;
+       size_t tmplen;
+       int res;
+
+       if (!(p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED))
+               return 0;
+
+       /*
+        * (Re)Association Request - P2P IE
+        * P2P Capability attribute (shall be present)
+        * P2P Interface attribute (present if concurrent device)
+        */
+       tmp = wpabuf_alloc(200);
+       if (tmp == NULL)
+               return -1;
+
+       lpos = p2p_buf_add_ie_hdr(tmp);
+       p2p_buf_add_capability(tmp, p2p->dev_capab, 0);
+       if (p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER)
+               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 *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);
+
+       /*
+        * (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);
+       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);
+
+       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);
+       os_free(p2p->cfg->dev_name);
+       os_free(p2p->groups);
+       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);
+}
+
+
+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_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;
+}
+
+
+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) {
+                       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->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->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->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);
+}
+
+
+static void p2p_go_neg_conf_cb(struct p2p_data *p2p, int success)
+{
+       struct p2p_device *dev;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: GO Negotiation Confirm TX callback: success=%d",
+               success);
+       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+       if (!success) {
+               /*
+                * 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, int success)
+{
+       enum p2p_pending_action_state state;
+
+       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+               "P2P: Action frame TX callback (state=%d freq=%u dst=" MACSTR
+               " src=" MACSTR " bssid=" MACSTR " success=%d",
+               p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
+               MAC2STR(bssid), 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, success);
+               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 = 1;
+       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) {
+               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;
+               }
+               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);
+       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->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"
+                         "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\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->pri_dev_type,
+                                              devtype, sizeof(devtype)),
+                         dev->device_name,
+                         dev->config_methods,
+                         dev->dev_capab,
+                         dev->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->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->cfg->send_action(p2p->cfg->cb_ctx, 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->cfg->send_action(p2p->cfg->cb_ctx, 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_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;
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
new file mode 100644 (file)
index 0000000..a1b7cab
--- /dev/null
@@ -0,0 +1,1216 @@
+/*
+ * 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;
+};
+
+struct p2p_data;
+
+enum p2p_scan_type {
+       P2P_SCAN_SOCIAL,
+       P2P_SCAN_FULL,
+       P2P_SCAN_SPECIFIC,
+       P2P_SCAN_SOCIAL_PLUS_ONE
+};
+
+/**
+ * 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;
+
+       /**
+        * 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];
+
+       /**
+        * dev_addr - P2P Device Address
+        */
+       u8 dev_addr[ETH_ALEN];
+
+       /**
+        * dev_name - Device Name
+        */
+       char *dev_name;
+
+       /**
+        * num_sec_dev_types - Number of sec_dev_type entries
+        */
+       size_t num_sec_dev_types;
+
+       /**
+        * 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;
+
+       /**
+        * 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
+        * 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);
+
+       /**
+        * 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
+        * @dev_addr: P2P Device Address of the found P2P Device
+        * @pri_dev_type: Primary Device Type
+        * @dev_name: Device Name
+        * @config_methods: Configuration Methods
+        * @dev_capab: Device Capabilities
+        * @group_capab: Group Capabilities
+        *
+        * 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 u8 *dev_addr,
+                         const u8 *pri_dev_type, const char *dev_name,
+                         u16 config_methods, u8 dev_capab, u8 group_capab);
+
+       /**
+        * 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
+        *
+        * 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);
+
+       /**
+        * 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_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);
+
+/**
+ * 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
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_find(struct p2p_data *p2p, unsigned int timeout,
+            enum p2p_discovery_type type);
+
+/**
+ * p2p_stop_find - Stop P2P Find (Device Discovery)
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_stop_find(struct p2p_data *p2p);
+
+/**
+ * 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);
+
+/**
+ * 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)
+ * @success: Whether the transmission succeeded
+ *
+ * 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, int success);
+
+/**
+ * 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];
+
+       /**
+        * 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);
+};
+
+/**
+ * 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
+ * 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);
+
+/**
+ * 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_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);
+
+#endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
new file mode 100644 (file)
index 0000000..c45ae89
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * 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) {
+               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);
+}
+
+
+void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, u16 pw_id,
+                     int all_attr)
+{
+       u8 *len;
+       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) {
+               size_t nlen;
+
+               wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
+               wpabuf_put_be16(buf, 1);
+               wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO);
+
+#if 0
+               /* FIX */
+               wps_build_uuid_e(buf, reg->wps->uuid);
+               wps_build_manufacturer(dev, buf);
+               wps_build_model_name(dev, buf);
+               wps_build_model_number(dev, buf);
+               wps_build_serial_number(dev, buf);
+#endif
+
+               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);
+
+               wpabuf_put_be16(buf, ATTR_DEV_NAME);
+               nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0;
+               wpabuf_put_be16(buf, nlen);
+               if (p2p->cfg->dev_name)
+                       wpabuf_put_data(buf, p2p->cfg->dev_name, nlen);
+
+               wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
+               wpabuf_put_be16(buf, 2);
+               wpabuf_put_be16(buf, 0); /* FIX: ? */
+       }
+
+       wps_build_version2(buf);
+
+       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 (file)
index 0000000..ac2a177
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * 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->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->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->p2p_device_addr), MAC2STR(dev->p2p_device_addr));
+
+       p2p->pending_client_disc_go = go;
+       os_memcpy(p2p->pending_client_disc_addr, dev->p2p_device_addr,
+                 ETH_ALEN);
+       p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST;
+       if (p2p->cfg->send_action(p2p->cfg->cb_ctx, dev->oper_freq,
+                                 go->p2p_device_addr, p2p->cfg->dev_addr,
+                                 go->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->cfg->send_action(p2p->cfg->cb_ctx, 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->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->p2p_device_addr, ETH_ALEN) != 0 ||
+           os_memcmp(p2p->go_neg_peer->member_in_go_dev, go->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");
+               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 (file)
index 0000000..dcd5dbd
--- /dev/null
@@ -0,0 +1,1064 @@
+/*
+ * 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[3] != 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;
+       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->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;
+       if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq,
+                                 dev->p2p_device_addr, p2p->cfg->dev_addr,
+                                 dev->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->flags & P2P_DEV_PREFER_PERSISTENT_GROUP))
+               group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+       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;
+}
+
+
+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);
+       } 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");
+                       /* TODO: check if force_freq is needed */
+                       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));
+               }
+
+               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)) {
+                               struct p2p_reg_class *cl;
+                               cl = &intersection.reg_class[0];
+                               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                                       "P2P: Selected operating channel "
+                                       "(reg_class %u channel %u) not "
+                                       "acceptable to the peer - pick "
+                                       "another channel (reg_class %u "
+                                       "channel %u)",
+                                       p2p->op_reg_class, p2p->op_channel,
+                                       cl->reg_class, cl->channel[0]);
+                               p2p->op_reg_class = cl->reg_class;
+                               p2p->op_channel = cl->channel[0];
+                       }
+
+                       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);
+
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: GO Negotiation with " MACSTR, MAC2STR(sa));
+               if (p2p->state != P2P_IDLE)
+                       p2p_stop_find(p2p);
+               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:
+       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->cfg->send_action(p2p->cfg->cb_ctx, 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->flags & P2P_DEV_PREFER_PERSISTENT_GROUP)
+               group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+       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) {
+               /* TODO: Store SSID for Provisioning step */
+       } else if (!go) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Mandatory P2P Group ID attribute missing from "
+                       "GO Negotiation Response");
+#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 */
+       }
+
+       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)) {
+                       struct p2p_reg_class *cl;
+                       cl = &intersection.reg_class[0];
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                               "P2P: Selected operating channel "
+                               "(reg_class %u channel %u) not "
+                               "acceptable to the peer - pick "
+                               "another channel (reg_class %u "
+                               "channel %u)",
+                               p2p->op_reg_class, p2p->op_channel,
+                               cl->reg_class, cl->channel[0]);
+                       p2p->op_reg_class = cl->reg_class;
+                       p2p->op_channel = cl->channel[0];
+               }
+
+               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->cfg->send_action(p2p->cfg->cb_ctx, 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) {
+               /* TODO: Store SSID for Provisioning step */
+       } 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");
+#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 (file)
index 0000000..ab0f9a1
--- /dev/null
@@ -0,0 +1,628 @@
+/*
+ * 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;
+       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);
+
+       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;
+       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 = group->p2p;
+
+       if (group == NULL)
+               return;
+
+       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 (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;
+       group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+       if (group->group_formation)
+               group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION;
+       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);
+
+       if (group->members) {
+               /* 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 == NULL) {
+               p2p_group_free_member(m);
+               return -1;
+       }
+
+       m->client_info = p2p_build_client_info(addr, m->p2p_ie, &m->dev_capab,
+                                              m->dev_addr);
+       if (m->client_info == NULL) {
+               p2p_group_free_member(m);
+               return -1;
+       }
+
+       m->next = group->members;
+       group->members = m;
+
+       p2p_group_update_ies(group);
+
+       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);
+               p2p_group_update_ies(group);
+       }
+}
+
+
+/**
+ * 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) {
+               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) {
+               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;
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
new file mode 100644 (file)
index 0000000..ccd46d3
--- /dev/null
@@ -0,0 +1,580 @@
+/*
+ * 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;
+
+       u8 p2p_device_addr[ETH_ALEN]; /* P2P Device Address of the peer */
+       u8 pri_dev_type[8];
+       char device_name[33];
+       u16 config_methods;
+       u8 dev_capab;
+       u8 group_capab;
+
+       /*
+        * 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)
+       unsigned int flags;
+
+       int status; /* enum p2p_status_code */
+       unsigned int wait_count;
+       unsigned int invitation_reqs;
+
+       u16 ext_listen_period;
+       u16 ext_listen_interval;
+};
+
+struct p2p_sd_query {
+       struct p2p_sd_query *next;
+       u8 peer[ETH_ALEN];
+       int for_all_peers;
+       struct wpabuf *tlvs;
+};
+
+/**
+ * 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;
+
+       /* 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_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;
+};
+
+/**
+ * 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;
+
+       /* 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 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);
+
+#endif /* P2P_I_H */
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
new file mode 100644 (file)
index 0000000..89662b2
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ * 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_buf_add_config_timeout(buf, 0, 0);
+       else
+               p2p_buf_add_config_timeout(buf, 100, 20);
+       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->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_invitation_flags(buf, p2p->inv_persistent ?
+                                    P2P_INVITATION_FLAGS_TYPE : 0);
+       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) {
+               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->cfg->send_action(p2p->cfg->cb_ctx, 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->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->cfg->send_action(p2p->cfg->cb_ctx, freq,
+                                 dev->p2p_device_addr, p2p->cfg->dev_addr,
+                                 dev->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,
+                                             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",
+               MAC2STR(peer), role);
+       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->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 (file)
index 0000000..07b9b51
--- /dev/null
@@ -0,0 +1,682 @@
+/*
+ * 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);
+               for (i = 0; i < nlen; i++) {
+                       if (msg->device_name[i] == '\0')
+                               break;
+                       if (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;
+
+       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)));
+       }
+
+       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] < 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;
+}
+
+
+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 (file)
index 0000000..2a16315
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * 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->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) {
+               wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+                       "P2P: Provision Discovery Request from "
+                       "unknown peer " 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->cfg->send_action(p2p->cfg->cb_ctx, 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->p2p_device_addr));
+               return -1;
+       }
+
+       if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
+               if (!(dev->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->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->cfg->send_action(p2p->cfg->cb_ctx, freq,
+                                 dev->p2p_device_addr, p2p->cfg->dev_addr,
+                                 dev->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 (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 (file)
index 0000000..7ac4a27
--- /dev/null
@@ -0,0 +1,526 @@
+/*
+ * 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->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->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_sd_response(u8 dialog_token, u16 status_code,
+                                            u16 update_indic,
+                                            const 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_RESP);
+       wpabuf_put_u8(buf, dialog_token);
+       wpabuf_put_le16(buf, status_code);
+       wpabuf_put_le16(buf, 0); /* GAS 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) */
+
+       /* 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);
+       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;
+}
+
+
+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->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->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->cfg->send_action(p2p->cfg->cb_ctx, freq,
+                                 dev->p2p_device_addr, p2p->cfg->dev_addr,
+                                 dev->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;
+       resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS,
+                                    p2p->srv_update_indic, resp_tlvs);
+       if (resp == NULL)
+               return;
+
+       p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+       if (p2p->cfg->send_action(p2p->cfg->cb_ctx, 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)
+{
+       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->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,
+               MAC2STR(sa));
+
+       if (len < 5 + 2)
+               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 (*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)
+               return;
+       slen = WPA_GET_LE16(pos);
+       pos += 2;
+       if (pos + slen > end)
+               return;
+       end = pos + slen;
+
+       /* 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_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 (file)
index 0000000..7e8870a
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * 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;
+}
index a150455..cccfcc8 100644 (file)
@@ -117,6 +117,12 @@ static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data)
        WPA_PUT_LE16(pos, data);
 }
 
+static inline void wpabuf_put_le32(struct wpabuf *buf, u32 data)
+{
+       u8 *pos = wpabuf_put(buf, 4);
+       WPA_PUT_LE32(pos, data);
+}
+
 static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data)
 {
        u8 *pos = wpabuf_put(buf, 2);
index 3301e61..c113dd6 100644 (file)
@@ -166,6 +166,26 @@ CFLAGS += -DCONFIG_IBSS_RSN
 OBJS += ibss_rsn.o
 endif
 
+ifdef CONFIG_P2P
+OBJS += p2p_supplicant.o
+OBJS += ../src/p2p/p2p.o
+OBJS += ../src/p2p/p2p_utils.o
+OBJS += ../src/p2p/p2p_parse.o
+OBJS += ../src/p2p/p2p_build.o
+OBJS += ../src/p2p/p2p_go_neg.o
+OBJS += ../src/p2p/p2p_sd.o
+OBJS += ../src/p2p/p2p_pd.o
+OBJS += ../src/p2p/p2p_invitation.o
+OBJS += ../src/p2p/p2p_dev_disc.o
+OBJS += ../src/p2p/p2p_group.o
+OBJS += ../src/ap/p2p_hostapd.o
+CFLAGS += -DCONFIG_P2P
+NEED_80211_COMMON=y
+ifdef CONFIG_P2P_STRICT
+CFLAGS += -DCONFIG_P2P_STRICT
+endif
+endif
+
 ifdef CONFIG_NO_WPA2
 CFLAGS += -DCONFIG_NO_WPA2
 endif
index ab63d6f..85a8bcd 100644 (file)
@@ -22,6 +22,7 @@
 #ifdef NEED_AP_MLME
 #include "ap/ieee802_11.h"
 #endif /* NEED_AP_MLME */
+#include "ap/beacon.h"
 #include "ap/ieee802_1x.h"
 #include "ap/wps_hostapd.h"
 #include "ap/ctrl_iface_ap.h"
@@ -471,6 +472,23 @@ int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
 #endif /* CONFIG_CTRL_IFACE */
 
 
+int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s)
+{
+       struct hostapd_iface *iface = wpa_s->ap_iface;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+       struct hostapd_data *hapd;
+
+       if (ssid == NULL || wpa_s->ap_iface == NULL)
+               return -1;
+
+       ieee802_11_set_beacons(iface);
+       hapd = iface->bss[0];
+       hapd->drv.set_ap_wps_ie(hapd);
+
+       return 0;
+}
+
+
 int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
                                      const u8 *addr)
 {
index 381a432..b162d14 100644 (file)
@@ -37,6 +37,7 @@ void ap_tx_status(void *ctx, const u8 *addr,
 void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len);
 void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt);
 void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok);
+int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s);
 int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
                                      const u8 *addr);
 
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
new file mode 100644 (file)
index 0000000..16864c7
--- /dev/null
@@ -0,0 +1,3139 @@
+/*
+ * wpa_supplicant - P2P
+ * 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_common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_i.h"
+#include "p2p/p2p.h"
+#include "ap/hostapd.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "ap.h"
+#include "config_ssid.h"
+#include "config.h"
+#include "mlme.h"
+#include "notify.h"
+#include "scan.h"
+#include "bss.h"
+#include "wps_supplicant.h"
+#include "p2p_supplicant.h"
+
+
+static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx);
+static struct wpa_supplicant *
+wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
+                        int go);
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s);
+static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
+
+
+static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
+                                     struct wpa_scan_results *scan_res)
+{
+       size_t i;
+
+       if (wpa_s->global->p2p_disabled)
+               return;
+
+       wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS)",
+                  (int) scan_res->num);
+
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *bss = scan_res->res[i];
+               if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid,
+                                        bss->freq, bss->level,
+                                        (const u8 *) (bss + 1),
+                                        bss->ie_len) > 0)
+                       return;
+       }
+
+       p2p_scan_res_handled(wpa_s->global->p2p);
+}
+
+
+static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_driver_scan_params params;
+       int ret;
+       struct wpabuf *wps_ie, *ies;
+       int social_channels[] = { 2412, 2437, 2462, 0, 0 };
+
+       os_memset(&params, 0, sizeof(params));
+
+       /* P2P Wildcard SSID */
+       params.num_ssids = 1;
+       params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+       params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+
+       wpa_s->wps->dev.p2p = 1;
+       wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
+                                       WPS_REQ_ENROLLEE);
+       if (wps_ie == NULL)
+               return -1;
+
+       ies = wpabuf_alloc(wpabuf_len(wps_ie) + 100);
+       if (ies == NULL) {
+               wpabuf_free(wps_ie);
+               return -1;
+       }
+       wpabuf_put_buf(ies, wps_ie);
+       wpabuf_free(wps_ie);
+
+       p2p_scan_ie(wpa_s->global->p2p, ies);
+
+       params.extra_ies = wpabuf_head(ies);
+       params.extra_ies_len = wpabuf_len(ies);
+
+       switch (type) {
+       case P2P_SCAN_SOCIAL:
+               params.freqs = social_channels;
+               break;
+       case P2P_SCAN_FULL:
+               break;
+       case P2P_SCAN_SPECIFIC:
+               social_channels[0] = freq;
+               social_channels[1] = 0;
+               params.freqs = social_channels;
+               break;
+       case P2P_SCAN_SOCIAL_PLUS_ONE:
+               social_channels[3] = freq;
+               params.freqs = social_channels;
+               break;
+       }
+
+       wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+               ret = ieee80211_sta_req_scan(wpa_s, &params);
+       else
+               ret = wpa_drv_scan(wpa_s, &params);
+
+       wpabuf_free(ies);
+
+       return ret;
+}
+
+
+#ifdef CONFIG_CLIENT_MLME
+static void p2p_rx_action_mlme(void *ctx, const u8 *buf, size_t len, int freq)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       const struct ieee80211_mgmt *mgmt;
+       size_t hdr_len;
+
+       if (wpa_s->global->p2p_disabled)
+               return;
+       mgmt = (const struct ieee80211_mgmt *) buf;
+       hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
+       if (hdr_len > len)
+               return;
+       p2p_rx_action(wpa_s->global->p2p, mgmt->da, mgmt->sa, mgmt->bssid,
+                     mgmt->u.action.category,
+                     &mgmt->u.action.u.vs_public_action.action,
+                     len - hdr_len, freq);
+}
+#endif /* CONFIG_CLIENT_MLME */
+
+
+static enum wpa_driver_if_type wpas_p2p_if_type(int p2p_group_interface)
+{
+       switch (p2p_group_interface) {
+       case P2P_GROUP_INTERFACE_PENDING:
+               return WPA_IF_P2P_GROUP;
+       case P2P_GROUP_INTERFACE_GO:
+               return WPA_IF_P2P_GO;
+       case P2P_GROUP_INTERFACE_CLIENT:
+               return WPA_IF_P2P_CLIENT;
+       }
+
+       return WPA_IF_P2P_GROUP;
+}
+
+
+static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_ssid *ssid;
+       char *gtype;
+
+       ssid = wpa_s->current_ssid;
+       if (ssid == NULL) {
+               /*
+                * The current SSID was not known, but there may still be a
+                * pending P2P group interface waiting for provisioning.
+                */
+               ssid = wpa_s->conf->ssid;
+               while (ssid) {
+                       if (ssid->p2p_group &&
+                           (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
+                            (ssid->key_mgmt & WPA_KEY_MGMT_WPS)))
+                               break;
+                       ssid = ssid->next;
+               }
+       }
+       if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO)
+               gtype = "GO";
+       else if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
+                (ssid && ssid->mode == WPAS_MODE_INFRA)) {
+               wpa_s->reassociate = 0;
+               wpa_s->disconnected = 1;
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+               gtype = "client";
+       } else
+               gtype = "GO";
+       wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s",
+               wpa_s->ifname, gtype);
+       if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
+               struct wpa_global *global;
+               char *ifname;
+               enum wpa_driver_if_type type;
+               wpa_printf(MSG_DEBUG, "P2P: Remove group interface %s",
+                       wpa_s->ifname);
+               global = wpa_s->global;
+               ifname = os_strdup(wpa_s->ifname);
+               type = wpas_p2p_if_type(wpa_s->p2p_group_interface);
+               wpa_supplicant_remove_iface(wpa_s->global, wpa_s);
+               wpa_s = global->ifaces;
+               if (wpa_s && ifname)
+                       wpa_drv_if_remove(wpa_s, type, ifname);
+               os_free(ifname);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network");
+       if (ssid && (ssid->p2p_group ||
+                    ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
+                    (ssid->key_mgmt & WPA_KEY_MGMT_WPS))) {
+               int id = ssid->id;
+               if (ssid == wpa_s->current_ssid)
+                       wpa_s->current_ssid = NULL;
+               wpas_notify_network_removed(wpa_s, ssid);
+               wpa_config_remove_network(wpa_s->conf, id);
+               wpa_supplicant_clear_status(wpa_s);
+       } else {
+               wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
+                          "found");
+       }
+       wpa_supplicant_ap_deinit(wpa_s);
+}
+
+
+static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s,
+                                    u8 *go_dev_addr,
+                                    const u8 *ssid, size_t ssid_len)
+{
+       struct wpa_bss *bss;
+       const u8 *bssid;
+       struct wpabuf *p2p;
+       u8 group_capab;
+       const u8 *addr;
+
+       if (wpa_s->go_params)
+               bssid = wpa_s->go_params->peer_interface_addr;
+       else
+               bssid = wpa_s->bssid;
+
+       bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len);
+       if (bss == NULL) {
+               u8 iface_addr[ETH_ALEN];
+               if (p2p_get_interface_addr(wpa_s->global->p2p, bssid,
+                                          iface_addr) == 0)
+                       bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len);
+       }
+       if (bss == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
+                          "group is persistent - BSS " MACSTR " not found",
+                          MAC2STR(bssid));
+               return 0;
+       }
+
+       p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+       if (p2p == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
+                          "group is persistent - BSS " MACSTR
+                          " did not include P2P IE", MAC2STR(bssid));
+               wpa_hexdump(MSG_DEBUG, "P2P: Probe Response IEs",
+                           (u8 *) (bss + 1), bss->ie_len);
+               wpa_hexdump(MSG_DEBUG, "P2P: Beacon IEs",
+                           ((u8 *) bss + 1) + bss->ie_len,
+                           bss->beacon_ie_len);
+               return 0;
+       }
+
+       group_capab = p2p_get_group_capab(p2p);
+       addr = p2p_get_go_dev_addr(p2p);
+       wpa_printf(MSG_DEBUG, "P2P: Checking whether group is persistent: "
+                  "group_capab=0x%x", group_capab);
+       if (addr) {
+               os_memcpy(go_dev_addr, addr, ETH_ALEN);
+               wpa_printf(MSG_DEBUG, "P2P: GO Device Address " MACSTR,
+                          MAC2STR(addr));
+       } else
+               os_memset(go_dev_addr, 0, ETH_ALEN);
+       wpabuf_free(p2p);
+
+       wpa_printf(MSG_DEBUG, "P2P: BSS " MACSTR " group_capab=0x%x "
+                  "go_dev_addr=" MACSTR,
+                  MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr));
+
+       return group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+}
+
+
+static void wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s,
+                                           struct wpa_ssid *ssid,
+                                           const u8 *go_dev_addr)
+{
+       struct wpa_ssid *s;
+       int changed = 0;
+
+       wpa_printf(MSG_DEBUG, "P2P: Storing credentials for a persistent "
+                  "group (GO Dev Addr " MACSTR ")", MAC2STR(go_dev_addr));
+       for (s = wpa_s->conf->ssid; s; s = s->next) {
+               if (s->disabled == 2 &&
+                   os_memcmp(go_dev_addr, s->bssid, ETH_ALEN) == 0 &&
+                   s->ssid_len == ssid->ssid_len &&
+                   os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0)
+                       break;
+       }
+
+       if (s) {
+               wpa_printf(MSG_DEBUG, "P2P: Update existing persistent group "
+                          "entry");
+               if (ssid->passphrase && !s->passphrase)
+                       changed = 1;
+               else if (ssid->passphrase && s->passphrase &&
+                        os_strcmp(ssid->passphrase, s->passphrase) != 0)
+                       changed = 1;
+       } else {
+               wpa_printf(MSG_DEBUG, "P2P: Create a new persistent group "
+                          "entry");
+               changed = 1;
+               s = wpa_config_add_network(wpa_s->conf);
+               if (s == NULL)
+                       return;
+               wpa_config_set_network_defaults(s);
+       }
+
+       s->p2p_group = 1;
+       s->p2p_persistent_group = 1;
+       s->disabled = 2;
+       s->bssid_set = 1;
+       os_memcpy(s->bssid, go_dev_addr, ETH_ALEN);
+       s->mode = ssid->mode;
+       s->auth_alg = WPA_AUTH_ALG_OPEN;
+       s->key_mgmt = WPA_KEY_MGMT_PSK;
+       s->proto = WPA_PROTO_RSN;
+       s->pairwise_cipher = WPA_CIPHER_CCMP;
+       if (ssid->passphrase) {
+               os_free(s->passphrase);
+               s->passphrase = os_strdup(ssid->passphrase);
+       }
+       if (ssid->psk_set) {
+               s->psk_set = 1;
+               os_memcpy(s->psk, ssid->psk, 32);
+       }
+       if (s->passphrase && !s->psk_set)
+               wpa_config_update_psk(s);
+       if (s->ssid == NULL || s->ssid_len < ssid->ssid_len) {
+               os_free(s->ssid);
+               s->ssid = os_malloc(ssid->ssid_len);
+       }
+       if (s->ssid) {
+               s->ssid_len = ssid->ssid_len;
+               os_memcpy(s->ssid, ssid->ssid, s->ssid_len);
+       }
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+       if (changed && wpa_s->conf->update_config &&
+           wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+               wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+       }
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
+
+static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
+                                          int success)
+{
+       struct wpa_ssid *ssid;
+       const char *ssid_txt;
+       int client;
+       int persistent;
+       u8 go_dev_addr[ETH_ALEN];
+
+       /*
+        * This callback is likely called for the main interface. Update wpa_s
+        * to use the group interface if a new interface was created for the
+        * group.
+        */
+       if (wpa_s->global->p2p_group_formation)
+               wpa_s = wpa_s->global->p2p_group_formation;
+       wpa_s->p2p_in_provisioning = 0;
+
+       if (!success) {
+               wpa_msg(wpa_s->parent, MSG_INFO,
+                       P2P_EVENT_GROUP_FORMATION_FAILURE);
+               wpas_p2p_group_delete(wpa_s);
+               return;
+       }
+
+       wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_SUCCESS);
+
+       ssid = wpa_s->current_ssid;
+       if (ssid && ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
+               ssid->mode = WPAS_MODE_P2P_GO;
+               p2p_group_notif_formation_done(wpa_s->p2p_group);
+               wpa_supplicant_ap_mac_addr_filter(wpa_s, NULL);
+       }
+
+       persistent = 0;
+       if (ssid) {
+               ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+               client = ssid->mode == WPAS_MODE_INFRA;
+               if (ssid->mode == WPAS_MODE_P2P_GO) {
+                       persistent = ssid->p2p_persistent_group;
+                       os_memcpy(go_dev_addr, wpa_s->parent->own_addr,
+                                 ETH_ALEN);
+               } else
+                       persistent = wpas_p2p_persistent_group(wpa_s,
+                                                              go_dev_addr,
+                                                              ssid->ssid,
+                                                              ssid->ssid_len);
+       } else {
+               ssid_txt = "";
+               client = wpa_s->p2p_group_interface ==
+                       P2P_GROUP_INTERFACE_CLIENT;
+       }
+
+       wpa_s->show_group_started = 0;
+       if (client) {
+               /*
+                * Indicate event only after successfully completed 4-way
+                * handshake, i.e., when the interface is ready for data
+                * packets.
+                */
+               wpa_s->show_group_started = 1;
+       } else if (ssid && ssid->passphrase == NULL && ssid->psk_set) {
+               char psk[65];
+               wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
+               wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+                       "%s GO ssid=\"%s\" psk=%s go_dev_addr=" MACSTR "%s",
+                       wpa_s->ifname, ssid_txt, psk, MAC2STR(go_dev_addr),
+                       persistent ? " [PERSISTENT]" : "");
+       } else {
+               wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+                       "%s GO ssid=\"%s\" passphrase=\"%s\" go_dev_addr="
+                       MACSTR "%s",
+                       wpa_s->ifname, ssid_txt,
+                       ssid && ssid->passphrase ? ssid->passphrase : "",
+                       MAC2STR(go_dev_addr),
+                       persistent ? " [PERSISTENT]" : "");
+       }
+
+       if (persistent)
+               wpas_p2p_store_persistent_group(wpa_s->parent, ssid,
+                                               go_dev_addr);
+}
+
+
+static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct wpa_supplicant *iface;
+       int res;
+       int without_roc;
+
+       without_roc = wpa_s->pending_action_without_roc;
+       wpa_s->pending_action_without_roc = 0;
+
+       if (wpa_s->pending_action_tx == NULL)
+               return;
+
+       if (wpa_s->off_channel_freq != wpa_s->pending_action_freq &&
+           wpa_s->pending_action_freq != 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX "
+                          "waiting for another freq=%u (off_channel_freq=%u)",
+                          wpa_s->pending_action_freq,
+                          wpa_s->off_channel_freq);
+               if (without_roc && wpa_s->off_channel_freq == 0) {
+                       /*
+                        * We may get here if wpas_send_action() found us to be
+                        * on the correct channel, but remain-on-channel cancel
+                        * event was received before getting here.
+                        */
+                       wpa_printf(MSG_DEBUG, "P2P: Schedule "
+                                  "remain-on-channel to send Action frame");
+                       if (wpa_drv_remain_on_channel(
+                                   wpa_s, wpa_s->pending_action_freq, 200) <
+                           0) {
+                               wpa_printf(MSG_DEBUG, "P2P: Failed to request "
+                                          "driver to remain on channel (%u "
+                                          "MHz) for Action Frame TX",
+                                          wpa_s->pending_action_freq);
+                       }
+               }
+               return;
+       }
+
+       /*
+        * This call is likely going to be on the P2P device instance if the
+        * driver uses a separate interface for that purpose. However, some
+        * Action frames are actually sent within a P2P Group and when that is
+        * the case, we need to follow power saving (e.g., GO buffering the
+        * frame for a client in PS mode or a client following the adverised
+        * NoA from its GO). To make that easier for the driver, select the
+        * correct group interface here.
+        */
+       if (os_memcmp(wpa_s->pending_action_src, wpa_s->own_addr, ETH_ALEN) !=
+           0) {
+               /*
+                * Try to find a group interface that matches with the source
+                * address.
+                */
+               iface = wpa_s->global->ifaces;
+               while (iface) {
+                       if (os_memcmp(wpa_s->pending_action_src,
+                                     iface->own_addr, ETH_ALEN) == 0)
+                               break;
+               }
+               if (iface) {
+                       wpa_printf(MSG_DEBUG, "P2P: Use group interface %s "
+                                  "instead of interface %s for Action TX",
+                                  iface->ifname, wpa_s->ifname);
+               } else
+                       iface = wpa_s;
+       } else
+               iface = wpa_s;
+
+       wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to "
+                  MACSTR " using interface %s",
+                  MAC2STR(wpa_s->pending_action_dst), iface->ifname);
+       res = wpa_drv_send_action(iface, wpa_s->pending_action_freq,
+                                 wpa_s->pending_action_dst,
+                                 wpa_s->pending_action_src,
+                                 wpa_s->pending_action_bssid,
+                                 wpabuf_head(wpa_s->pending_action_tx),
+                                 wpabuf_len(wpa_s->pending_action_tx));
+       if (res) {
+               wpa_printf(MSG_DEBUG, "P2P: Failed to send the pending "
+                          "Action frame");
+               /*
+                * Use fake TX status event to allow P2P state machine to
+                * continue.
+                */
+               wpas_send_action_tx_status(
+                       wpa_s, wpa_s->pending_action_dst,
+                       wpabuf_head(wpa_s->pending_action_tx),
+                       wpabuf_len(wpa_s->pending_action_tx), 0);
+       }
+}
+
+
+void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
+                               const u8 *data, size_t data_len, int ack)
+{
+       if (wpa_s->global->p2p_disabled)
+               return;
+
+       if (wpa_s->pending_action_tx == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - no "
+                          "pending operation");
+               return;
+       }
+
+       if (os_memcmp(dst, wpa_s->pending_action_dst, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - unknown "
+                          "destination address");
+               return;
+       }
+
+       wpabuf_free(wpa_s->pending_action_tx);
+       wpa_s->pending_action_tx = NULL;
+
+       p2p_send_action_cb(wpa_s->global->p2p, wpa_s->pending_action_freq,
+                          wpa_s->pending_action_dst,
+                          wpa_s->pending_action_src,
+                          wpa_s->pending_action_bssid,
+                          ack);
+
+       if (wpa_s->pending_pd_before_join &&
+           (os_memcmp(wpa_s->pending_action_dst, wpa_s->pending_join_dev_addr,
+                      ETH_ALEN) == 0 ||
+            os_memcmp(wpa_s->pending_action_dst,
+                      wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
+               wpa_s->pending_pd_before_join = 0;
+               wpa_printf(MSG_DEBUG, "P2P: Starting pending "
+                          "join-existing-group operation");
+               wpas_p2p_join_start(wpa_s);
+       }
+}
+
+
+static int wpas_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)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       wpa_printf(MSG_DEBUG, "P2P: Send action frame: freq=%d dst=" MACSTR
+                  " src=" MACSTR " bssid=" MACSTR,
+                  freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid));
+
+       if (wpa_s->pending_action_tx) {
+               wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX "
+                          "to " MACSTR, MAC2STR(wpa_s->pending_action_dst));
+               wpabuf_free(wpa_s->pending_action_tx);
+       }
+       wpa_s->pending_action_tx = wpabuf_alloc(len);
+       if (wpa_s->pending_action_tx == NULL)
+               return -1;
+       wpabuf_put_data(wpa_s->pending_action_tx, buf, len);
+       os_memcpy(wpa_s->pending_action_src, src, ETH_ALEN);
+       os_memcpy(wpa_s->pending_action_dst, dst, ETH_ALEN);
+       os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
+       wpa_s->pending_action_freq = freq;
+
+       if (wpa_s->off_channel_freq == freq || freq == 0) {
+               /* Already on requested channel; send immediately */
+               /* TODO: Would there ever be need to extend the current
+                * duration on the channel? */
+               wpa_s->pending_action_without_roc = 1;
+               eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+               eloop_register_timeout(0, 0, wpas_send_action_cb, wpa_s, NULL);
+               return 0;
+       }
+       wpa_s->pending_action_without_roc = 0;
+
+       wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted "
+                  "once the driver gets to the requested channel");
+       if (wait_time > wpa_s->max_remain_on_chan)
+               wait_time = wpa_s->max_remain_on_chan;
+       if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Failed to request driver "
+                          "to remain on channel (%u MHz) for Action "
+                          "Frame TX", freq);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void wpas_send_action_done(void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       wpa_printf(MSG_DEBUG, "P2P: Action frame sequence done notification");
+       wpabuf_free(wpa_s->pending_action_tx);
+       wpa_s->pending_action_tx = NULL;
+       if (wpa_s->off_channel_freq) {
+               wpa_drv_cancel_remain_on_channel(wpa_s);
+               wpa_s->off_channel_freq = 0;
+       }
+}
+
+
+static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s,
+                                   struct p2p_go_neg_results *params)
+{
+       if (wpa_s->go_params == NULL) {
+               wpa_s->go_params = os_malloc(sizeof(*params));
+               if (wpa_s->go_params == NULL)
+                       return -1;
+       }
+       os_memcpy(wpa_s->go_params, params, sizeof(*params));
+       return 0;
+}
+
+
+static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
+                                   struct p2p_go_neg_results *res)
+{
+       wpa_supplicant_ap_deinit(wpa_s);
+       wpas_copy_go_neg_results(wpa_s, res);
+       if (res->wps_method == WPS_PBC)
+               wpas_wps_start_pbc(wpa_s, NULL /* res->peer_interface_addr */,
+                                  1);
+       else
+               wpas_wps_start_pin(wpa_s, res->peer_interface_addr,
+                                  wpa_s->p2p_pin, 1);
+}
+
+
+static void p2p_go_configured(void *ctx, void *data)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct p2p_go_neg_results *params = data;
+       struct wpa_ssid *ssid;
+
+       ssid = wpa_s->current_ssid;
+       if (ssid && ssid->mode == WPAS_MODE_P2P_GO) {
+               wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning");
+               wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+                       "%s GO ssid=\"%s\" passphrase=\"%s\" go_dev_addr="
+                       MACSTR "%s",
+                       wpa_s->ifname,
+                       wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+                       params->passphrase ? params->passphrase : "",
+                       MAC2STR(wpa_s->parent->own_addr),
+                       params->persistent_group ? " [PERSISTENT]" : "");
+               if (params->persistent_group)
+                       wpas_p2p_store_persistent_group(
+                               wpa_s->parent, ssid,
+                               wpa_s->parent->own_addr);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning");
+       if (wpa_supplicant_ap_mac_addr_filter(wpa_s,
+                                             params->peer_interface_addr)) {
+               wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address "
+                          "filtering");
+               return;
+       }
+       if (params->wps_method == WPS_PBC)
+               wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr);
+       else if (wpa_s->p2p_pin[0])
+               wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
+                                         wpa_s->p2p_pin, NULL, 0);
+       os_free(wpa_s->go_params);
+       wpa_s->go_params = NULL;
+}
+
+
+static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
+                             struct p2p_go_neg_results *params,
+                             int group_formation)
+{
+       struct wpa_ssid *ssid;
+
+       if (wpas_copy_go_neg_results(wpa_s, params) < 0)
+               return;
+
+       ssid = wpa_config_add_network(wpa_s->conf);
+       if (ssid == NULL)
+               return;
+
+       wpa_config_set_network_defaults(ssid);
+       ssid->temporary = 1;
+       ssid->p2p_group = 1;
+       ssid->p2p_persistent_group = params->persistent_group;
+       ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION :
+               WPAS_MODE_P2P_GO;
+       ssid->frequency = params->freq;
+       ssid->ssid = os_zalloc(params->ssid_len + 1);
+       if (ssid->ssid) {
+               os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
+               ssid->ssid_len = params->ssid_len;
+       }
+       ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+       ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+       ssid->proto = WPA_PROTO_RSN;
+       ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+       ssid->passphrase = os_strdup(params->passphrase);
+
+       wpa_s->ap_configured_cb = p2p_go_configured;
+       wpa_s->ap_configured_cb_ctx = wpa_s;
+       wpa_s->ap_configured_cb_data = wpa_s->go_params;
+       wpa_s->connect_without_scan = 1;
+       wpa_s->reassociate = 1;
+       wpa_s->disconnected = 0;
+       wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
+                                 const struct wpa_supplicant *src)
+{
+       struct wpa_config *d;
+       const struct wpa_config *s;
+
+       d = dst->conf;
+       s = src->conf;
+
+#define C(n) if (s->n) d->n = os_strdup(s->n)
+       C(device_name);
+       C(manufacturer);
+       C(model_name);
+       C(model_number);
+       C(serial_number);
+       C(device_type);
+       C(config_methods);
+#undef C
+}
+
+
+static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
+                                       enum wpa_driver_if_type type)
+{
+       char ifname[120], force_ifname[120];
+
+       if (wpa_s->pending_interface_name[0]) {
+               wpa_printf(MSG_DEBUG, "P2P: Pending virtual interface exists "
+                          "- skip creation of a new one");
+               if (is_zero_ether_addr(wpa_s->pending_interface_addr)) {
+                       wpa_printf(MSG_DEBUG, "P2P: Pending virtual address "
+                                  "unknown?! ifname='%s'",
+                                  wpa_s->pending_interface_name);
+                       return -1;
+               }
+               return 0;
+       }
+
+       os_snprintf(ifname, sizeof(ifname), "%s-p2p-%d", wpa_s->ifname,
+                   wpa_s->p2p_group_idx);
+       force_ifname[0] = '\0';
+
+       wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group",
+                  ifname);
+       wpa_s->p2p_group_idx++;
+
+       wpa_s->pending_interface_type = type;
+       if (wpa_drv_if_add(wpa_s, type, ifname, NULL, NULL, force_ifname,
+                          wpa_s->pending_interface_addr) < 0) {
+               wpa_printf(MSG_ERROR, "P2P: Failed to create new group "
+                          "interface");
+               return -1;
+       }
+
+       if (force_ifname[0]) {
+               wpa_printf(MSG_DEBUG, "P2P: Driver forced interface name %s",
+                          force_ifname);
+               os_strlcpy(wpa_s->pending_interface_name, force_ifname,
+                          sizeof(wpa_s->pending_interface_name));
+       } else
+               os_strlcpy(wpa_s->pending_interface_name, ifname,
+                          sizeof(wpa_s->pending_interface_name));
+       wpa_printf(MSG_DEBUG, "P2P: Created pending virtual interface %s addr "
+                  MACSTR, wpa_s->pending_interface_name,
+                  MAC2STR(wpa_s->pending_interface_addr));
+
+       return 0;
+}
+
+
+static void wpas_p2p_remove_pending_group_interface(
+       struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->pending_interface_name[0] ||
+           is_zero_ether_addr(wpa_s->pending_interface_addr))
+               return; /* No pending virtual interface */
+
+       wpa_printf(MSG_DEBUG, "P2P: Removing pending group interface %s",
+                  wpa_s->pending_interface_name);
+       wpa_drv_if_remove(wpa_s, wpa_s->pending_interface_type,
+                         wpa_s->pending_interface_name);
+       os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
+       wpa_s->pending_interface_name[0] = '\0';
+}
+
+
+static struct wpa_supplicant *
+wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go)
+{
+       struct wpa_interface iface;
+       struct wpa_supplicant *group_wpa_s;
+
+       if (!wpa_s->pending_interface_name[0]) {
+               wpa_printf(MSG_ERROR, "P2P: No pending group interface");
+               return NULL;
+       }
+
+       os_memset(&iface, 0, sizeof(iface));
+       iface.ifname = wpa_s->pending_interface_name;
+       iface.driver = wpa_s->driver->name;
+       iface.ctrl_interface = wpa_s->conf->ctrl_interface;
+       iface.driver_param = wpa_s->conf->driver_param;
+       group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface);
+       if (group_wpa_s == NULL) {
+               wpa_printf(MSG_ERROR, "P2P: Failed to create new "
+                          "wpa_supplicant interface");
+               return NULL;
+       }
+       wpa_s->pending_interface_name[0] = '\0';
+       group_wpa_s->parent = wpa_s;
+       group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO :
+               P2P_GROUP_INTERFACE_CLIENT;
+       wpa_s->global->p2p_group_formation = group_wpa_s;
+
+       wpas_p2p_clone_config(group_wpa_s, wpa_s);
+
+       return group_wpa_s;
+}
+
+
+static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
+                                            void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
+       if (wpa_s->global->p2p)
+               p2p_group_formation_failed(wpa_s->global->p2p);
+       wpas_group_formation_completed(wpa_s, 0);
+}
+
+
+void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       if (wpa_s->off_channel_freq) {
+               wpa_drv_cancel_remain_on_channel(wpa_s);
+               wpa_s->off_channel_freq = 0;
+       }
+
+       if (res->status) {
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_FAILURE "status=%d",
+                       res->status);
+               wpas_p2p_remove_pending_group_interface(wpa_s);
+               return;
+       }
+
+       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS);
+
+       if (wpa_s->create_p2p_iface) {
+               struct wpa_supplicant *group_wpa_s =
+                       wpas_p2p_init_group_interface(wpa_s, res->role_go);
+               if (group_wpa_s == NULL) {
+                       wpas_p2p_remove_pending_group_interface(wpa_s);
+                       return;
+               }
+               if (group_wpa_s != wpa_s)
+                       os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin,
+                                 sizeof(group_wpa_s->p2p_pin));
+               os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
+               wpa_s->pending_interface_name[0] = '\0';
+               group_wpa_s->p2p_in_provisioning = 1;
+
+               if (res->role_go)
+                       wpas_start_wps_go(group_wpa_s, res, 1);
+               else
+                       wpas_start_wps_enrollee(group_wpa_s, res);
+       } else {
+               wpa_s->p2p_in_provisioning = 1;
+               wpa_s->global->p2p_group_formation = wpa_s;
+
+               if (res->role_go)
+                       wpas_start_wps_go(wpa_s, res, 1);
+               else
+                       wpas_start_wps_enrollee(ctx, res);
+       }
+
+       wpa_s->p2p_long_listen = 0;
+       eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+
+       eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+       /* TODO: add peer Config Timeout */
+       eloop_register_timeout(15, 0, wpas_p2p_group_formation_timeout, wpa_s,
+                              NULL);
+}
+
+
+void wpas_go_neg_req_rx(void *ctx, const u8 *src)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR,
+               MAC2STR(src));
+}
+
+
+void wpas_dev_found(void *ctx, const u8 *addr, const u8 *dev_addr,
+                   const u8 *pri_dev_type, const char *dev_name,
+                   u16 config_methods, u8 dev_capab, u8 group_capab)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       char devtype[WPS_DEV_TYPE_BUFSIZE];
+       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
+               " p2p_dev_addr=" MACSTR
+               " pri_dev_type=%s name='%s' config_methods=0x%x "
+               "dev_capab=0x%x group_capab=0x%x",
+               MAC2STR(addr), MAC2STR(dev_addr),
+               wps_dev_type_bin2str(pri_dev_type, devtype, sizeof(devtype)),
+               dev_name, config_methods, dev_capab, group_capab);
+}
+
+
+static int wpas_start_listen(void *ctx, unsigned int freq,
+                            unsigned int duration,
+                            const struct wpabuf *probe_resp_ie)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       wpa_drv_set_ap_wps_ie(wpa_s, NULL, probe_resp_ie);
+
+       if (wpa_drv_probe_req_report(wpa_s, 1) < 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to "
+                          "report received Probe Request frames");
+               return -1;
+       }
+
+       wpa_s->pending_listen_freq = freq;
+       wpa_s->pending_listen_duration = duration;
+
+       if (wpa_drv_remain_on_channel(wpa_s, freq, duration) < 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
+                          "to remain on channel (%u MHz) for Listen "
+                          "state", freq);
+               wpa_s->pending_listen_freq = 0;
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void wpas_stop_listen(void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       if (wpa_s->off_channel_freq) {
+               wpa_drv_cancel_remain_on_channel(wpa_s);
+               wpa_s->off_channel_freq = 0;
+       }
+       wpa_drv_probe_req_report(wpa_s, 0);
+}
+
+
+static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf));
+}
+
+
+static struct p2p_srv_bonjour *
+wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
+                            const struct wpabuf *query)
+{
+       struct p2p_srv_bonjour *bsrv;
+       size_t len;
+
+       len = wpabuf_len(query);
+       dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+                        struct p2p_srv_bonjour, list) {
+               if (len == wpabuf_len(bsrv->query) &&
+                   os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
+                             len) == 0)
+                       return bsrv;
+       }
+       return NULL;
+}
+
+
+static struct p2p_srv_upnp *
+wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
+                         const char *service)
+{
+       struct p2p_srv_upnp *usrv;
+
+       dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+                        struct p2p_srv_upnp, list) {
+               if (version == usrv->version &&
+                   os_strcmp(service, usrv->service) == 0)
+                       return usrv;
+       }
+       return NULL;
+}
+
+
+static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
+                                       u8 srv_trans_id)
+{
+       u8 *len_pos;
+
+       if (wpabuf_tailroom(resp) < 5)
+               return;
+
+       /* Length (to be filled) */
+       len_pos = wpabuf_put(resp, 2);
+       wpabuf_put_u8(resp, srv_proto);
+       wpabuf_put_u8(resp, srv_trans_id);
+       /* Status Code */
+       wpabuf_put_u8(resp, P2P_SD_PROTO_NOT_AVAILABLE);
+       /* Response Data: empty */
+       WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
+                               struct wpabuf *resp, u8 srv_trans_id)
+{
+       struct p2p_srv_bonjour *bsrv;
+       u8 *len_pos;
+
+       wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
+
+       if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+               wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+               return;
+       }
+
+       dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+                        struct p2p_srv_bonjour, list) {
+               if (wpabuf_tailroom(resp) <
+                   5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
+                       return;
+               /* Length (to be filled) */
+               len_pos = wpabuf_put(resp, 2);
+               wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+               wpabuf_put_u8(resp, srv_trans_id);
+               /* Status Code */
+               wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+               wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+                                 wpabuf_head(bsrv->resp),
+                                 wpabuf_len(bsrv->resp));
+               /* Response Data */
+               wpabuf_put_buf(resp, bsrv->query); /* Key */
+               wpabuf_put_buf(resp, bsrv->resp); /* Value */
+               WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+                            2);
+       }
+}
+
+
+static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
+                               struct wpabuf *resp, u8 srv_trans_id,
+                               const u8 *query, size_t query_len)
+{
+       struct p2p_srv_bonjour *bsrv;
+       struct wpabuf buf;
+       u8 *len_pos;
+
+       wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
+                         query, query_len);
+       if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+               wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+               wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
+                                           srv_trans_id);
+               return;
+       }
+
+       if (query_len == 0) {
+               wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+               return;
+       }
+
+       if (wpabuf_tailroom(resp) < 5)
+               return;
+       /* Length (to be filled) */
+       len_pos = wpabuf_put(resp, 2);
+       wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+       wpabuf_put_u8(resp, srv_trans_id);
+
+       wpabuf_set(&buf, query, query_len);
+       bsrv = wpas_p2p_service_get_bonjour(wpa_s, &buf);
+       if (bsrv == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
+                          "available");
+
+               /* Status Code */
+               wpabuf_put_u8(resp, P2P_SD_QUERY_DATA_NOT_AVAILABLE);
+               /* Response Data: empty */
+               WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+                            2);
+               return;
+       }
+
+       /* Status Code */
+       wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+       wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+                         wpabuf_head(bsrv->resp), wpabuf_len(bsrv->resp));
+
+       if (wpabuf_tailroom(resp) >=
+           wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp)) {
+               /* Response Data */
+               wpabuf_put_buf(resp, bsrv->query); /* Key */
+               wpabuf_put_buf(resp, bsrv->resp); /* Value */
+       }
+       WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
+                            struct wpabuf *resp, u8 srv_trans_id)
+{
+       struct p2p_srv_upnp *usrv;
+       u8 *len_pos;
+
+       wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
+
+       if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+               wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+               return;
+       }
+
+       dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+                        struct p2p_srv_upnp, list) {
+               if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
+                       return;
+
+               /* Length (to be filled) */
+               len_pos = wpabuf_put(resp, 2);
+               wpabuf_put_u8(resp, P2P_SERV_UPNP);
+               wpabuf_put_u8(resp, srv_trans_id);
+
+               /* Status Code */
+               wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+               /* Response Data */
+               wpabuf_put_u8(resp, usrv->version);
+               wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+                          usrv->service);
+               wpabuf_put_str(resp, usrv->service);
+               WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+                            2);
+       }
+}
+
+
+static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
+                            struct wpabuf *resp, u8 srv_trans_id,
+                            const u8 *query, size_t query_len)
+{
+       struct p2p_srv_upnp *usrv;
+       u8 *len_pos;
+       u8 version;
+       char *str;
+       int count = 0;
+
+       wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
+                         query, query_len);
+
+       if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+               wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+               wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
+                                           srv_trans_id);
+               return;
+       }
+
+       if (query_len == 0) {
+               wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+               return;
+       }
+
+       version = query[0];
+       str = os_malloc(query_len);
+       if (str == NULL)
+               return;
+       os_memcpy(str, query + 1, query_len - 1);
+       str[query_len - 1] = '\0';
+
+       if (wpabuf_tailroom(resp) < 5)
+               return;
+
+       /* Length (to be filled) */
+       len_pos = wpabuf_put(resp, 2);
+       wpabuf_put_u8(resp, P2P_SERV_UPNP);
+       wpabuf_put_u8(resp, srv_trans_id);
+
+       dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+                        struct p2p_srv_upnp, list) {
+               if (version != usrv->version)
+                       continue;
+
+               if (os_strcmp(str, "ssdp:all") != 0 &&
+                   os_strstr(usrv->service, str) == NULL)
+                       continue;
+
+               if (wpabuf_tailroom(resp) < 2)
+                       break;
+               if (count == 0) {
+                       /* Status Code */
+                       wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+                       /* Response Data */
+                       wpabuf_put_u8(resp, version);
+               } else
+                       wpabuf_put_u8(resp, ',');
+
+               count++;
+
+               wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+                          usrv->service);
+               if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
+                       break;
+               wpabuf_put_str(resp, usrv->service);
+       }
+
+       if (count == 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
+                          "available");
+               /* Status Code */
+               wpabuf_put_u8(resp, P2P_SD_QUERY_DATA_NOT_AVAILABLE);
+               /* Response Data: empty */
+       }
+
+       WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+                    u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       const u8 *pos = tlvs;
+       const u8 *end = tlvs + tlvs_len;
+       const u8 *tlv_end;
+       u16 slen;
+       struct wpabuf *resp;
+       u8 srv_proto, srv_trans_id;
+       size_t buf_len;
+       char *buf;
+
+       wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
+                   tlvs, tlvs_len);
+       buf_len = 2 * tlvs_len + 1;
+       buf = os_malloc(buf_len);
+       if (buf) {
+               wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+               wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
+                            MACSTR " %u %u %s",
+                            freq, MAC2STR(sa), dialog_token, update_indic,
+                            buf);
+               os_free(buf);
+       }
+
+       if (wpa_s->p2p_sd_over_ctrl_iface)
+               return; /* to be processed by an external program */
+
+       resp = wpabuf_alloc(10000);
+       if (resp == NULL)
+               return;
+
+       while (pos + 1 < end) {
+               wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
+               slen = WPA_GET_LE16(pos);
+               pos += 2;
+               if (pos + slen > end || slen < 2) {
+                       wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
+                                  "length");
+                       wpabuf_free(resp);
+                       return;
+               }
+               tlv_end = pos + slen;
+
+               srv_proto = *pos++;
+               wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+                          srv_proto);
+               srv_trans_id = *pos++;
+               wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+                          srv_trans_id);
+
+               wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
+                           pos, tlv_end - pos);
+
+               switch (srv_proto) {
+               case P2P_SERV_ALL_SERVICES:
+                       wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
+                                  "for all services");
+                       if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
+                           dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+                               wpa_printf(MSG_DEBUG, "P2P: No service "
+                                          "discovery protocols available");
+                               wpas_sd_add_proto_not_avail(
+                                       resp, P2P_SERV_ALL_SERVICES,
+                                       srv_trans_id);
+                               break;
+                       }
+                       wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+                       wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+                       break;
+               case P2P_SERV_BONJOUR:
+                       wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
+                                           pos, tlv_end - pos);
+                       break;
+               case P2P_SERV_UPNP:
+                       wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
+                                        pos, tlv_end - pos);
+                       break;
+               default:
+                       wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
+                                  "protocol %u", srv_proto);
+                       wpas_sd_add_proto_not_avail(resp, srv_proto,
+                                                   srv_trans_id);
+                       break;
+               }
+
+               pos = tlv_end;
+       }
+
+       wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
+
+       wpabuf_free(resp);
+}
+
+
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+                     const u8 *tlvs, size_t tlvs_len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       const u8 *pos = tlvs;
+       const u8 *end = tlvs + tlvs_len;
+       const u8 *tlv_end;
+       u16 slen;
+       size_t buf_len;
+       char *buf;
+
+       wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
+                   tlvs, tlvs_len);
+       buf_len = 2 * tlvs_len + 1;
+       buf = os_malloc(buf_len);
+       if (buf) {
+               wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+               wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_RESP MACSTR
+                            " %u %s",
+                            MAC2STR(sa), update_indic, buf);
+               os_free(buf);
+       }
+
+       while (pos < end) {
+               u8 srv_proto, srv_trans_id, status;
+
+               wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
+               slen = WPA_GET_LE16(pos);
+               pos += 2;
+               if (pos + slen > end || slen < 3) {
+                       wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
+                                  "length");
+                       return;
+               }
+               tlv_end = pos + slen;
+
+               srv_proto = *pos++;
+               wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+                          srv_proto);
+               srv_trans_id = *pos++;
+               wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+                          srv_trans_id);
+               status = *pos++;
+               wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
+                          status);
+
+               wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
+                           pos, tlv_end - pos);
+
+               pos = tlv_end;
+       }
+}
+
+
+void * wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+                          const struct wpabuf *tlvs)
+{
+       return p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
+}
+
+
+void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+                               u8 version, const char *query)
+{
+       struct wpabuf *tlvs;
+       void *ret;
+
+       tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
+       if (tlvs == NULL)
+               return NULL;
+       wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
+       wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
+       wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
+       wpabuf_put_u8(tlvs, version);
+       wpabuf_put_str(tlvs, query);
+       ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+       wpabuf_free(tlvs);
+       return ret;
+}
+
+
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *req)
+{
+       return p2p_sd_cancel_request(wpa_s->global->p2p, req);
+}
+
+
+void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
+                         const u8 *dst, u8 dialog_token,
+                         const struct wpabuf *resp_tlvs)
+{
+       p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
+                       resp_tlvs);
+}
+
+
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
+{
+       p2p_sd_service_update(wpa_s->global->p2p);
+}
+
+
+static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
+{
+       dl_list_del(&bsrv->list);
+       wpabuf_free(bsrv->query);
+       wpabuf_free(bsrv->resp);
+       os_free(bsrv);
+}
+
+
+static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
+{
+       dl_list_del(&usrv->list);
+       os_free(usrv->service);
+       os_free(usrv);
+}
+
+
+void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
+{
+       struct p2p_srv_bonjour *bsrv, *bn;
+       struct p2p_srv_upnp *usrv, *un;
+
+       dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
+                             struct p2p_srv_bonjour, list)
+               wpas_p2p_srv_bonjour_free(bsrv);
+
+       dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
+                             struct p2p_srv_upnp, list)
+               wpas_p2p_srv_upnp_free(usrv);
+
+       wpas_p2p_sd_service_update(wpa_s);
+}
+
+
+int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
+                                struct wpabuf *query, struct wpabuf *resp)
+{
+       struct p2p_srv_bonjour *bsrv;
+
+       bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
+       if (bsrv) {
+               wpabuf_free(query);
+               wpabuf_free(bsrv->resp);
+               bsrv->resp = resp;
+               return 0;
+       }
+
+       bsrv = os_zalloc(sizeof(*bsrv));
+       if (bsrv == NULL)
+               return -1;
+       bsrv->query = query;
+       bsrv->resp = resp;
+       dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
+
+       wpas_p2p_sd_service_update(wpa_s);
+       return 0;
+}
+
+
+int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
+                                const struct wpabuf *query)
+{
+       struct p2p_srv_bonjour *bsrv;
+
+       bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
+       if (bsrv == NULL)
+               return -1;
+       wpas_p2p_srv_bonjour_free(bsrv);
+       wpas_p2p_sd_service_update(wpa_s);
+       return 0;
+}
+
+
+int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
+                             const char *service)
+{
+       struct p2p_srv_upnp *usrv;
+
+       if (wpas_p2p_service_get_upnp(wpa_s, version, service))
+               return 0; /* Already listed */
+       usrv = os_zalloc(sizeof(*usrv));
+       if (usrv == NULL)
+               return -1;
+       usrv->version = version;
+       usrv->service = os_strdup(service);
+       if (usrv->service == NULL) {
+               os_free(usrv);
+               return -1;
+       }
+       dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
+
+       wpas_p2p_sd_service_update(wpa_s);
+       return 0;
+}
+
+
+int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
+                             const char *service)
+{
+       struct p2p_srv_upnp *usrv;
+
+       usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
+       if (usrv == NULL)
+               return -1;
+       wpas_p2p_srv_upnp_free(usrv);
+       wpas_p2p_sd_service_update(wpa_s);
+       return 0;
+}
+
+
+static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s,
+                                        const u8 *peer, const char *params)
+{
+       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_SHOW_PIN MACSTR " %08d%s",
+               MAC2STR(peer), wps_generate_pin(), params);
+}
+
+
+static void wpas_prov_disc_local_keypad(struct wpa_supplicant *wpa_s,
+                                       const u8 *peer, const char *params)
+{
+       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR "%s",
+               MAC2STR(peer), params);
+}
+
+
+void wpas_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)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       char devtype[WPS_DEV_TYPE_BUFSIZE];
+       char params[200];
+       u8 empty_dev_type[8];
+
+       if (pri_dev_type == NULL) {
+               os_memset(empty_dev_type, 0, sizeof(empty_dev_type));
+               pri_dev_type = empty_dev_type;
+       }
+       os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR
+                   " pri_dev_type=%s name='%s' config_methods=0x%x "
+                   "dev_capab=0x%x group_capab=0x%x",
+                   MAC2STR(dev_addr),
+                   wps_dev_type_bin2str(pri_dev_type, devtype,
+                                        sizeof(devtype)),
+                   dev_name, supp_config_methods, dev_capab, group_capab);
+       params[sizeof(params) - 1] = '\0';
+
+       if (config_methods & WPS_CONFIG_DISPLAY)
+               wpas_prov_disc_local_display(wpa_s, peer, params);
+       else if (config_methods & WPS_CONFIG_KEYPAD)
+               wpas_prov_disc_local_keypad(wpa_s, peer, params);
+       else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_REQ MACSTR
+                       "%s", MAC2STR(peer), params);
+}
+
+
+void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       if (config_methods & WPS_CONFIG_DISPLAY)
+               wpas_prov_disc_local_keypad(wpa_s, peer, "");
+       else if (config_methods & WPS_CONFIG_KEYPAD)
+               wpas_prov_disc_local_display(wpa_s, peer, "");
+       else if (config_methods & WPS_CONFIG_PUSHBUTTON)
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR,
+                       MAC2STR(peer));
+}
+
+
+static u8 wpas_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)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_ssid *s;
+       u8 cur_bssid[ETH_ALEN];
+       int res;
+
+       if (!persistent_group) {
+               wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
+                          " to join an active group", MAC2STR(sa));
+               /*
+                * Do not accept the invitation automatically; notify user and
+                * request approval.
+                */
+               return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+       }
+
+       if (!wpa_s->conf->persistent_reconnect)
+               return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+
+       for (s = wpa_s->conf->ssid; s; s = s->next) {
+               if (s->disabled == 2 &&
+                   os_memcmp(s->bssid, go_dev_addr, ETH_ALEN) == 0 &&
+                   s->ssid_len == ssid_len &&
+                   os_memcmp(ssid, s->ssid, ssid_len) == 0)
+                       break;
+       }
+
+       if (!s) {
+               wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
+                          " requested reinvocation of an unknown group",
+                          MAC2STR(sa));
+               return P2P_SC_FAIL_UNKNOWN_GROUP;
+       }
+
+       if (s->mode == WPAS_MODE_P2P_GO && !wpas_p2p_create_iface(wpa_s)) {
+               *go = 1;
+               if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+                       wpa_printf(MSG_DEBUG, "P2P: The only available "
+                                  "interface is already in use - reject "
+                                  "invitation");
+                       return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+               }
+               os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN);
+       } else if (s->mode == WPAS_MODE_P2P_GO) {
+               *go = 1;
+               if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0)
+               {
+                       wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
+                                  "interface address for the group");
+                       return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+               }
+               os_memcpy(group_bssid, wpa_s->pending_interface_addr,
+                         ETH_ALEN);
+       }
+
+       if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, cur_bssid) == 0 &&
+           wpa_s->assoc_freq) {
+               wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
+                          "the channel we are already using");
+               *force_freq = wpa_s->assoc_freq;
+       }
+
+       res = wpa_drv_shared_freq(wpa_s);
+       if (res > 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
+                          "with the channel we are already using on a "
+                          "shared interface");
+               *force_freq = res;
+       }
+
+       return P2P_SC_SUCCESS;
+}
+
+
+static void wpas_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)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_ssid *s;
+
+       for (s = wpa_s->conf->ssid; s; s = s->next) {
+               if (s->disabled == 2 &&
+                   s->ssid_len == ssid_len &&
+                   os_memcmp(ssid, s->ssid, ssid_len) == 0)
+                       break;
+       }
+
+       if (status == P2P_SC_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
+                          " was accepted; op_freq=%d MHz",
+                          MAC2STR(sa), op_freq);
+               if (s) {
+                       wpas_p2p_group_add_persistent(
+                               wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0);
+               }
+               return;
+       }
+
+       if (status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+               wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
+                          " was rejected (status %u)", MAC2STR(sa), status);
+               return;
+       }
+
+       if (!s) {
+               if (bssid) {
+                       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
+                               "sa=" MACSTR " go_dev_addr=" MACSTR
+                               " bssid=" MACSTR " unknown-network",
+                               MAC2STR(sa), MAC2STR(go_dev_addr),
+                               MAC2STR(bssid));
+               } else {
+                       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
+                               "sa=" MACSTR " go_dev_addr=" MACSTR
+                               " unknown-network",
+                               MAC2STR(sa), MAC2STR(go_dev_addr));
+               }
+               return;
+       }
+
+       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED "sa=" MACSTR
+               " persistent=%d", MAC2STR(sa), s->id);
+}
+
+
+static void wpas_invitation_result(void *ctx, int status, const u8 *bssid)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_ssid *ssid;
+
+       if (bssid) {
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
+                       "status=%d " MACSTR,
+                       status, MAC2STR(bssid));
+       } else {
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
+                       "status=%d ", status);
+       }
+
+       if (status != P2P_SC_SUCCESS) {
+               wpas_p2p_remove_pending_group_interface(wpa_s);
+               return;
+       }
+
+       ssid = wpa_config_get_network(wpa_s->conf,
+                                     wpa_s->pending_invite_ssid_id);
+       if (ssid == NULL) {
+               wpa_printf(MSG_ERROR, "P2P: Could not find persistent group "
+                          "data matching with invitation");
+               return;
+       }
+
+       wpas_p2p_group_add_persistent(wpa_s, ssid,
+                                     ssid->mode == WPAS_MODE_P2P_GO, 0);
+}
+
+
+static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
+                                  struct p2p_config *p2p)
+{
+       struct hostapd_hw_modes *modes;
+       u16 num_modes, flags;
+       int i, cla;
+       int band24 = 0, band5_low = 0, band5_high = 0;
+
+       /* TODO: more detailed selection of channels within reg_class based on
+        * driver capabilities */
+
+       modes = wpa_drv_get_hw_feature_data(wpa_s, &num_modes, &flags);
+       if (modes == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching "
+                          "of all supported channels; assume dualband "
+                          "support");
+               band24 = band5_low = band5_high = 1;
+       } else {
+               for (i = 0; i < num_modes; i++) {
+                       struct hostapd_hw_modes *mode;
+                       mode = &modes[i];
+                       if (mode->mode == HOSTAPD_MODE_IEEE80211G) {
+                               band24 = 1;
+                       } else if (mode->mode == HOSTAPD_MODE_IEEE80211A) {
+                               int j;
+                               for (j = 0; j < mode->num_channels; j++) {
+                                       struct hostapd_channel_data *ch;
+                                       ch = &mode->channels[j];
+                                       if (ch->chan == 36)
+                                               band5_low = 1;
+                                       else if (ch->chan == 157)
+                                               band5_high = 1;
+                               }
+                       }
+               }
+       }
+
+       cla = 0;
+
+       if (band24) {
+               wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for "
+                          "2.4 GHz band");
+
+               /* Operating class 81 - 2.4 GHz band channels 1..13 */
+               p2p->channels.reg_class[cla].reg_class = 81;
+               p2p->channels.reg_class[cla].channels = 13;
+               for (i = 0; i < 13; i++)
+                       p2p->channels.reg_class[cla].channel[i] = i + 1;
+               cla++;
+
+               /* Operating class 82 - 2.4 GHz band channel 14 */
+               p2p->channels.reg_class[cla].reg_class = 82;
+               p2p->channels.reg_class[cla].channels = 1;
+               p2p->channels.reg_class[cla].channel[0] = 14;
+               cla++;
+
+#if 0
+               /* Operating class 83 - 2.4 GHz band channels 1..9; 40 MHz */
+               p2p->channels.reg_class[cla].reg_class = 83;
+               p2p->channels.reg_class[cla].channels = 9;
+               for (i = 0; i < 9; i++)
+                       p2p->channels.reg_class[cla].channel[i] = i + 1;
+               cla++;
+
+               /* Operating class 84 - 2.4 GHz band channels 5..13; 40 MHz */
+               p2p->channels.reg_class[cla].reg_class = 84;
+               p2p->channels.reg_class[cla].channels = 9;
+               for (i = 0; i < 9; i++)
+                       p2p->channels.reg_class[cla].channel[i] = i + 5;
+               cla++;
+#endif
+       }
+
+       if (band5_low) {
+               wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for "
+                          "lower 5 GHz band");
+
+               /* Operating class 115 - 5 GHz, channels 36-48 */
+               p2p->channels.reg_class[cla].reg_class = 115;
+               p2p->channels.reg_class[cla].channels = 4;
+               p2p->channels.reg_class[cla].channel[0] = 36;
+               p2p->channels.reg_class[cla].channel[1] = 40;
+               p2p->channels.reg_class[cla].channel[2] = 44;
+               p2p->channels.reg_class[cla].channel[3] = 48;
+               cla++;
+
+#if 0
+               /* Operating class 116 - 5 GHz, channels 36,44; 40 MHz */
+               p2p->channels.reg_class[cla].reg_class = 116;
+               p2p->channels.reg_class[cla].channels = 2;
+               p2p->channels.reg_class[cla].channel[0] = 36;
+               p2p->channels.reg_class[cla].channel[1] = 44;
+               cla++;
+
+               /* Operating class 117 - 5 GHz, channels 40,48; 40 MHz */
+               p2p->channels.reg_class[cla].reg_class = 117;
+               p2p->channels.reg_class[cla].channels = 2;
+               p2p->channels.reg_class[cla].channel[0] = 40;
+               p2p->channels.reg_class[cla].channel[1] = 48;
+               cla++;
+#endif
+       }
+
+       if (band5_high) {
+               wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for "
+                          "higher 5 GHz band");
+
+               /* Operating class 124 - 5 GHz, channels 149,153,157,161 */
+               p2p->channels.reg_class[cla].reg_class = 124;
+               p2p->channels.reg_class[cla].channels = 4;
+               p2p->channels.reg_class[cla].channel[0] = 149;
+               p2p->channels.reg_class[cla].channel[1] = 153;
+               p2p->channels.reg_class[cla].channel[2] = 157;
+               p2p->channels.reg_class[cla].channel[3] = 161;
+               cla++;
+
+#if 0
+               /* Operating class 126 - 5 GHz, channels 149,157; 40 MHz */
+               p2p->channels.reg_class[cla].reg_class = 126;
+               p2p->channels.reg_class[cla].channels = 2;
+               p2p->channels.reg_class[cla].channel[0] = 149;
+               p2p->channels.reg_class[cla].channel[1] = 157;
+               cla++;
+
+               /* Operating class 127 - 5 GHz, channels 153,161; 40 MHz */
+               p2p->channels.reg_class[cla].reg_class = 127;
+               p2p->channels.reg_class[cla].channels = 2;
+               p2p->channels.reg_class[cla].channel[0] = 153;
+               p2p->channels.reg_class[cla].channel[1] = 161;
+               cla++;
+#endif
+       }
+
+       p2p->channels.reg_classes = cla;
+
+       if (modes)
+               ieee80211_sta_free_hw_features(modes, num_modes);
+
+       return 0;
+}
+
+
+static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
+                       size_t buf_len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               if (os_memcmp(wpa_s->own_addr, interface_addr, ETH_ALEN) == 0)
+                       break;
+       }
+       if (wpa_s == NULL)
+               return -1;
+
+       return wpa_drv_get_noa(wpa_s, buf, buf_len);
+}
+
+
+/**
+ * wpas_p2p_init - Initialize P2P module for %wpa_supplicant
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * Returns: 0 on success, -1 on failure
+ */
+int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
+{
+       struct p2p_config p2p;
+       unsigned int r;
+       int i;
+
+       if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
+               return 0;
+
+#ifdef CONFIG_CLIENT_MLME
+       if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)) {
+               wpa_s->mlme.public_action_cb = p2p_rx_action_mlme;
+               wpa_s->mlme.public_action_cb_ctx = wpa_s;
+       }
+#endif /* CONFIG_CLIENT_MLME */
+
+       if (wpa_drv_disable_11b_rates(wpa_s, 1) < 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Failed to disable 11b rates");
+               /* Continue anyway; this is not really a fatal error */
+       }
+
+       if (global->p2p)
+               return 0;
+
+       os_memset(&p2p, 0, sizeof(p2p));
+       p2p.msg_ctx = wpa_s;
+       p2p.cb_ctx = wpa_s;
+       p2p.p2p_scan = wpas_p2p_scan;
+       p2p.send_action = wpas_send_action;
+       p2p.send_action_done = wpas_send_action_done;
+       p2p.go_neg_completed = wpas_go_neg_completed;
+       p2p.go_neg_req_rx = wpas_go_neg_req_rx;
+       p2p.dev_found = wpas_dev_found;
+       p2p.start_listen = wpas_start_listen;
+       p2p.stop_listen = wpas_stop_listen;
+       p2p.send_probe_resp = wpas_send_probe_resp;
+       p2p.sd_request = wpas_sd_request;
+       p2p.sd_response = wpas_sd_response;
+       p2p.prov_disc_req = wpas_prov_disc_req;
+       p2p.prov_disc_resp = wpas_prov_disc_resp;
+       p2p.invitation_process = wpas_invitation_process;
+       p2p.invitation_received = wpas_invitation_received;
+       p2p.invitation_result = wpas_invitation_result;
+       p2p.get_noa = wpas_get_noa;
+
+       os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
+       os_memcpy(p2p.dev_addr, wpa_s->own_addr, ETH_ALEN);
+       p2p.dev_name = wpa_s->conf->device_name;
+
+       if (wpa_s->conf->p2p_listen_reg_class &&
+           wpa_s->conf->p2p_listen_channel) {
+               p2p.reg_class = wpa_s->conf->p2p_listen_reg_class;
+               p2p.channel = wpa_s->conf->p2p_listen_channel;
+       } else {
+               p2p.reg_class = 81;
+               /*
+                * Pick one of the social channels randomly as the listen
+                * channel.
+                */
+               os_get_random((u8 *) &r, sizeof(r));
+               p2p.channel = 1 + (r % 3) * 5;
+       }
+
+       if (wpa_s->conf->p2p_oper_reg_class &&
+           wpa_s->conf->p2p_oper_channel) {
+               p2p.op_reg_class = wpa_s->conf->p2p_oper_reg_class;
+               p2p.op_channel = wpa_s->conf->p2p_oper_channel;
+       } else {
+               p2p.op_reg_class = 81;
+               /*
+                * For initial tests, pick the operation channel randomly.
+                * TODO: Use scan results (etc.) to select the best channel.
+                */
+               p2p.op_channel = 1 + r % 11;
+       }
+       wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d  "
+                  "Own preferred operation channel: %d",
+                  p2p.channel, p2p.op_channel);
+       if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+               os_memcpy(p2p.country, wpa_s->conf->country, 2);
+               p2p.country[2] = 0x04;
+       } else
+               os_memcpy(p2p.country, "US\x04", 3);
+
+       if (wpas_p2p_setup_channels(wpa_s, &p2p)) {
+               wpa_printf(MSG_ERROR, "P2P: Failed to configure supported "
+                          "channel list");
+               return -1;
+       }
+
+       if (wpa_s->conf->device_type &&
+           wps_dev_type_str2bin(wpa_s->conf->device_type, p2p.pri_dev_type) <
+           0) {
+               wpa_printf(MSG_ERROR, "P2P: Invalid device_type");
+               return -1;
+       }
+
+       for (i = 0; i < MAX_SEC_DEVICE_TYPES; i++) {
+               if (wpa_s->conf->sec_device_type[i] == NULL)
+                       continue;
+               if (wps_dev_type_str2bin(
+                           wpa_s->conf->sec_device_type[i],
+                           p2p.sec_dev_type[p2p.num_sec_dev_types]) < 0) {
+                       wpa_printf(MSG_ERROR, "P2P: Invalid sec_device_type");
+                       return -1;
+               }
+               p2p.num_sec_dev_types++;
+               if (p2p.num_sec_dev_types == P2P_SEC_DEVICE_TYPES)
+                       break;
+       }
+
+       p2p.concurrent_operations = !!(wpa_s->drv_flags &
+                                      WPA_DRIVER_FLAGS_P2P_CONCURRENT);
+
+       p2p.max_peers = 100;
+
+       if (wpa_s->conf->p2p_ssid_postfix) {
+               p2p.ssid_postfix_len =
+                       os_strlen(wpa_s->conf->p2p_ssid_postfix);
+               if (p2p.ssid_postfix_len > sizeof(p2p.ssid_postfix))
+                       p2p.ssid_postfix_len = sizeof(p2p.ssid_postfix);
+               os_memcpy(p2p.ssid_postfix, wpa_s->conf->p2p_ssid_postfix,
+                         p2p.ssid_postfix_len);
+       }
+
+       global->p2p = p2p_init(&p2p);
+       if (global->p2p == NULL)
+               return -1;
+
+       return 0;
+}
+
+
+/**
+ * wpas_p2p_deinit - Deinitialize per-interface P2P data
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ *
+ * This function deinitialize per-interface P2P data.
+ */
+void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver && wpa_s->drv_priv)
+               wpa_drv_probe_req_report(wpa_s, 0);
+       os_free(wpa_s->go_params);
+       wpa_s->go_params = NULL;
+       wpabuf_free(wpa_s->pending_action_tx);
+       wpa_s->pending_action_tx = NULL;
+       eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
+       eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+       wpa_s->p2p_long_listen = 0;
+       eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+       wpas_p2p_remove_pending_group_interface(wpa_s);
+
+       /* TODO: remove group interface from the driver if this wpa_s instance
+        * is on top of a P2P group interface */
+}
+
+
+/**
+ * wpas_p2p_deinit_global - Deinitialize global P2P module
+ * @global: Pointer to global data from wpa_supplicant_init()
+ *
+ * This function deinitializes the global (per device) P2P module.
+ */
+void wpas_p2p_deinit_global(struct wpa_global *global)
+{
+       struct wpa_supplicant *wpa_s, *tmp;
+       char *ifname;
+
+       if (global->p2p == NULL)
+               return;
+
+       /* Remove remaining P2P group interfaces */
+       wpa_s = global->ifaces;
+       while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
+               wpa_s = wpa_s->next;
+       while (wpa_s) {
+               enum wpa_driver_if_type type;
+               tmp = global->ifaces;
+               while (tmp &&
+                      (tmp == wpa_s ||
+                       tmp->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) {
+                       tmp = tmp->next;
+               }
+               if (tmp == NULL)
+                       break;
+               ifname = os_strdup(tmp->ifname);
+               type = wpas_p2p_if_type(tmp->p2p_group_interface);
+               wpa_supplicant_remove_iface(global, tmp);
+               if (ifname)
+                       wpa_drv_if_remove(wpa_s, type, ifname);
+               os_free(ifname);
+       }
+
+       p2p_deinit(global->p2p);
+       global->p2p = NULL;
+}
+
+
+static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)
+               return 1; /* P2P group requires a new interface in every case
+                          */
+       if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT))
+               return 0; /* driver does not support concurrent operations */
+       if (wpa_s->global->ifaces->next)
+               return 1; /* more that one interface already in use */
+       if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+               return 1; /* this interface is already in use */
+       return 0;
+}
+
+
+static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
+                                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)
+{
+       return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method,
+                          go_intent, own_interface_addr, force_freq,
+                          persistent_group);
+}
+
+
+static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s,
+                               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)
+{
+       return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
+                            go_intent, own_interface_addr, force_freq,
+                            persistent_group);
+}
+
+
+static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
+                        const u8 *dev_addr, enum p2p_wps_method wps_method)
+{
+       struct wpa_bss *bss;
+
+       wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
+                  MACSTR " dev " MACSTR ")",
+                  MAC2STR(iface_addr), MAC2STR(dev_addr));
+
+       os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN);
+       os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN);
+       wpa_s->pending_join_wps_method = wps_method;
+
+       /* Make sure we are not running find during connection establishment */
+       wpas_p2p_stop_find(wpa_s);
+
+       bss = wpa_bss_get_bssid(wpa_s, iface_addr);
+       if (bss) {
+               u16 method;
+
+               wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request "
+                          "prior to joining an existing group (GO " MACSTR
+                          " freq=%u MHz)",
+                          MAC2STR(dev_addr), bss->freq);
+               wpa_s->pending_pd_before_join = 1;
+
+               switch (wps_method) {
+               case WPS_PIN_LABEL:
+               case WPS_PIN_DISPLAY:
+                       method = WPS_CONFIG_KEYPAD;
+                       break;
+               case WPS_PIN_KEYPAD:
+                       method = WPS_CONFIG_DISPLAY;
+                       break;
+               case WPS_PBC:
+                       method = WPS_CONFIG_PUSHBUTTON;
+                       break;
+               default:
+                       method = 0;
+                       break;
+               }
+
+               if (p2p_prov_disc_req(wpa_s->global->p2p, dev_addr, method, 1)
+                   < 0) {
+                       wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
+                                  "Discovery Request before joining an "
+                                  "existing group");
+                       wpa_s->pending_pd_before_join = 0;
+                       goto start;
+               }
+
+               /*
+                * Actual join operation will be started from the Action frame
+                * TX status callback.
+                */
+               return 0;
+       }
+
+       wpa_printf(MSG_DEBUG, "P2P: Target BSS/GO not yet in BSS table - "
+                  "cannot send Provision Discovery Request");
+
+start:
+       /* Start join operation immediately */
+       return wpas_p2p_join_start(wpa_s);
+}
+
+
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_supplicant *group;
+       struct p2p_go_neg_results res;
+
+       group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
+       if (group == NULL)
+               return -1;
+       if (group != wpa_s)
+               os_memcpy(group->p2p_pin, wpa_s->p2p_pin,
+                         sizeof(group->p2p_pin));
+
+       group->p2p_in_provisioning = 1;
+
+       os_memset(&res, 0, sizeof(res));
+       os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
+                 ETH_ALEN);
+       res.wps_method = wpa_s->pending_join_wps_method;
+       wpas_start_wps_enrollee(group, &res);
+
+       return 0;
+}
+
+
+/**
+ * wpas_p2p_connect - Request P2P Group Formation to be started
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @peer_addr: Address of the peer P2P Device
+ * @pin: PIN to use during provisioning or %NULL to indicate PBC mode
+ * @persistent_group: Whether to create a persistent group
+ * @join: Whether to join an existing group (as a client) instead of starting
+ *     Group Owner negotiation; @peer_addr is BSSID in that case
+ * @auth: Whether to only authorize the connection instead of doing that and
+ *     initiating Group Owner negotiation
+ * @go_intent: GO Intent or -1 to use default
+ * @freq: Frequency for the group or 0 for auto-selection
+ * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on failure
+ */
+int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+                    const char *pin, enum p2p_wps_method wps_method,
+                    int persistent_group, int join, int auth, int go_intent,
+                    int freq)
+{
+       int force_freq = 0;
+       u8 bssid[ETH_ALEN];
+       int ret = 0;
+       enum wpa_driver_if_type iftype;
+
+       if (go_intent < 0)
+               go_intent = wpa_s->conf->p2p_go_intent;
+
+       if (!auth)
+               wpa_s->p2p_long_listen = 0;
+
+       if (pin)
+               os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
+       else if (wps_method == WPS_PIN_DISPLAY) {
+               ret = wps_generate_pin();
+               os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), "%08d",
+                           ret);
+               wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s",
+                          wpa_s->p2p_pin);
+       } else
+               wpa_s->p2p_pin[0] = '\0';
+
+       if (join) {
+               u8 iface_addr[ETH_ALEN];
+               if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr,
+                                          iface_addr) < 0)
+                       os_memcpy(iface_addr, peer_addr, ETH_ALEN);
+               if (wpas_p2p_join(wpa_s, iface_addr, peer_addr, wps_method) <
+                   0)
+                       return -1;
+               return ret;
+       }
+
+       if (freq > 0)
+               force_freq = freq;
+       else if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+                wpa_s->assoc_freq)
+               force_freq = wpa_s->assoc_freq;
+       else {
+               force_freq = wpa_drv_shared_freq(wpa_s);
+               if (force_freq < 0)
+                       force_freq = 0;
+       }
+
+       if (force_freq > 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+                          "channel we are already using (%u MHz) on another "
+                          "interface", force_freq);
+       }
+
+       wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
+
+       if (!wpa_s->create_p2p_iface) {
+               if (auth) {
+                       if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
+                                                go_intent, wpa_s->own_addr,
+                                                force_freq, persistent_group)
+                           < 0)
+                               return -1;
+                       return ret;
+               }
+               if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method,
+                                         go_intent, wpa_s->own_addr,
+                                         force_freq, persistent_group) < 0)
+                       return -1;
+               return ret;
+       }
+
+       /* Prepare to add a new interface for the group */
+       iftype = WPA_IF_P2P_GROUP;
+       if (join)
+               iftype = WPA_IF_P2P_CLIENT;
+       else if (go_intent == 15)
+               iftype = WPA_IF_P2P_GO;
+       if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) {
+               wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
+                          "interface for the group");
+               return -1;
+       }
+
+       if (auth) {
+               if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
+                                        go_intent,
+                                        wpa_s->pending_interface_addr,
+                                        force_freq, persistent_group) < 0)
+                       return -1;
+               return ret;
+       }
+       if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method, go_intent,
+                                 wpa_s->pending_interface_addr,
+                                 force_freq, persistent_group) < 0) {
+               wpas_p2p_remove_pending_group_interface(wpa_s);
+               return -1;
+       }
+       return ret;
+}
+
+
+/**
+ * wpas_p2p_remain_on_channel_cb - Indication of remain-on-channel start
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @freq: Frequency of the channel in MHz
+ * @duration: Duration of the stay on the channel in milliseconds
+ *
+ * This callback is called when the driver indicates that it has started the
+ * requested remain-on-channel duration.
+ */
+void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+                                  unsigned int freq, unsigned int duration)
+{
+       wpa_s->off_channel_freq = freq;
+       wpas_send_action_cb(wpa_s, NULL);
+       if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
+               p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq,
+                             wpa_s->pending_listen_duration);
+               wpa_s->pending_listen_freq = 0;
+       }
+}
+
+
+static int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s,
+                                unsigned int timeout)
+{
+       /* Limit maximum Listen state time based on driver limitation. */
+       if (timeout > wpa_s->max_remain_on_chan)
+               timeout = wpa_s->max_remain_on_chan;
+
+       return p2p_listen(wpa_s->global->p2p, timeout);
+}
+
+
+/**
+ * wpas_p2p_cancel_remain_on_channel_cb - Remain-on-channel timeout
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @freq: Frequency of the channel in MHz
+ *
+ * This callback is called when the driver indicates that a remain-on-channel
+ * operation has been completed, i.e., the duration on the requested channel
+ * has timed out.
+ */
+void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+                                         unsigned int freq)
+{
+       wpa_s->off_channel_freq = 0;
+       if (p2p_listen_end(wpa_s->global->p2p, freq) > 0)
+               return; /* P2P module started a new operation */
+       if (wpa_s->pending_action_tx)
+               return;
+       if (wpa_s->p2p_long_listen > 0)
+               wpa_s->p2p_long_listen -= 5;
+       if (wpa_s->p2p_long_listen > 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
+               wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen * 1000);
+       }
+}
+
+
+/**
+ * wpas_p2p_group_remove - Remove a P2P group
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @ifname: Network interface name of the group interface or "*" to remove all
+ *     groups
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to remove a P2P group. This can be used to disconnect
+ * from a group in which the local end is a P2P Client or to end a P2P Group in
+ * case the local end is the Group Owner. If a virtual network interface was
+ * created for this group, that interface will be removed. Otherwise, only the
+ * configured P2P group network will be removed from the interface.
+ */
+int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
+{
+       struct wpa_global *global = wpa_s->global;
+
+       if (os_strcmp(ifname, "*") == 0) {
+               struct wpa_supplicant *prev;
+               wpa_s = global->ifaces;
+               while (wpa_s) {
+                       prev = wpa_s;
+                       wpa_s = wpa_s->next;
+                       wpas_p2p_group_delete(prev);
+               }
+               return 0;
+       }
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               if (os_strcmp(wpa_s->ifname, ifname) == 0)
+                       break;
+       }
+
+       if (wpa_s == NULL)
+               return -1;
+
+       wpas_p2p_group_delete(wpa_s);
+
+       return 0;
+}
+
+
+static void wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
+                                   struct p2p_go_neg_results *params,
+                                   int freq)
+{
+       u8 bssid[ETH_ALEN];
+       int res;
+
+       os_memset(params, 0, sizeof(*params));
+       params->role_go = 1;
+       params->freq = 2412;
+       if (freq)
+               params->freq = freq;
+       else if (wpa_s->conf->p2p_oper_reg_class == 81 &&
+                wpa_s->conf->p2p_oper_channel >= 1 &&
+                wpa_s->conf->p2p_oper_channel <= 11)
+               params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
+       else if (wpa_s->conf->p2p_oper_reg_class == 115 ||
+                wpa_s->conf->p2p_oper_reg_class == 118)
+               params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
+       if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+           wpa_s->assoc_freq && !freq) {
+               wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are "
+                          "already using");
+               params->freq = wpa_s->assoc_freq;
+       }
+
+       res = wpa_drv_shared_freq(wpa_s);
+       if (res > 0 && !freq) {
+               wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are "
+                          "already using on a shared interface");
+               params->freq = res;
+       }
+}
+
+
+static struct wpa_supplicant *
+wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
+                        int go)
+{
+       struct wpa_supplicant *group_wpa_s;
+
+       if (!wpas_p2p_create_iface(wpa_s))
+               return wpa_s;
+
+       if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
+                                        WPA_IF_P2P_CLIENT) < 0)
+               return NULL;
+       group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go);
+       if (group_wpa_s == NULL) {
+               wpas_p2p_remove_pending_group_interface(wpa_s);
+               return NULL;
+       }
+
+       return group_wpa_s;
+}
+
+
+/**
+ * wpas_p2p_group_add - Add a new P2P group with local end as Group Owner
+ * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
+ * @persistent_group: Whether to create a persistent group
+ * @freq: Frequency for the group or 0 to indicate no hardcoding
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function creates a new P2P group with the local end as the Group Owner,
+ * i.e., without using Group Owner Negotiation.
+ */
+int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
+                      int freq)
+{
+       struct p2p_go_neg_results params;
+
+       wpas_p2p_init_go_params(wpa_s, &params, freq);
+       p2p_go_params(wpa_s->global->p2p, &params);
+       params.persistent_group = persistent_group;
+
+       wpa_s = wpas_p2p_get_group_iface(wpa_s, 0, 1);
+       if (wpa_s == NULL)
+               return -1;
+       wpas_start_wps_go(wpa_s, &params, 0);
+
+       return 0;
+}
+
+
+static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
+                                struct wpa_ssid *params, int addr_allocated)
+{
+       struct wpa_ssid *ssid;
+
+       wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
+       if (wpa_s == NULL)
+               return -1;
+
+       wpa_supplicant_ap_deinit(wpa_s);
+
+       ssid = wpa_config_add_network(wpa_s->conf);
+       if (ssid == NULL)
+               return -1;
+       wpas_notify_network_added(wpa_s, ssid);
+       wpa_config_set_network_defaults(ssid);
+       ssid->temporary = 1;
+       ssid->proto = WPA_PROTO_RSN;
+       ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+       ssid->group_cipher = WPA_CIPHER_CCMP;
+       ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+       ssid->ssid = os_malloc(params->ssid_len);
+       if (ssid->ssid == NULL) {
+               wpas_notify_network_removed(wpa_s, ssid);
+               wpa_config_remove_network(wpa_s->conf, ssid->id);
+               return -1;
+       }
+       os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
+       ssid->ssid_len = params->ssid_len;
+       ssid->p2p_group = 1;
+       if (params->psk_set) {
+               os_memcpy(ssid->psk, params->psk, 32);
+               ssid->psk_set = 1;
+       }
+       if (params->passphrase)
+               ssid->passphrase = os_strdup(params->passphrase);
+
+       wpa_supplicant_select_network(wpa_s, ssid);
+
+       wpa_s->show_group_started = 1;
+
+       return 0;
+}
+
+
+int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
+                                 struct wpa_ssid *ssid, int addr_allocated,
+                                 int freq)
+{
+       struct p2p_go_neg_results params;
+
+       if (ssid->disabled != 2 || ssid->ssid == NULL)
+               return -1;
+
+       wpa_s->p2p_long_listen = 0;
+       eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+
+       if (ssid->mode == WPAS_MODE_INFRA)
+               return wpas_start_p2p_client(wpa_s, ssid, addr_allocated);
+
+       if (ssid->mode != WPAS_MODE_P2P_GO)
+               return -1;
+
+       wpas_p2p_init_go_params(wpa_s, &params, freq);
+
+       params.role_go = 1;
+       if (ssid->passphrase == NULL ||
+           os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
+               wpa_printf(MSG_DEBUG, "P2P: Invalid passphrase in persistent "
+                          "group");
+               return -1;
+       }
+       os_strlcpy(params.passphrase, ssid->passphrase,
+                  sizeof(params.passphrase));
+       os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len);
+       params.ssid_len = ssid->ssid_len;
+       params.persistent_group = 1;
+
+       wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1);
+       if (wpa_s == NULL)
+               return -1;
+
+       wpas_start_wps_go(wpa_s, &params, 0);
+
+       return 0;
+}
+
+
+static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies,
+                              struct wpabuf *proberesp_ies)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       if (wpa_s->ap_iface) {
+               struct hostapd_data *hapd = wpa_s->ap_iface->bss[0];
+               if (beacon_ies) {
+                       wpabuf_free(hapd->p2p_beacon_ie);
+                       hapd->p2p_beacon_ie = beacon_ies;
+               }
+               wpabuf_free(hapd->p2p_probe_resp_ie);
+               hapd->p2p_probe_resp_ie = proberesp_ies;
+       } else {
+               wpabuf_free(beacon_ies);
+               wpabuf_free(proberesp_ies);
+       }
+       wpa_supplicant_ap_update_beacon(wpa_s);
+}
+
+
+struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
+                                      int persistent_group,
+                                      int group_formation)
+{
+       struct p2p_group *group;
+       struct p2p_group_config *cfg;
+
+       cfg = os_zalloc(sizeof(*cfg));
+       if (cfg == NULL)
+               return NULL;
+
+       cfg->persistent_group = persistent_group;
+       os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN);
+       cfg->cb_ctx = wpa_s;
+       cfg->ie_update = wpas_p2p_ie_update;
+
+       group = p2p_group_init(wpa_s->global->p2p, cfg);
+       if (group == NULL)
+               os_free(cfg);
+       if (!group_formation)
+               p2p_group_notif_formation_done(group);
+       wpa_s->p2p_group = group;
+       return group;
+}
+
+
+void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+                         int registrar)
+{
+       if (!wpa_s->p2p_in_provisioning) {
+               wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P "
+                          "provisioning not in progress");
+               return;
+       }
+
+       eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
+                            NULL);
+       if (wpa_s->global->p2p)
+               p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
+       wpas_group_formation_completed(wpa_s, 1);
+}
+
+
+int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+                      const char *config_method)
+{
+       u16 config_methods;
+
+       if (os_strcmp(config_method, "display") == 0)
+               config_methods = WPS_CONFIG_DISPLAY;
+       else if (os_strcmp(config_method, "keypad") == 0)
+               config_methods = WPS_CONFIG_KEYPAD;
+       else if (os_strcmp(config_method, "pbc") == 0 ||
+                os_strcmp(config_method, "pushbutton") == 0)
+               config_methods = WPS_CONFIG_PUSHBUTTON;
+       else
+               return -1;
+
+       if (wpa_s->global->p2p == NULL)
+               return -1;
+
+       return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr,
+                                config_methods, 0);
+}
+
+
+int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+                             char *end)
+{
+       return p2p_scan_result_text(ies, ies_len, buf, end);
+}
+
+
+int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
+                 enum p2p_discovery_type type)
+{
+       wpa_s->p2p_long_listen = 0;
+
+       return p2p_find(wpa_s->global->p2p, timeout, type);
+}
+
+
+void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
+{
+       wpa_s->p2p_long_listen = 0;
+
+       p2p_stop_find(wpa_s->global->p2p);
+
+       wpas_p2p_remove_pending_group_interface(wpa_s);
+}
+
+
+static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       wpa_s->p2p_long_listen = 0;
+}
+
+
+int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout)
+{
+       int res;
+
+       if (timeout == 0) {
+               /*
+                * This is a request for unlimited Listen state. However, at
+                * least for now, this is mapped to a Listen state for one
+                * hour.
+                */
+               timeout = 3600;
+       }
+       eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
+       wpa_s->p2p_long_listen = 0;
+
+       res = wpas_p2p_listen_start(wpa_s, timeout * 1000);
+       if (res == 0 && timeout * 1000 > wpa_s->max_remain_on_chan) {
+               wpa_s->p2p_long_listen = timeout;
+               eloop_register_timeout(timeout, 0,
+                                      wpas_p2p_long_listen_timeout,
+                                      wpa_s, NULL);
+       }
+
+       return res;
+}
+
+
+int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                         u8 *buf, size_t len, int p2p_group)
+{
+       if (wpa_s->global->p2p_disabled)
+               return -1;
+       if (wpa_s->global->p2p == NULL)
+               return -1;
+
+       return p2p_assoc_req_ie(wpa_s->global->p2p, bssid, buf, len,
+                               p2p_group);
+}
+
+
+int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
+                         const u8 *ie, size_t ie_len)
+{
+       if (wpa_s->global->p2p_disabled)
+               return 0;
+       if (wpa_s->global->p2p == NULL)
+               return 0;
+
+       return p2p_probe_req_rx(wpa_s->global->p2p, addr, ie, ie_len);
+}
+
+
+void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+                       const u8 *sa, const u8 *bssid,
+                       u8 category, const u8 *data, size_t len, int freq)
+{
+       if (wpa_s->global->p2p_disabled)
+               return;
+       if (wpa_s->global->p2p == NULL)
+               return;
+
+       p2p_rx_action(wpa_s->global->p2p, da, sa, bssid, category, data, len,
+                     freq);
+}
+
+
+void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies)
+{
+       if (wpa_s->global->p2p_disabled)
+               return;
+       if (wpa_s->global->p2p == NULL)
+               return;
+
+       p2p_scan_ie(wpa_s->global->p2p, ies);
+}
+
+
+void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
+{
+       p2p_group_deinit(wpa_s->p2p_group);
+       wpa_s->p2p_group = NULL;
+}
+
+
+int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+       wpa_s->p2p_long_listen = 0;
+
+       return p2p_reject(wpa_s->global->p2p, addr);
+}
+
+
+/* Invite to reinvoke a persistent group */
+int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+                   struct wpa_ssid *ssid, const u8 *go_dev_addr)
+{
+       enum p2p_invite_role role;
+       u8 *bssid = NULL;
+
+       if (ssid->mode == WPAS_MODE_P2P_GO) {
+               role = P2P_INVITE_ROLE_GO;
+               if (peer_addr == NULL) {
+                       wpa_printf(MSG_DEBUG, "P2P: Missing peer "
+                                  "address in invitation command");
+                       return -1;
+               }
+               if (wpas_p2p_create_iface(wpa_s)) {
+                       if (wpas_p2p_add_group_interface(wpa_s,
+                                                        WPA_IF_P2P_GO) < 0) {
+                               wpa_printf(MSG_ERROR, "P2P: Failed to "
+                                          "allocate a new interface for the "
+                                          "group");
+                               return -1;
+                       }
+                       bssid = wpa_s->pending_interface_addr;
+               } else
+                       bssid = wpa_s->own_addr;
+       } else {
+               role = P2P_INVITE_ROLE_CLIENT;
+               peer_addr = ssid->bssid;
+       }
+       wpa_s->pending_invite_ssid_id = ssid->id;
+
+       return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
+                         ssid->ssid, ssid->ssid_len, 0, go_dev_addr, 1);
+}
+
+
+/* Invite to join an active group */
+int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
+                         const u8 *peer_addr, const u8 *go_dev_addr)
+{
+       struct wpa_global *global = wpa_s->global;
+       enum p2p_invite_role role;
+       u8 *bssid = NULL;
+       struct wpa_ssid *ssid;
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               if (os_strcmp(wpa_s->ifname, ifname) == 0)
+                       break;
+       }
+       if (wpa_s == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: Interface '%s' not found", ifname);
+               return -1;
+       }
+
+       ssid = wpa_s->current_ssid;
+       if (ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: No current SSID to use for "
+                          "invitation");
+               return -1;
+       }
+
+       if (ssid->mode == WPAS_MODE_P2P_GO) {
+               role = P2P_INVITE_ROLE_ACTIVE_GO;
+               bssid = wpa_s->own_addr;
+               if (go_dev_addr == NULL)
+                       go_dev_addr = wpa_s->own_addr;
+       } else {
+               role = P2P_INVITE_ROLE_CLIENT;
+               if (wpa_s->wpa_state < WPA_ASSOCIATED) {
+                       wpa_printf(MSG_DEBUG, "P2P: Not associated - cannot "
+                                  "invite to current group");
+                       return -1;
+               }
+               bssid = wpa_s->bssid;
+               if (go_dev_addr == NULL &&
+                   !is_zero_ether_addr(wpa_s->go_dev_addr))
+                       go_dev_addr = wpa_s->go_dev_addr;
+       }
+       wpa_s->pending_invite_ssid_id = -1;
+
+       return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
+                         ssid->ssid, ssid->ssid_len, 0, go_dev_addr, 0);
+}
+
+
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+       const char *ssid_txt;
+       u8 go_dev_addr[ETH_ALEN];
+       int persistent;
+
+       if (!wpa_s->show_group_started || !ssid)
+               return;
+
+       wpa_s->show_group_started = 0;
+
+       ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+       os_memset(go_dev_addr, 0, ETH_ALEN);
+       if (ssid->bssid_set)
+               os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN);
+       persistent = wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
+                                              ssid->ssid_len);
+       os_memcpy(wpa_s->go_dev_addr, go_dev_addr, ETH_ALEN);
+
+       if (ssid->passphrase == NULL && ssid->psk_set) {
+               char psk[65];
+               wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
+               wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+                       "%s client ssid=\"%s\" psk=%s go_dev_addr=" MACSTR
+                       "%s",
+                       wpa_s->ifname, ssid_txt, psk, MAC2STR(go_dev_addr),
+                       persistent ? " [PERSISTENT]" : "");
+       } else {
+               wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+                       "%s client ssid=\"%s\" passphrase=\"%s\" go_dev_addr="
+                       MACSTR "%s",
+                       wpa_s->ifname, ssid_txt,
+                       ssid->passphrase ? ssid->passphrase : "",
+                       MAC2STR(go_dev_addr),
+                       persistent ? " [PERSISTENT]" : "");
+       }
+
+       if (persistent)
+               wpas_p2p_store_persistent_group(wpa_s->parent, ssid,
+                                               go_dev_addr);
+}
+
+
+int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
+                         u32 interval1, u32 duration2, u32 interval2)
+{
+       if (wpa_s->wpa_state < WPA_ASSOCIATED ||
+           wpa_s->current_ssid == NULL ||
+           wpa_s->current_ssid->mode != WPAS_MODE_INFRA)
+               return -1;
+
+       return p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid,
+                               wpa_s->own_addr, wpa_s->assoc_freq,
+                               duration1, interval1, duration2, interval2);
+}
+
+
+int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
+                       unsigned int interval)
+{
+       return p2p_ext_listen(wpa_s->global->p2p, period, interval);
+}
+
+
+void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                          u16 reason_code, const u8 *ie, size_t ie_len)
+{
+       if (wpa_s->global->p2p_disabled)
+               return;
+
+       p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len);
+}
+
+
+void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                            u16 reason_code, const u8 *ie, size_t ie_len)
+{
+       if (wpa_s->global->p2p_disabled)
+               return;
+
+       p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len);
+}
+
+
+void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
+{
+       struct p2p_data *p2p = wpa_s->global->p2p;
+
+       if (p2p == NULL)
+               return;
+
+       if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_NAME)
+               p2p_set_dev_name(p2p, wpa_s->conf->device_name);
+
+       if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE) {
+               u8 pri_dev_type[8];
+               if (wpa_s->conf->device_type) {
+                       if (wps_dev_type_str2bin(wpa_s->conf->device_type,
+                                                pri_dev_type) < 0) {
+                               wpa_printf(MSG_ERROR, "P2P: Invalid "
+                                          "device_type");
+                       } else
+                               p2p_set_pri_dev_type(p2p, pri_dev_type);
+               }
+       }
+
+       if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) {
+               u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8];
+               size_t num = 0;
+               int i;
+               for (i = 0; i < MAX_SEC_DEVICE_TYPES; i++) {
+                       if (wpa_s->conf->sec_device_type[i] == NULL)
+                               continue;
+                       if (wps_dev_type_str2bin(
+                                   wpa_s->conf->sec_device_type[i],
+                                   sec_dev_type[num]) < 0) {
+                               wpa_printf(MSG_ERROR, "P2P: Invalid "
+                                          "sec_device_type");
+                               continue;
+                       }
+                       num++;
+                       if (num == P2P_SEC_DEVICE_TYPES)
+                               break;
+               }
+               p2p_set_sec_dev_types(p2p, (void *) sec_dev_type, num);
+       }
+
+       if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
+           wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
+               char country[3];
+               country[0] = wpa_s->conf->country[0];
+               country[1] = wpa_s->conf->country[1];
+               country[2] = 0x04;
+               p2p_set_country(p2p, country);
+       }
+
+       if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_SSID_POSTFIX) {
+               p2p_set_ssid_postfix(p2p, (u8 *) wpa_s->conf->p2p_ssid_postfix,
+                                    wpa_s->conf->p2p_ssid_postfix ?
+                                    os_strlen(wpa_s->conf->p2p_ssid_postfix) :
+                                    0);
+       }
+}
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
new file mode 100644 (file)
index 0000000..6b28dc3
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * wpa_supplicant - P2P
+ * 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_SUPPLICANT_H
+#define P2P_SUPPLICANT_H
+
+enum p2p_wps_method;
+
+int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit(struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit_global(struct wpa_global *global);
+int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+                    const char *pin, enum p2p_wps_method wps_method,
+                    int persistent_group, int join, int auth, int go_intent,
+                    int freq);
+void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+                                  unsigned int freq, unsigned int duration);
+void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+                                         unsigned int freq);
+int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
+int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
+                      int freq);
+int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
+                                 struct wpa_ssid *ssid, int addr_allocated,
+                                 int freq);
+struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
+                                      int persistent_group,
+                                      int group_formation);
+void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+                         int registrar);
+int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+                      const char *config_method);
+void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
+                               const u8 *data, size_t data_len, int ack);
+int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+                             char *end);
+enum p2p_discovery_type;
+int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
+                 enum p2p_discovery_type type);
+void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s);
+int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout);
+int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                         u8 *buf, size_t len, int p2p_group);
+int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
+                         const u8 *ie, size_t ie_len);
+void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+                       const u8 *sa, const u8 *bssid,
+                       u8 category, const u8 *data, size_t len, int freq);
+void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies);
+void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
+void wpas_dev_found(void *ctx, const u8 *addr, const u8 *dev_addr,
+                   const u8 *pri_dev_type, const char *dev_name,
+                   u16 config_methods, u8 dev_capab, u8 group_capab);
+void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res);
+void wpas_go_neg_req_rx(void *ctx, const u8 *src);
+void wpas_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);
+void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods);
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+                    u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+                     const u8 *tlvs, size_t tlvs_len);
+void * wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+                          const struct wpabuf *tlvs);
+void * wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+                               u8 version, const char *query);
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, void *req);
+void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
+                         const u8 *dst, u8 dialog_token,
+                         const struct wpabuf *resp_tlvs);
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s);
+void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s);
+int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
+                                struct wpabuf *query, struct wpabuf *resp);
+int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
+                                const struct wpabuf *query);
+int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
+                             const char *service);
+int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
+                             const char *service);
+int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
+int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+                   struct wpa_ssid *ssid, const u8 *go_dev_addr);
+int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
+                         const u8 *peer_addr, const u8 *go_dev_addr);
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
+int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
+                         u32 interval1, u32 duration2, u32 interval2);
+int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
+                       unsigned int interval);
+void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                          u16 reason_code, const u8 *ie, size_t ie_len);
+void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                            u16 reason_code, const u8 *ie, size_t ie_len);
+void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
+
+#endif /* P2P_SUPPLICANT_H */
index d6b12e8..b69197e 100644 (file)
@@ -42,6 +42,7 @@
 #include "ibss_rsn.h"
 #include "sme.h"
 #include "ap.h"
+#include "p2p_supplicant.h"
 #include "notify.h"
 #include "bgscan.h"
 #include "bss.h"
@@ -427,6 +428,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 #ifdef CONFIG_AP
        wpa_supplicant_ap_deinit(wpa_s);
 #endif /* CONFIG_AP */
+
+#ifdef CONFIG_P2P
+       wpas_p2p_deinit(wpa_s);
+#endif /* CONFIG_P2P */
 }
 
 
@@ -545,6 +550,9 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
                wpa_s->reassociated_connection = 1;
                wpa_drv_set_operstate(wpa_s, 1);
                wpa_s->after_wps = 0;
+#ifdef CONFIG_P2P
+               wpas_p2p_completed(wpa_s);
+#endif /* CONFIG_P2P */
        } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
                   state == WPA_ASSOCIATED) {
                wpa_s->new_connection = 1;
@@ -586,7 +594,7 @@ static void wpa_supplicant_terminate(int sig, void *signal_ctx)
 }
 
 
-static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
+void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
 {
        enum wpa_states old_state = wpa_s->wpa_state;
 
@@ -1884,6 +1892,7 @@ static struct wpa_supplicant * wpa_supplicant_alloc(void)
                return NULL;
        wpa_s->scan_req = 1;
        wpa_s->new_connection = 1;
+       wpa_s->parent = wpa_s;
 
        return wpa_s;
 }
@@ -2092,6 +2101,13 @@ next_driver:
        }
 #endif /* CONFIG_IBSS_RSN */
 
+#ifdef CONFIG_P2P
+       if (wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
+               wpa_printf(MSG_ERROR, "Failed to init P2P");
+               return -1;
+       }
+#endif /* CONFIG_P2P */
+
        if (wpa_bss_init(wpa_s) < 0)
                return -1;
 
@@ -2220,6 +2236,8 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
 
        wpa_printf(MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
 
+       if (global->p2p_group_formation == wpa_s)
+               global->p2p_group_formation = NULL;
        wpa_supplicant_deinit_iface(wpa_s, 1);
        os_free(wpa_s);
 
@@ -2279,6 +2297,8 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
        global = os_zalloc(sizeof(*global));
        if (global == NULL)
                return NULL;
+       dl_list_init(&global->p2p_srv_bonjour);
+       dl_list_init(&global->p2p_srv_upnp);
        global->params.daemonize = params->daemonize;
        global->params.wait_for_monitor = params->wait_for_monitor;
        global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
@@ -2392,6 +2412,10 @@ void wpa_supplicant_deinit(struct wpa_global *global)
        if (global == NULL)
                return;
 
+#ifdef CONFIG_P2P
+       wpas_p2p_deinit_global(global);
+#endif /* CONFIG_P2P */
+
        while (global->ifaces)
                wpa_supplicant_remove_iface(global, global->ifaces);
 
@@ -2434,5 +2458,9 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s)
        wpas_wps_update_config(wpa_s);
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_P2P
+       wpas_p2p_update_config(wpa_s);
+#endif /* CONFIG_P2P */
+
        wpa_s->conf->changed_parameters = 0;
 }
index 2ee22be..4f4bdf4 100644 (file)
@@ -182,6 +182,18 @@ struct wpa_params {
        char *override_ctrl_interface;
 };
 
+struct p2p_srv_bonjour {
+       struct dl_list list;
+       struct wpabuf *query;
+       struct wpabuf *resp;
+};
+
+struct p2p_srv_upnp {
+       struct dl_list list;
+       u8 version;
+       char *service;
+};
+
 /**
  * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces
  *
@@ -196,6 +208,12 @@ struct wpa_global {
        void **drv_priv;
        size_t drv_count;
        struct os_time suspend_time;
+       struct p2p_data *p2p;
+       struct wpa_supplicant *p2p_group_formation;
+       u8 p2p_dev_addr[ETH_ALEN];
+       struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */
+       struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */
+       int p2p_disabled;
 };
 
 
@@ -303,6 +321,7 @@ struct wpa_client_mlme {
  */
 struct wpa_supplicant {
        struct wpa_global *global;
+       struct wpa_supplicant *parent;
        struct wpa_supplicant *next;
        struct l2_packet_data *l2;
        struct l2_packet_data *l2_br;
@@ -409,7 +428,7 @@ struct wpa_supplicant {
                u8 ssid[32];
                size_t ssid_len;
                int freq;
-               u8 assoc_req_ie[80];
+               u8 assoc_req_ie[200];
                size_t assoc_req_ie_len;
                int mfp;
                int ft_used;
@@ -429,6 +448,42 @@ struct wpa_supplicant {
        void *ap_configured_cb_data;
 #endif /* CONFIG_AP */
 
+#ifdef CONFIG_P2P
+       struct p2p_go_neg_results *go_params;
+       int create_p2p_iface;
+       u8 pending_interface_addr[ETH_ALEN];
+       char pending_interface_name[IFNAMSIZ];
+       int pending_interface_type;
+       int p2p_group_idx;
+       unsigned int off_channel_freq;
+       struct wpabuf *pending_action_tx;
+       u8 pending_action_src[ETH_ALEN];
+       u8 pending_action_dst[ETH_ALEN];
+       u8 pending_action_bssid[ETH_ALEN];
+       unsigned int pending_action_freq;
+       int pending_action_without_roc;
+       unsigned int pending_listen_freq;
+       unsigned int pending_listen_duration;
+       enum {
+               NOT_P2P_GROUP_INTERFACE,
+               P2P_GROUP_INTERFACE_PENDING,
+               P2P_GROUP_INTERFACE_GO,
+               P2P_GROUP_INTERFACE_CLIENT
+       } p2p_group_interface;
+       struct p2p_group *p2p_group;
+       int p2p_long_listen;
+       char p2p_pin[10];
+       int p2p_sd_over_ctrl_iface;
+       int p2p_in_provisioning;
+       int pending_invite_ssid_id;
+       int show_group_started;
+       u8 go_dev_addr[ETH_ALEN];
+       int pending_pd_before_join;
+       u8 pending_join_iface_addr[ETH_ALEN];
+       u8 pending_join_dev_addr[ETH_ALEN];
+       int pending_join_wps_method;
+#endif /* CONFIG_P2P */
+
        struct wpa_ssid *bgscan_ssid;
        const struct bgscan_ops *bgscan;
        void *bgscan_priv;
@@ -501,6 +556,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 enum wpa_key_mgmt key_mgmt2driver(int key_mgmt);
 enum wpa_cipher cipher_suite2driver(int cipher);
 void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
 
 /* events.c */
 void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s);
index 2da1697..6d95921 100644 (file)
@@ -32,6 +32,7 @@
 #include "blacklist.h"
 #include "bss.h"
 #include "scan.h"
+#include "p2p_supplicant.h"
 #include "wps_supplicant.h"
 
 
@@ -399,6 +400,9 @@ static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
        wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
        wpa_s->wps_success = 1;
        wpas_notify_wps_event_success(wpa_s);
+#ifdef CONFIG_P2P
+       wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
+#endif /* CONFIG_P2P */
 }