Cumulative patch from commit 2e988392436227c51002b573ee27a8cee37f70e9

2e98839 P2P: Disable DNS server from dnsmasq
c07f261 P2P NFC: Add script for connection handover with nfcpy
12288d8 WPS NFC: Protect nfcpy pretty print calls against exceptions
c209dd1 WPS NFC: nfcpy script to use new connection handover design
6202500 WPS NFC: Logging level configuration to wps-nfc.py and wps-ap-nfc.py
1f1b5b3 WPS NFC: Clean up nfcpy script no-wait operations
79ede5a WPS NFC: Validate ctrl_iface response before decoding it
ab1db08 WPS NFC: Use argparse in the nfcpy scripts
6f8fa6e WPS NFC: Update wps-nfc.py and wps-ap-nfc.py to use new nfcpy API
b56f6c8 P2P NFC: Add support for freq option in NFC ctrl_iface commands
91a6501 WPS NFC: Use BSSID and AP Channel from handover select
91226e0 WPS: Add testing option to corrupt public key hash
7312776 WPS NFC: add more debug prints for connection handover report
5cd4f66 WPS NFC: Use AP Channel information from credential container
d2f1837 WPS NFC: Add BSSID and AP channel info to Configuration Token
75dbf98 WPS-STRICT: Update valid Device Password ID and Config Error range
5cd4740 P2P NFC: WPA state machine config with driver-based BSS selection
8e9f53c P2P NFC: Static handover with NFC Tag on client
dd87677 P2P NFC: Enable own NFC Tag on GO Registrar
abe44e3 P2P NFC: Add GO info into handover message when in client role
23318be P2P NFC: Optimize join-a-group operation based on NFC information
86e3208 P2P NFC: Copy DH parameters to a separate group interface
d4b4d7f WPS NFC: Update DH keys for ER operations
ac08752 WPS NFC: Use pubkey mismatch config error from Enrollee
59b45d1 P2P NFC: Add processing of P2P client while NFC handover case
74df9ec P2P NFC: Do not try to join peer if both devices are already GO
201b0f5 P2P: Add test option to disable IP address assignment request
25ef852 P2P: Add support for IP address assignment in 4-way handshake
fdd48ff P2P NFC: Optimize GO Negotiation retries
c4f87a7 P2P NFC: Add NFC tag enabling for static handover
dd37a93 P2P NFC: Report handover select from tag for static handover
db6ae69 P2P NFC: Report connection handover as trigger for P2P
9358878 P2P NFC: Build connection handover messages
c00ab85 P2P NFC: Define WPS_NFC config method
0deab08 P2P NFC: Allow separate WPS/P2P IES to be parsed
fca9958 P2P NFC: Pass OOB Dev Password through P2P parser
ab9e344 P2P NFC: Pass OOB Device Password ID to P2P
5154689 P2P NFC: Add WPS attribute building for P2P NFC
01afd8d P2P NFC: Add NDEF helpers for P2P connection handover messages
9e323a2 P2P NFC: Add OOB GO Negotiation Channel attribute
14d8645 WPS NFC: Allow BSSID and channel to be included in handover select
50d1f89 NFC: Update WPS ER to use the new connection handover design
d950793 WPS NFC: Add support for wpa_supplicant AP/GO mode to use handover
fa4c298 WPS NFC: Process new style handover select
068cdb1 WPS NFC: New style connection handover select from AP/Registrar
3189ca0 WPS NFC: Add AP mode connection handover report
41f9ffb WPS NFC: Build new style carrier record for connection handover request
3f1639d WPS NFC: Split DH key generation to a separate function
9754917 WPS NFC: Update NFC connection handover design
34b6795 WPS NFC: Use abbreviated handshake if both PK hashes delivered OOB
57630e6 WPS: Preparations for allowing SSID filtering for provisioning step
5f45455 WPS NFC: Validate peer public key hash on Enrollee
ff40cd6 WPS NFC: Send M2D with config error 20 on pkhash mismatch
e435417 WPS: Remove Version attribute from NFC messages
72403ec WPS: Add builder functions for AP Channel and RF Bands attributes
ea43ad9 P2P: Make group operating channel available
9f7cd9a P2P: Split add-group-info into a helper function
253f2e3 P2P: Apply unsafe frequency rules to available channels
1682c62 Add a header file defining QCA OUI and vendor extensions

Change-Id: Ia7604d018e1ffb25e06bdc01ce258fc4a0569245
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 72faeae..4a9da5f 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -399,13 +399,70 @@
 static int hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd,
 						  char *cmd)
 {
-	/*
-	 * Since NFC connection handover provided full WPS Credential, there is
-	 * no need for additional operations within hostapd. Just report this in
-	 * debug log.
-	 */
-	wpa_printf(MSG_DEBUG, "NFC: Connection handover reported: %s", cmd);
-	return 0;
+	size_t len;
+	struct wpabuf *req, *sel;
+	int ret;
+	char *pos, *role, *type, *pos2;
+
+	role = cmd;
+	pos = os_strchr(role, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	type = pos;
+	pos = os_strchr(type, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	pos2 = os_strchr(pos, ' ');
+	if (pos2 == NULL)
+		return -1;
+	*pos2++ = '\0';
+
+	len = os_strlen(pos);
+	if (len & 0x01)
+		return -1;
+	len /= 2;
+
+	req = wpabuf_alloc(len);
+	if (req == NULL)
+		return -1;
+	if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
+		wpabuf_free(req);
+		return -1;
+	}
+
+	len = os_strlen(pos2);
+	if (len & 0x01) {
+		wpabuf_free(req);
+		return -1;
+	}
+	len /= 2;
+
+	sel = wpabuf_alloc(len);
+	if (sel == NULL) {
+		wpabuf_free(req);
+		return -1;
+	}
+	if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
+		wpabuf_free(req);
+		wpabuf_free(sel);
+		return -1;
+	}
+
+	if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) {
+		ret = hostapd_wps_nfc_report_handover(hapd, req, sel);
+	} else {
+		wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
+			   "reported: role=%s type=%s", role, type);
+		ret = -1;
+	}
+	wpabuf_free(req);
+	wpabuf_free(sel);
+
+	return ret;
 }
 
 #endif /* CONFIG_WPS_NFC */
@@ -908,6 +965,10 @@
 		wps_testing_dummy_cred = atoi(value);
 		wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
 			   wps_testing_dummy_cred);
+	} else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) {
+		wps_corrupt_pkhash = atoi(value);
+		wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
+			   wps_corrupt_pkhash);
 #endif /* CONFIG_WPS_TESTING */
 #ifdef CONFIG_INTERWORKING
 	} else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) {
@@ -1101,7 +1162,7 @@
 				       void *sock_ctx)
 {
 	struct hostapd_data *hapd = eloop_ctx;
-	char buf[256];
+	char buf[4096];
 	int res;
 	struct sockaddr_un from;
 	socklen_t fromlen = sizeof(from);
@@ -1542,6 +1603,16 @@
 }
 
 
+static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces)
+{
+#ifdef CONFIG_WPS_TESTING
+	wps_version_number = 0x20;
+	wps_testing_dummy_cred = 0;
+	wps_corrupt_pkhash = 0;
+#endif /* CONFIG_WPS_TESTING */
+}
+
+
 static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
 					      void *sock_ctx)
 {
@@ -1571,6 +1642,8 @@
 	} else if (os_strncmp(buf, "RELOG", 5) == 0) {
 		if (wpa_debug_reopen_file() < 0)
 			reply_len = -1;
+	} else if (os_strcmp(buf, "FLUSH") == 0) {
+		hostapd_ctrl_iface_flush(interfaces);
 	} else if (os_strncmp(buf, "ADD ", 4) == 0) {
 		if (hostapd_ctrl_iface_add(interfaces, buf + 4) < 0)
 			reply_len = -1;
diff --git a/hostapd/wps-ap-nfc.py b/hostapd/wps-ap-nfc.py
index 61b5519..58e538a 100755
--- a/hostapd/wps-ap-nfc.py
+++ b/hostapd/wps-ap-nfc.py
@@ -9,6 +9,7 @@
 import os
 import sys
 import time
+import argparse
 
 import nfc
 import nfc.ndef
@@ -16,11 +17,11 @@
 import nfc.handover
 
 import logging
-logging.basicConfig()
 
 import wpaspy
 
 wpas_ctrl = '/var/run/hostapd'
+continue_loop = True
 
 def wpas_connect():
     ifaces = []
@@ -48,7 +49,9 @@
     wpas = wpas_connect()
     if (wpas == None):
         return
-    print wpas.request("WPS_NFC_TAG_READ " + message.encode("hex"))
+    if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
+        return False
+    return True
 
 
 def wpas_get_config_token():
@@ -82,12 +85,18 @@
 
 
 class HandoverServer(nfc.handover.HandoverServer):
-    def __init__(self):
-        super(HandoverServer, self).__init__()
+    def __init__(self, llc):
+        super(HandoverServer, self).__init__(llc)
+        self.ho_server_processing = False
+        self.success = False
 
     def process_request(self, request):
         print "HandoverServer - request received"
-        print "Parsed handover request: " + request.pretty()
+        try:
+            print "Parsed handover request: " + request.pretty()
+        except Exception, e:
+            print e
+        print str(request).encode("hex")
 
         sel = nfc.ndef.HandoverSelectMessage(version="1.2")
 
@@ -95,172 +104,172 @@
             print "Remote carrier type: " + carrier.type
             if carrier.type == "application/vnd.wfa.wsc":
                 print "WPS carrier type match - add WPS carrier record"
-                self.received_carrier = carrier.record
                 data = wpas_get_handover_sel()
                 if data is None:
                     print "Could not get handover select carrier record from hostapd"
                     continue
                 print "Handover select carrier record from hostapd:"
                 print data.encode("hex")
-                self.sent_carrier = data
+                wpas_report_handover(carrier.record, data)
 
                 message = nfc.ndef.Message(data);
                 sel.add_carrier(message[0], "active", message[1:])
 
         print "Handover select:"
-        print sel.pretty()
+        try:
+            print sel.pretty()
+        except Exception, e:
+            print e
         print str(sel).encode("hex")
 
         print "Sending handover select"
+        self.success = True
         return sel
 
 
-def wps_handover_resp(peer):
-    print "Trying to handle WPS handover"
-
-    srv = HandoverServer()
-    srv.sent_carrier = None
-
-    nfc.llcp.activate(peer);
-
-    try:
-        print "Trying handover";
-        srv.start()
-        print "Wait for disconnect"
-        while nfc.llcp.connected():
-            time.sleep(0.1)
-        print "Disconnected after handover"
-    except nfc.llcp.ConnectRefused:
-        print "Handover connection refused"
-        nfc.llcp.shutdown()
-        return
-
-    if srv.sent_carrier:
-        wpas_report_handover(srv.received_carrier, srv.sent_carrier)
-
-    print "Remove peer"
-    nfc.llcp.shutdown()
-    print "Done with handover"
-
-
 def wps_tag_read(tag):
+    success = False
     if len(tag.ndef.message):
-        message = nfc.ndef.Message(tag.ndef.message)
-        print "message type " + message.type
-
-        for record in message:
+        for record in tag.ndef.message:
             print "record type " + record.type
             if record.type == "application/vnd.wfa.wsc":
                 print "WPS tag - send to hostapd"
-                wpas_tag_read(tag.ndef.message)
+                success = wpas_tag_read(tag.ndef.message)
                 break
     else:
         print "Empty tag"
 
-    print "Remove tag"
-    while tag.is_present:
+    return success
+
+
+def rdwr_connected_write(tag):
+    print "Tag found - writing"
+    global write_data
+    tag.ndef.message = str(write_data)
+    print "Done - remove tag"
+    global only_one
+    if only_one:
+        global continue_loop
+        continue_loop = False
+    global write_wait_remove
+    while write_wait_remove and tag.is_present:
         time.sleep(0.1)
 
-
-def wps_write_config_tag(clf):
+def wps_write_config_tag(clf, wait_remove=True):
     print "Write WPS config token"
-    data = wpas_get_config_token()
-    if (data == None):
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_config_token()
+    if write_data == None:
         print "Could not get WPS config token from hostapd"
         return
 
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while tag.is_present:
-        time.sleep(0.1)
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
 
-def wps_write_password_tag(clf):
+def wps_write_password_tag(clf, wait_remove=True):
     print "Write WPS password token"
-    data = wpas_get_password_token()
-    if (data == None):
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_password_token()
+    if write_data == None:
         print "Could not get WPS password token from hostapd"
         return
 
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while tag.is_present:
-        time.sleep(0.1)
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
 
-def find_peer(clf):
-    while True:
-        if nfc.llcp.connected():
-            print "LLCP connected"
-        general_bytes = nfc.llcp.startup({})
-        peer = clf.listen(ord(os.urandom(1)) + 250, general_bytes)
-        if isinstance(peer, nfc.DEP):
-            print "listen -> DEP";
-            if peer.general_bytes.startswith("Ffm"):
-                print "Found DEP"
-                return peer
-            print "mismatch in general_bytes"
-            print peer.general_bytes
+def rdwr_connected(tag):
+    global only_one, no_wait
+    print "Tag connected: " + str(tag)
 
-        peer = clf.poll(general_bytes)
-        if isinstance(peer, nfc.DEP):
-            print "poll -> DEP";
-            if peer.general_bytes.startswith("Ffm"):
-                print "Found DEP"
-                return peer
-            print "mismatch in general_bytes"
-            print peer.general_bytes
+    if tag.ndef:
+        print "NDEF tag: " + tag.type
+        try:
+            print tag.ndef.message.pretty()
+        except Exception, e:
+            print e
+        success = wps_tag_read(tag)
+        if only_one and success:
+            global continue_loop
+            continue_loop = False
+    else:
+        print "Not an NDEF tag - remove tag"
 
-        if peer:
-            print "Found tag"
-            return peer
+    return not no_wait
+
+
+def llcp_startup(clf, llc):
+    print "Start LLCP server"
+    global srv
+    srv = HandoverServer(llc)
+    return llc
+
+def llcp_connected(llc):
+    print "P2P LLCP connected"
+    global wait_connection
+    wait_connection = False
+    global srv
+    srv.start()
+    return True
 
 
 def main():
     clf = nfc.ContactlessFrontend()
 
+    parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations')
+    parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+                        action='store_const', dest='loglevel',
+                        help='verbose debug output')
+    parser.add_argument('-q', const=logging.WARNING, action='store_const',
+                        dest='loglevel', help='be quiet')
+    parser.add_argument('--only-one', '-1', action='store_true',
+                        help='run only one operation and exit')
+    parser.add_argument('--no-wait', action='store_true',
+                        help='do not wait for tag to be removed before exiting')
+    parser.add_argument('command', choices=['write-config',
+                                            'write-password'],
+                        nargs='?')
+    args = parser.parse_args()
+
+    global only_one
+    only_one = args.only_one
+
+    global no_wait
+    no_wait = args.no_wait
+
+    logging.basicConfig(level=args.loglevel)
+
     try:
-        if len(sys.argv) > 1 and sys.argv[1] == "write-config":
-            wps_write_config_tag(clf)
+        if not clf.open("usb"):
+            print "Could not open connection with an NFC device"
             raise SystemExit
 
-        if len(sys.argv) > 1 and sys.argv[1] == "write-password":
-            wps_write_password_tag(clf)
+        if args.command == "write-config":
+            wps_write_config_tag(clf, wait_remove=not args.no_wait)
             raise SystemExit
 
-        while True:
+        if args.command == "write-password":
+            wps_write_password_tag(clf, wait_remove=not args.no_wait)
+            raise SystemExit
+
+        global continue_loop
+        while continue_loop:
             print "Waiting for a tag or peer to be touched"
+            wait_connection = True
+            try:
+                if not clf.connect(rdwr={'on-connect': rdwr_connected},
+                                   llcp={'on-startup': llcp_startup,
+                                         'on-connect': llcp_connected}):
+                    break
+            except Exception, e:
+                print "clf.connect failed"
 
-            tag = find_peer(clf)
-            if isinstance(tag, nfc.DEP):
-                wps_handover_resp(tag)
-                continue
-
-            if tag.ndef:
-                wps_tag_read(tag)
-                continue
-
-            print "Not an NDEF tag - remove tag"
-            while tag.is_present:
-                time.sleep(0.1)
+            global srv
+            if only_one and srv and srv.success:
+                raise SystemExit
 
     except KeyboardInterrupt:
         raise SystemExit
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 0bb937e..b4860a0 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -384,6 +384,12 @@
 #define P2P_MANAGE BIT(3)
 #define P2P_ALLOW_CROSS_CONNECTION BIT(4)
 	int p2p;
+#ifdef CONFIG_P2P
+	u8 ip_addr_go[4];
+	u8 ip_addr_mask[4];
+	u8 ip_addr_start[4];
+	u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
 
 	int disassoc_low_ack;
 	int skip_inactivity_poll;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 670b834..24e764d 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -908,6 +908,7 @@
 	char buf[100];
 #ifdef CONFIG_P2P
 	u8 addr[ETH_ALEN];
+	u8 ip_addr_buf[4];
 #endif /* CONFIG_P2P */
 
 	if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
@@ -929,12 +930,25 @@
 		os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
 
 	if (authorized) {
-		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s", buf);
+		char ip_addr[100];
+		ip_addr[0] = '\0';
+#ifdef CONFIG_P2P
+		if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
+			os_snprintf(ip_addr, sizeof(ip_addr),
+				    " ip_addr=%u.%u.%u.%u",
+				    ip_addr_buf[0], ip_addr_buf[1],
+				    ip_addr_buf[2], ip_addr_buf[3]);
+		}
+#endif /* CONFIG_P2P */
+
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s",
+			buf, ip_addr);
 
 		if (hapd->msg_ctx_parent &&
 		    hapd->msg_ctx_parent != hapd->msg_ctx)
 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
-					  AP_STA_CONNECTED "%s", buf);
+					  AP_STA_CONNECTED "%s%s",
+					  buf, ip_addr);
 
 		sta->flags |= WLAN_STA_AUTHORIZED;
 	} else {
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 5993edf..707a63f 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -11,6 +11,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "utils/state_machine.h"
+#include "utils/bitfield.h"
 #include "common/ieee802_11_defs.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
@@ -424,6 +425,17 @@
 				       wpa_rekey_gtk, wpa_auth, NULL);
 	}
 
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(conf->ip_addr_start)) {
+		int count = WPA_GET_BE32(conf->ip_addr_end) -
+			WPA_GET_BE32(conf->ip_addr_start) + 1;
+		if (count > 1000)
+			count = 1000;
+		if (count > 0)
+			wpa_auth->ip_pool = bitfield_alloc(count);
+	}
+#endif /* CONFIG_P2P */
+
 	return wpa_auth;
 }
 
@@ -466,6 +478,11 @@
 	wpa_auth->ft_pmk_cache = NULL;
 #endif /* CONFIG_IEEE80211R */
 
+#ifdef CONFIG_P2P
+	bitfield_free(wpa_auth->ip_pool);
+#endif /* CONFIG_P2P */
+
+
 	os_free(wpa_auth->wpa_ie);
 
 	group = wpa_auth->group;
@@ -583,6 +600,19 @@
 
 static void wpa_free_sta_sm(struct wpa_state_machine *sm)
 {
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr)) {
+		u32 start;
+		wpa_printf(MSG_DEBUG, "P2P: Free assigned IP "
+			   "address %u.%u.%u.%u from " MACSTR,
+			   sm->ip_addr[0], sm->ip_addr[1],
+			   sm->ip_addr[2], sm->ip_addr[3],
+			   MAC2STR(sm->addr));
+		start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start);
+		bitfield_clear(sm->wpa_auth->ip_pool,
+			       WPA_GET_BE32(sm->ip_addr) - start);
+	}
+#endif /* CONFIG_P2P */
 	if (sm->GUpdateStationKeys) {
 		sm->group->GKeyDoneStations--;
 		sm->GUpdateStationKeys = FALSE;
@@ -1000,6 +1030,26 @@
 			return;
 		}
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+		if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+		    wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+			int idx;
+			wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
+				   "EAPOL-Key exchange");
+			idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+			if (idx >= 0) {
+				u32 start = WPA_GET_BE32(wpa_auth->conf.
+							 ip_addr_start);
+				bitfield_set(wpa_auth->ip_pool, idx);
+				WPA_PUT_BE32(sm->ip_addr, start + idx);
+				wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
+					   "address %u.%u.%u.%u to " MACSTR,
+					   sm->ip_addr[0], sm->ip_addr[1],
+					   sm->ip_addr[2], sm->ip_addr[3],
+					   MAC2STR(sm->addr));
+			}
+		}
+#endif /* CONFIG_P2P */
 		break;
 	case PAIRWISE_4:
 		if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -1995,6 +2045,10 @@
 		kde_len += 300; /* FTIE + 2 * TIE */
 	}
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr) > 0)
+		kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
+#endif /* CONFIG_P2P */
 	kde = os_malloc(kde_len);
 	if (kde == NULL)
 		return;
@@ -2056,6 +2110,16 @@
 		pos += 4;
 	}
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+	if (WPA_GET_BE32(sm->ip_addr) > 0) {
+		u8 addr[3 * 4];
+		os_memcpy(addr, sm->ip_addr, 4);
+		os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4);
+		os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4);
+		pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC,
+				  addr, sizeof(addr), NULL, 0);
+	}
+#endif /* CONFIG_P2P */
 
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
@@ -3103,3 +3167,14 @@
 		return 0;
 	return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE;
 }
+
+
+#ifdef CONFIG_P2P
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr)
+{
+	if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0)
+		return -1;
+	os_memcpy(addr, sm->ip_addr, 4);
+	return 0;
+}
+#endif /* CONFIG_P2P */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index da45ae4..bc3dec4 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -163,6 +163,12 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	double corrupt_gtk_rekey_mic_probability;
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+	u8 ip_addr_go[4];
+	u8 ip_addr_mask[4];
+	u8 ip_addr_start[4];
+	u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
 };
 
 typedef enum {
@@ -297,4 +303,6 @@
 int wpa_auth_uses_sae(struct wpa_state_machine *sm);
 int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm);
 
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr);
+
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 4c1d625..5af1495 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -78,6 +78,12 @@
 	wconf->corrupt_gtk_rekey_mic_probability =
 		iconf->corrupt_gtk_rekey_mic_probability;
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+	os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
+	os_memcpy(wconf->ip_addr_mask, conf->ip_addr_mask, 4);
+	os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4);
+	os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4);
+#endif /* CONFIG_P2P */
 }
 
 
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 9736874..fcd5878 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -121,6 +121,10 @@
 #endif /* CONFIG_IEEE80211R */
 
 	int pending_1_of_4_timeout;
+
+#ifdef CONFIG_P2P
+	u8 ip_addr[4];
+#endif /* CONFIG_P2P */
 };
 
 
@@ -185,6 +189,10 @@
 
 	struct rsn_pmksa_cache *pmksa;
 	struct wpa_ft_pmk_cache *ft_pmk_cache;
+
+#ifdef CONFIG_P2P
+	struct bitfield *ip_pool;
+#endif /* CONFIG_P2P */
 };
 
 
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index cdfcca1..274f4d6 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -708,6 +708,25 @@
 	}
 #endif /* CONFIG_IEEE80211W */
 
+#ifdef CONFIG_P2P
+	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
+		ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
+			    ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+
+	if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+		ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG,
+			    "WPA: IP Address Allocation in EAPOL-Key",
+			    ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+#endif /* CONFIG_P2P */
+
 	return 0;
 }
 
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index 4999139..f945882 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -39,6 +39,10 @@
 	const u8 *ftie;
 	size_t ftie_len;
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+	const u8 *ip_addr_req;
+	const u8 *ip_addr_alloc;
+#endif /* CONFIG_P2P */
 };
 
 int wpa_parse_kde_ies(const u8 *buf, size_t len,
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 4c94210..1b1dce4 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -1784,7 +1784,8 @@
 	if (hapd->wps == NULL)
 		return NULL;
 
-	ret = wps_get_oob_cred(hapd->wps);
+	ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd),
+			       hapd->iconf->channel);
 	if (ndef && ret) {
 		struct wpabuf *tmp;
 		tmp = ndef_build_wifi(ret);
@@ -1800,11 +1801,136 @@
 
 struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef)
 {
+	struct wpabuf *ret;
+
+	if (hapd->wps == NULL)
+		return NULL;
+
+	if (hapd->conf->wps_nfc_dh_pubkey == NULL) {
+		struct wps_context *wps = hapd->wps;
+		if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey,
+				   &hapd->conf->wps_nfc_dh_privkey) < 0)
+			return NULL;
+		hostapd_wps_nfc_clear(wps);
+		wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+		wps->ap_nfc_dh_pubkey =
+			wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+		wps->ap_nfc_dh_privkey =
+			wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+		if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+			hostapd_wps_nfc_clear(wps);
+			return NULL;
+		}
+	}
+
+	ret = wps_build_nfc_handover_sel(hapd->wps,
+					 hapd->conf->wps_nfc_dh_pubkey,
+					 hapd->own_addr, hapd->iface->freq);
+
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
+}
+
+
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel)
+{
+	struct wpabuf *wps;
+	int ret = -1;
+	u16 wsc_len;
+	const u8 *pos;
+	struct wpabuf msg;
+	struct wps_parse_attr attr;
+	u16 dev_pw_id;
+
 	/*
-	 * Handover Select carrier record for WPS uses the same format as
-	 * configuration token.
+	 * Enrollee/station is always initiator of the NFC connection handover,
+	 * so use the request message here to find Enrollee public key hash.
 	 */
-	return hostapd_wps_nfc_config_token(hapd, ndef);
+	wps = ndef_parse_wifi(req);
+	if (wps == NULL)
+		return -1;
+	wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+		   "payload from NFC connection handover");
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+	if (wpabuf_len(wps) < 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
+			   "Message");
+		goto out;
+	}
+	pos = wpabuf_head(wps);
+	wsc_len = WPA_GET_BE16(pos);
+	if (wsc_len > wpabuf_len(wps) - 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+			   "in rt Wi-Fi Handover Request Message", wsc_len);
+		goto out;
+	}
+	pos += 2;
+
+	wpa_hexdump(MSG_DEBUG,
+		    "WPS: WSC attributes in Wi-Fi Handover Request Message",
+		    pos, wsc_len);
+	if (wsc_len < wpabuf_len(wps) - 2) {
+		wpa_hexdump(MSG_DEBUG,
+			    "WPS: Ignore extra data after WSC attributes",
+			    pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+	}
+
+	wpabuf_set(&msg, pos, wsc_len);
+	ret = wps_parse_msg(&msg, &attr);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+			   "Wi-Fi Handover Request Message");
+		goto out;
+	}
+
+	if (attr.oob_dev_password == NULL ||
+	    attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+		wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+			   "included in Wi-Fi Handover Request Message");
+		ret = -1;
+		goto out;
+	}
+
+	if (attr.uuid_e == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
+			   "Handover Request Message");
+		ret = -1;
+		goto out;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
+
+	wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+		    attr.oob_dev_password, attr.oob_dev_password_len);
+	dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+				 WPS_OOB_PUBKEY_HASH_LEN);
+	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+			   "%u in Wi-Fi Handover Request Message", dev_pw_id);
+		ret = -1;
+		goto out;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
+		    attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+	ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar,
+					     attr.oob_dev_password,
+					     DEV_PW_NFC_CONNECTION_HANDOVER,
+					     NULL, 0, 1);
+
+out:
+	wpabuf_free(wps);
+	return ret;
 }
 
 
diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h
index a292598..204bd82 100644
--- a/src/ap/wps_hostapd.h
+++ b/src/ap/wps_hostapd.h
@@ -37,6 +37,9 @@
 struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
 					     int ndef);
 struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef);
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel);
 struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef);
 int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd);
 void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 592ae54..6f7f777 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -926,6 +926,7 @@
 	P2P_ATTR_INTERFACE = 16,
 	P2P_ATTR_OPERATING_CHANNEL = 17,
 	P2P_ATTR_INVITATION_FLAGS = 18,
+	P2P_ATTR_OOB_GO_NEG_CHANNEL = 19,
 	P2P_ATTR_VENDOR_SPECIFIC = 221
 };
 
@@ -947,6 +948,7 @@
 #define P2P_GROUP_CAPAB_CROSS_CONN BIT(4)
 #define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5)
 #define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6)
+#define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7)
 
 /* Invitation Flags */
 #define P2P_INVITATION_FLAGS_TYPE BIT(0)
@@ -971,6 +973,12 @@
 	P2P_SC_FAIL_REJECTED_BY_USER = 11,
 };
 
+enum p2p_role_indication {
+	P2P_DEVICE_NOT_IN_GROUP = 0x00,
+	P2P_CLIENT_IN_A_GROUP = 0x01,
+	P2P_GO_IN_A_GROUP = 0x02,
+};
+
 #define P2P_WILDCARD_SSID "DIRECT-"
 #define P2P_WILDCARD_SSID_LEN 7
 
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
new file mode 100644
index 0000000..0d83920
--- /dev/null
+++ b/src/common/qca-vendor.h
@@ -0,0 +1,51 @@
+/*
+ * Qualcomm Atheros OUI and vendor specific assignments
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef QCA_VENDOR_H
+#define QCA_VENDOR_H
+
+/*
+ * This file is a registry of identifier assignments from the Qualcomm Atheros
+ * OUI 00:13:74 for purposes other than MAC address assignment. New identifiers
+ * can be assigned through normal review process for changes to the upstream
+ * hostap.git repository.
+ */
+
+#define OUI_QCA 0x001374
+
+/**
+ * enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency
+ *	ranges to avoid to reduce issues due to interference or internal
+ *	co-existence information in the driver. The event data structure is
+ *	defined in struct qca_avoid_freq_list.
+ */
+enum qca_nl80211_vendor_subcmds {
+	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
+	QCA_NL80211_VENDOR_SUBCMD_TEST = 1,
+	/* subcmds 2..9 not yet allocated */
+	QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10,
+};
+
+
+struct qca_avoid_freq_range {
+	u32 start_freq;
+	u32 end_freq;
+} STRUCT_PACKED;
+
+struct qca_avoid_freq_list {
+	u32 count;
+	struct qca_avoid_freq_range range[0];
+} STRUCT_PACKED;
+
+#endif /* QCA_VENDOR_H */
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 62505f1..dcc035c 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -109,6 +109,9 @@
 #define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
 #define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
 
+#define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
+#define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
+
 #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
 
 #define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val))
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 9bd59a9..759cee4 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -74,6 +74,8 @@
  * be used again.
  */
 #define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
+/** Frequency ranges that the driver recommends to avoid */
+#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ "
 /** WPS overlap detected in PBC mode */
 #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
 /** Available WPS AP with active PBC found in scan results */
@@ -148,9 +150,13 @@
 #define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
 #define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id="
 #define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE "
+#define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO "
+#define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT "
+#define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT "
 
 /* parameters: <PMF enabled> <timeout in ms> <Session Information URL> */
 #define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT "
+#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP "
 
 #define INTERWORKING_AP "INTERWORKING-AP "
 #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 4fd16ed..2f4536e 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -3147,7 +3147,16 @@
 	 * EVENT_SCAN_RESULTS is used to indicate when the scan has been
 	 * completed (either successfully or by getting cancelled).
 	 */
-	EVENT_SCAN_STARTED
+	EVENT_SCAN_STARTED,
+
+	/**
+	 * EVENT_AVOID_FREQUENCIES - Received avoid frequency range
+	 *
+	 * This event indicates a set of frequency ranges that should be avoided
+	 * to reduce issues due to interference or internal co-existence
+	 * information in the driver.
+	 */
+	EVENT_AVOID_FREQUENCIES
 };
 
 
@@ -3766,6 +3775,13 @@
 	struct channel_list_changed {
 		enum reg_change_initiator initiator;
 	} channel_list_changed;
+
+	/**
+	 * freq_range - List of frequency ranges
+	 *
+	 * This is used as the data with EVENT_AVOID_FREQUENCIES.
+	 */
+	struct wpa_freq_range_list freq_range;
 };
 
 /**
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 64bdddb..3058cd5 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -78,6 +78,7 @@
 	E2S(DFS_NOP_FINISHED);
 	E2S(SURVEY);
 	E2S(SCAN_STARTED);
+	E2S(AVOID_FREQUENCIES);
 	}
 
 	return "UNKNOWN";
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index b5bf368..646d3f8 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -28,6 +28,7 @@
 #include "common.h"
 #include "eloop.h"
 #include "utils/list.h"
+#include "common/qca-vendor.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "l2_packet/l2_packet.h"
@@ -2716,6 +2717,69 @@
 }
 
 
+static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
+				   const u8 *data, size_t len)
+{
+	u32 i, count;
+	union wpa_event_data event;
+	struct wpa_freq_range *range = NULL;
+	const struct qca_avoid_freq_list *freq_range;
+
+	freq_range = (const struct qca_avoid_freq_list *) data;
+	if (len < sizeof(freq_range->count))
+		return;
+
+	count = freq_range->count;
+	if (len < sizeof(freq_range->count) +
+	    count * sizeof(struct qca_avoid_freq_range)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)",
+			   (unsigned int) len);
+		return;
+	}
+
+	if (count > 0) {
+		range = os_calloc(count, sizeof(struct wpa_freq_range));
+		if (range == NULL)
+			return;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	for (i = 0; i < count; i++) {
+		unsigned int idx = event.freq_range.num;
+		range[idx].min = freq_range->range[i].start_freq;
+		range[idx].max = freq_range->range[i].end_freq;
+		wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u",
+			   range[idx].min, range[idx].max);
+		if (range[idx].min > range[idx].max) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range");
+			continue;
+		}
+		event.freq_range.num++;
+	}
+	event.freq_range.range = range;
+
+	wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event);
+
+	os_free(range);
+}
+
+
+static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
+				     u32 subcmd, u8 *data, size_t len)
+{
+	switch (subcmd) {
+	case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+		qca_nl80211_avoid_freq(drv, data, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore unsupported QCA vendor event %u",
+			   subcmd);
+		break;
+	}
+}
+
+
 static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
 				 struct nlattr **tb)
 {
@@ -2751,6 +2815,9 @@
 	}
 
 	switch (vendor_id) {
+	case OUI_QCA:
+		nl80211_vendor_event_qca(drv, subcmd, data, len);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
 		break;
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index 8edb1ca..6bdd341 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -144,12 +144,13 @@
 	size_t identity_len;
 	int registrar;
 	struct wps_config cfg;
-	const char *pos;
+	const char *pos, *end;
 	const char *phase1;
 	struct wps_context *wps;
 	struct wps_credential new_ap_settings;
 	int res;
 	int nfc = 0;
+	u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN];
 
 	wps = sm->wps;
 	if (wps == NULL) {
@@ -209,6 +210,15 @@
 			cfg.pbc = 1;
 	}
 
+	pos = os_strstr(phase1, "dev_pw_id=");
+	if (pos) {
+		u16 id = atoi(pos + 10);
+		if (id == DEV_PW_NFC_CONNECTION_HANDOVER)
+			nfc = 1;
+		if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER)
+			cfg.dev_pw_id = id;
+	}
+
 	if (cfg.pin == NULL && !cfg.pbc && !nfc) {
 		wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
 			   "configuration data");
@@ -216,9 +226,23 @@
 		return NULL;
 	}
 
-	pos = os_strstr(phase1, "dev_pw_id=");
-	if (pos && cfg.pin)
-		cfg.dev_pw_id = atoi(pos + 10);
+	pos = os_strstr(phase1, " pkhash=");
+	if (pos) {
+		size_t len;
+		pos += 8;
+		end = os_strchr(pos, ' ');
+		if (end)
+			len = end - pos;
+		else
+			len = os_strlen(pos);
+		if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN ||
+		    hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) {
+			wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash");
+			os_free(data);
+			return NULL;
+		}
+		cfg.peer_pubkey_hash = pkhash;
+	}
 
 	res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
 	if (res < 0) {
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 8aabfc0..ca347e7 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -211,6 +211,7 @@
 	if (p2p->go_neg_peer) {
 		p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
 		p2p->go_neg_peer->wps_method = WPS_NOT_READY;
+		p2p->go_neg_peer->oob_pw_id = 0;
 	}
 	p2p->go_neg_peer = NULL;
 
@@ -1292,15 +1293,16 @@
 		int go_intent, const u8 *own_interface_addr,
 		unsigned int force_freq, int persistent_group,
 		const u8 *force_ssid, size_t force_ssid_len,
-		int pd_before_go_neg, unsigned int pref_freq)
+		int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id)
 {
 	struct p2p_device *dev;
 
 	p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR
 		"  GO Intent=%d  Intended Interface Address=" MACSTR
-		" wps_method=%d persistent_group=%d pd_before_go_neg=%d",
+		" wps_method=%d persistent_group=%d pd_before_go_neg=%d "
+		"oob_pw_id=%u",
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-		wps_method, persistent_group, pd_before_go_neg);
+		wps_method, persistent_group, pd_before_go_neg, oob_pw_id);
 
 	dev = p2p_get_device(p2p, peer_addr);
 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
@@ -1384,6 +1386,7 @@
 	}
 
 	dev->wps_method = wps_method;
+	dev->oob_pw_id = oob_pw_id;
 	dev->status = P2P_SC_SUCCESS;
 
 	if (p2p->p2p_scan_running) {
@@ -1403,15 +1406,15 @@
 		  int go_intent, const u8 *own_interface_addr,
 		  unsigned int force_freq, int persistent_group,
 		  const u8 *force_ssid, size_t force_ssid_len,
-		  unsigned int pref_freq)
+		  unsigned int pref_freq, u16 oob_pw_id)
 {
 	struct p2p_device *dev;
 
 	p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR
 		"  GO Intent=%d  Intended Interface Address=" MACSTR
-		" wps_method=%d  persistent_group=%d",
+		" wps_method=%d  persistent_group=%d oob_pw_id=%u",
 		MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-		wps_method, persistent_group);
+		wps_method, persistent_group, oob_pw_id);
 
 	dev = p2p_get_device(p2p, peer_addr);
 	if (dev == NULL) {
@@ -1442,6 +1445,7 @@
 	os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
 
 	dev->wps_method = wps_method;
+	dev->oob_pw_id = oob_pw_id;
 	dev->status = P2P_SC_SUCCESS;
 
 	return 0;
@@ -1593,6 +1597,7 @@
 	p2p->ssid_set = 0;
 	peer->go_neg_req_sent = 0;
 	peer->wps_method = WPS_NOT_READY;
+	peer->oob_pw_id = 0;
 
 	p2p_set_state(p2p, P2P_PROVISIONING);
 	p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
@@ -1757,7 +1762,8 @@
 	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);
+	p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
+			p2p->invite_dev_pw_id);
 }
 
 
@@ -2459,6 +2465,7 @@
 		p2p->go_neg_peer = NULL;
 
 	dev->wps_method = WPS_NOT_READY;
+	dev->oob_pw_id = 0;
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
 	dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
 
@@ -3122,7 +3129,12 @@
 		p2p_connect_send(p2p, p2p->go_neg_peer);
 		return;
 	}
-
+	if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) {
+		p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)");
+		p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+		p2p_set_timeout(p2p, 0, 30000);
+		return;
+	}
 	p2p_set_state(p2p, P2P_CONNECT_LISTEN);
 	p2p_listen_in_find(p2p, 0);
 }
@@ -3266,7 +3278,7 @@
 	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);
+				p2p->invite_go_dev_addr, p2p->invite_dev_pw_id);
 	} else {
 		if (p2p->invite_peer) {
 			p2p_dbg(p2p, "Invitation Request retry limit reached");
@@ -3381,6 +3393,8 @@
 		return "Keypad";
 	case WPS_PBC:
 		return "PBC";
+	case WPS_NFC:
+		return "NFC";
 	}
 
 	return "??";
@@ -4362,3 +4376,219 @@
 	va_end(ap);
 	p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf);
 }
+
+
+#ifdef CONFIG_WPS_NFC
+
+static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p,
+					      int client_freq,
+					      const u8 *go_dev_addr,
+					      const u8 *ssid, size_t ssid_len)
+{
+	struct wpabuf *buf;
+	u8 op_class, channel;
+	enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP;
+
+	buf = wpabuf_alloc(1000);
+	if (buf == NULL)
+		return NULL;
+
+	op_class = p2p->cfg->reg_class;
+	channel = p2p->cfg->channel;
+
+	p2p_buf_add_capability(buf, p2p->dev_capab &
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+	p2p_buf_add_device_info(buf, p2p, NULL);
+
+	if (p2p->num_groups > 0) {
+		role = P2P_GO_IN_A_GROUP;
+		p2p_freq_to_channel(p2p_group_get_freq(p2p->groups[0]),
+				    &op_class, &channel);
+	} else if (client_freq > 0) {
+		role = P2P_CLIENT_IN_A_GROUP;
+		p2p_freq_to_channel(client_freq, &op_class, &channel);
+	}
+
+	p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class,
+				       channel, role);
+
+	if (p2p->num_groups > 0) {
+		/* Limit number of clients to avoid very long message */
+		p2p_buf_add_group_info(p2p->groups[0], buf, 5);
+		p2p_group_buf_add_id(p2p->groups[0], buf);
+	} else if (client_freq > 0 &&
+		   go_dev_addr && !is_zero_ether_addr(go_dev_addr) &&
+		   ssid && ssid_len > 0) {
+		/*
+		 * Add the optional P2P Group ID to indicate in which group this
+		 * device is a P2P Client.
+		 */
+		p2p_buf_add_group_id(buf, go_dev_addr, ssid, ssid_len);
+	}
+
+	return buf;
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len)
+{
+	return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
+				      ssid_len);
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len)
+{
+	return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
+				      ssid_len);
+}
+
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+					struct p2p_nfc_params *params)
+{
+	struct p2p_message msg;
+	struct p2p_device *dev;
+	const u8 *p2p_dev_addr;
+	int freq;
+	enum p2p_role_indication role;
+
+	params->next_step = NO_ACTION;
+
+	if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len,
+				   params->p2p_attr, params->p2p_len, &msg)) {
+		p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC");
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	if (msg.p2p_device_addr)
+		p2p_dev_addr = msg.p2p_device_addr;
+	else if (msg.device_id)
+		p2p_dev_addr = msg.device_id;
+	else {
+		p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id");
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	if (msg.oob_dev_password) {
+		os_memcpy(params->oob_dev_pw, msg.oob_dev_password,
+			  msg.oob_dev_password_len);
+		params->oob_dev_pw_len = msg.oob_dev_password_len;
+	}
+
+	dev = p2p_create_device(p2p, p2p_dev_addr);
+	if (dev == NULL) {
+		p2p_parse_free(&msg);
+		return -1;
+	}
+
+	params->peer = &dev->info;
+
+	os_get_reltime(&dev->last_seen);
+	dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+	p2p_copy_wps_info(p2p, dev, 0, &msg);
+
+	if (!msg.oob_go_neg_channel) {
+		p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included");
+		return -1;
+	}
+
+	if (msg.oob_go_neg_channel[3] == 0 &&
+	    msg.oob_go_neg_channel[4] == 0)
+		freq = 0;
+	else
+		freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3],
+					   msg.oob_go_neg_channel[4]);
+	if (freq < 0) {
+		p2p_dbg(p2p, "Unknown peer OOB GO Neg channel");
+		return -1;
+	}
+	role = msg.oob_go_neg_channel[5];
+
+	if (role == P2P_GO_IN_A_GROUP) {
+		p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq);
+		params->go_freq = freq;
+	} else if (role == P2P_CLIENT_IN_A_GROUP) {
+		p2p_dbg(p2p, "Peer (client) OOB GO operating channel: %u MHz",
+			freq);
+		params->go_freq = freq;
+	} else
+		p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq);
+	dev->oob_go_neg_freq = freq;
+
+	if (!params->sel && role != P2P_GO_IN_A_GROUP) {
+		freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+		if (freq < 0) {
+			p2p_dbg(p2p, "Own listen channel not known");
+			return -1;
+		}
+		p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq);
+		dev->oob_go_neg_freq = freq;
+	}
+
+	if (msg.group_id) {
+		os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN);
+		params->go_ssid_len = msg.group_id_len - ETH_ALEN;
+		os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN,
+			  params->go_ssid_len);
+	}
+
+	p2p_parse_free(&msg);
+
+	if (dev->flags & P2P_DEV_USER_REJECTED) {
+		p2p_dbg(p2p, "Do not report rejected device");
+		return 0;
+	}
+
+	if (!(dev->flags & P2P_DEV_REPORTED)) {
+		p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info,
+				    !(dev->flags & P2P_DEV_REPORTED_ONCE));
+		dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+	}
+
+	if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0)
+		params->next_step = BOTH_GO;
+	else if (role == P2P_GO_IN_A_GROUP)
+		params->next_step = JOIN_GROUP;
+	else if (role == P2P_CLIENT_IN_A_GROUP) {
+		dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
+		params->next_step = PEER_CLIENT;
+	} else if (p2p->num_groups > 0)
+		params->next_step = AUTH_JOIN;
+	else if (params->sel)
+		params->next_step = INIT_GO_NEG;
+	else
+		params->next_step = RESP_GO_NEG;
+
+	return 0;
+}
+
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+				      int go_intent,
+				      const u8 *own_interface_addr)
+{
+
+	p2p->authorized_oob_dev_pw_id = dev_pw_id;
+	if (dev_pw_id == 0) {
+		p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover");
+		return;
+	}
+
+	p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover",
+		dev_pw_id);
+
+	p2p->go_intent = go_intent;
+	os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 2ce6ea6..08e7176 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -9,6 +9,8 @@
 #ifndef P2P_H
 #define P2P_H
 
+#include "wps/wps_defs.h"
+
 /**
  * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
  */
@@ -50,7 +52,7 @@
 };
 
 enum p2p_wps_method {
-	WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC
+	WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC
 };
 
 /**
@@ -706,6 +708,8 @@
 	 *	persistent group (instead of invitation to join an active
 	 *	group)
 	 * @channels: Available operating channels for the group
+	 * @dev_pw_id: Device Password ID for NFC static handover or -1 if not
+	 *	used
 	 * Returns: Status code (P2P_SC_*)
 	 *
 	 * This optional callback can be used to implement persistent reconnect
@@ -727,7 +731,8 @@
 				 const u8 *go_dev_addr, const u8 *ssid,
 				 size_t ssid_len, int *go, u8 *group_bssid,
 				 int *force_freq, int persistent_group,
-				 const struct p2p_channels *channels);
+				 const struct p2p_channels *channels,
+				 int dev_pw_id);
 
 	/**
 	 * invitation_received - Callback on Invitation Request RX
@@ -972,7 +977,7 @@
 		int go_intent, const u8 *own_interface_addr,
 		unsigned int force_freq, int persistent_group,
 		const u8 *force_ssid, size_t force_ssid_len,
-		int pd_before_go_neg, unsigned int pref_freq);
+		int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id);
 
 /**
  * p2p_authorize - Authorize P2P group formation (GO negotiation)
@@ -1000,7 +1005,7 @@
 		  int go_intent, const u8 *own_interface_addr,
 		  unsigned int force_freq, int persistent_group,
 		  const u8 *force_ssid, size_t force_ssid_len,
-		  unsigned int pref_freq);
+		  unsigned int pref_freq, u16 oob_pw_id);
 
 /**
  * p2p_reject - Reject peer device (explicitly block connection attempts)
@@ -1102,12 +1107,14 @@
  * @persistent_group: Whether this is to reinvoke a persistent group
  * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
  *	force_freq == 0)
+ * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover
+ *	case or -1 if not used
  * 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, unsigned int pref_freq);
+	       int persistent_group, unsigned int pref_freq, int dev_pw_id);
 
 /**
  * p2p_presence_req - Request GO presence
@@ -1372,6 +1379,11 @@
 	size_t ssid_len;
 
 	/**
+	 * freq - Operating channel of the group
+	 */
+	int freq;
+
+	/**
 	 * cb_ctx - Context to use with callback functions
 	 */
 	void *cb_ctx;
@@ -1892,4 +1904,41 @@
  */
 const char * p2p_get_state_txt(struct p2p_data *p2p);
 
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len);
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+					   int client_freq,
+					   const u8 *go_dev_addr,
+					   const u8 *ssid, size_t ssid_len);
+
+struct p2p_nfc_params {
+	int sel;
+	const u8 *wsc_attr;
+	size_t wsc_len;
+	const u8 *p2p_attr;
+	size_t p2p_len;
+
+	enum {
+		NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG,
+		BOTH_GO, PEER_CLIENT
+	} next_step;
+	struct p2p_peer_info *peer;
+	u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 +
+		      WPS_OOB_DEVICE_PASSWORD_LEN];
+	size_t oob_dev_pw_len;
+	int go_freq;
+	u8 go_dev_addr[ETH_ALEN];
+	u8 go_ssid[32];
+	size_t go_ssid_len;
+};
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+					struct p2p_nfc_params *params);
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+				      int go_intent,
+				      const u8 *own_interface_addr);
+
 #endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 42c0232..664fade 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -328,6 +328,23 @@
 }
 
 
+void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
+				    u8 oper_class, u8 channel,
+				    enum p2p_role_indication role)
+{
+	/* OOB Group Owner Negotiation Channel */
+	wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL);
+	wpabuf_put_le16(buf, 6);
+	wpabuf_put_data(buf, country, 3);
+	wpabuf_put_u8(buf, oper_class); /* Operating Class */
+	wpabuf_put_u8(buf, channel); /* Channel Number */
+	wpabuf_put_u8(buf, (u8) role); /* Role indication */
+	wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating "
+		   "Class %u Channel %u Role %d",
+		   oper_class, channel, role);
+}
+
+
 static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
 			      const char *val)
 {
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index a887a5e..76436f5 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -103,6 +103,8 @@
 		return DEV_PW_USER_SPECIFIED;
 	case WPS_PBC:
 		return DEV_PW_PUSHBUTTON;
+	case WPS_NFC:
+		return DEV_PW_NFC_CONNECTION_HANDOVER;
 	default:
 		return DEV_PW_DEFAULT;
 	}
@@ -118,6 +120,8 @@
 		return "Keypad";
 	case WPS_PBC:
 		return "PBC";
+	case WPS_NFC:
+		return "NFC";
 	default:
 		return "??";
 	}
@@ -131,6 +135,7 @@
 	u8 *len;
 	u8 group_capab;
 	size_t extra = 0;
+	u16 pw_id;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (p2p->wfd_ie_go_neg)
@@ -172,8 +177,10 @@
 	p2p_buf_update_ie_hdr(buf, len);
 
 	/* WPS IE with Device Password ID attribute */
-	if (p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method),
-			     0) < 0) {
+	pw_id = p2p_wps_method_pw_id(peer->wps_method);
+	if (peer->oob_pw_id)
+		pw_id = peer->oob_pw_id;
+	if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
 		p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request");
 		wpabuf_free(buf);
 		return NULL;
@@ -210,6 +217,8 @@
 	}
 
 	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (dev->oob_go_neg_freq > 0)
+		freq = dev->oob_go_neg_freq;
 	if (freq <= 0) {
 		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
 			MACSTR " to send GO Negotiation Request",
@@ -250,6 +259,7 @@
 	u8 *len;
 	u8 group_capab;
 	size_t extra = 0;
+	u16 pw_id;
 
 	p2p_dbg(p2p, "Building GO Negotiation Response");
 
@@ -312,9 +322,10 @@
 	p2p_buf_update_ie_hdr(buf, len);
 
 	/* WPS IE with Device Password ID attribute */
-	if (p2p_build_wps_ie(p2p, buf,
-			     p2p_wps_method_pw_id(peer ? peer->wps_method :
-						  WPS_NOT_READY), 0) < 0) {
+	pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY);
+	if (peer && peer->oob_pw_id)
+		pw_id = peer->oob_pw_id;
+	if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
 		p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response");
 		wpabuf_free(buf);
 		return NULL;
@@ -605,7 +616,11 @@
 	if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
 		p2p_dbg(p2p, "User has rejected this peer");
 		status = P2P_SC_FAIL_REJECTED_BY_USER;
-	} else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+	} else if (dev == NULL ||
+		   (dev->wps_method == WPS_NOT_READY &&
+		    (p2p->authorized_oob_dev_pw_id == 0 ||
+		     p2p->authorized_oob_dev_pw_id !=
+		     msg.dev_password_id))) {
 		p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
 			MAC2STR(sa));
 		status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
@@ -692,6 +707,28 @@
 			}
 			break;
 		default:
+			if (msg.dev_password_id &&
+			    msg.dev_password_id == dev->oob_pw_id) {
+				p2p_dbg(p2p, "Peer using NFC");
+				if (dev->wps_method != WPS_NFC) {
+					p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+						p2p_wps_method_str(
+							dev->wps_method));
+					status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+					goto fail;
+				}
+				break;
+			}
+#ifdef CONFIG_WPS_NFC
+			if (p2p->authorized_oob_dev_pw_id &&
+			    msg.dev_password_id ==
+			    p2p->authorized_oob_dev_pw_id) {
+				p2p_dbg(p2p, "Using static handover with our device password from NFC Tag");
+				dev->wps_method = WPS_NFC;
+				dev->oob_pw_id = p2p->authorized_oob_dev_pw_id;
+				break;
+			}
+#endif /* CONFIG_WPS_NFC */
 			p2p_dbg(p2p, "Unsupported Device Password ID %d",
 				msg.dev_password_id);
 			status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
@@ -1017,6 +1054,17 @@
 		}
 		break;
 	default:
+		if (msg.dev_password_id &&
+		    msg.dev_password_id == dev->oob_pw_id) {
+			p2p_dbg(p2p, "Peer using NFC");
+			if (dev->wps_method != WPS_NFC) {
+				p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+					p2p_wps_method_str(dev->wps_method));
+				status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+				goto fail;
+			}
+			break;
+		}
 		p2p_dbg(p2p, "Unsupported Device Password ID %d",
 			msg.dev_password_id);
 		status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 92b5583..395ca08 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -154,6 +154,7 @@
 		group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
 	if (group->num_members >= group->cfg->max_clients)
 		group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT;
+	group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION;
 	p2p_buf_add_capability(ie, dev_capab, group_capab);
 }
 
@@ -397,10 +398,38 @@
 #endif /* CONFIG_WIFI_DISPLAY */
 
 
+void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
+			    int max_clients)
+{
+	u8 *group_info;
+	int count = 0;
+	struct p2p_group_member *m;
+
+	p2p_dbg(group->p2p, "* P2P Group Info");
+	group_info = wpabuf_put(buf, 0);
+	wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO);
+	wpabuf_put_le16(buf, 0); /* Length to be filled */
+	for (m = group->members; m; m = m->next) {
+		p2p_client_info(buf, m);
+		count++;
+		if (max_clients >= 0 && count >= max_clients)
+			break;
+	}
+	WPA_PUT_LE16(group_info + 1,
+		     (u8 *) wpabuf_put(buf, 0) - group_info - 3);
+}
+
+
+void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf)
+{
+	p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid,
+			     group->cfg->ssid_len);
+}
+
+
 static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
 {
 	struct wpabuf *p2p_subelems, *ie;
-	struct p2p_group_member *m;
 
 	p2p_subelems = wpabuf_alloc(500);
 	if (p2p_subelems == NULL)
@@ -413,17 +442,8 @@
 	p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL);
 
 	/* P2P Group Info: Only when at least one P2P Client is connected */
-	if (group->members) {
-		u8 *group_info;
-		group_info = wpabuf_put(p2p_subelems, 0);
-		wpabuf_put_u8(p2p_subelems, P2P_ATTR_GROUP_INFO);
-		wpabuf_put_le16(p2p_subelems, 0); /* Length to be filled */
-		for (m = group->members; m; m = m->next)
-			p2p_client_info(p2p_subelems, m);
-		WPA_PUT_LE16(group_info + 1,
-			     (u8 *) wpabuf_put(p2p_subelems, 0) - group_info -
-			     3);
-	}
+	if (group->members)
+		p2p_buf_add_group_info(group, p2p_subelems, -1);
 
 	ie = p2p_group_encaps_probe_resp(p2p_subelems);
 	wpabuf_free(p2p_subelems);
@@ -987,3 +1007,9 @@
 	group->beacon_update = 1;
 	p2p_group_update_ies(group);
 }
+
+
+int p2p_group_get_freq(struct p2p_group *group)
+{
+	return group->cfg->freq;
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 3e105eb..6ebaa84 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -12,6 +12,8 @@
 #include "utils/list.h"
 #include "p2p.h"
 
+enum p2p_role_indication;
+
 enum p2p_go_state {
 	UNKNOWN_GO,
 	LOCAL_GO,
@@ -25,7 +27,9 @@
 	struct dl_list list;
 	struct os_reltime last_seen;
 	int listen_freq;
+	int oob_go_neg_freq;
 	enum p2p_wps_method wps_method;
+	u16 oob_pw_id;
 
 	struct p2p_peer_info info;
 
@@ -239,6 +243,7 @@
 
 	const u8 *invite_go_dev_addr;
 	u8 invite_go_dev_addr_buf[ETH_ALEN];
+	int invite_dev_pw_id;
 
 	/**
 	 * sd_peer - Pointer to Service Discovery peer
@@ -462,6 +467,8 @@
 	struct wpabuf *wfd_assoc_bssid;
 	struct wpabuf *wfd_coupled_sink_info;
 #endif /* CONFIG_WIFI_DISPLAY */
+
+	u16 authorized_oob_dev_pw_id;
 };
 
 /**
@@ -503,6 +510,8 @@
 
 	const u8 *minor_reason_code;
 
+	const u8 *oob_go_neg_channel;
+
 	/* P2P Device Info */
 	const u8 *p2p_device_info;
 	size_t p2p_device_info_len;
@@ -514,6 +523,7 @@
 
 	/* WPS IE */
 	u16 dev_password_id;
+	int dev_password_id_present;
 	u16 wps_config_methods;
 	const u8 *wps_pri_dev_type;
 	const u8 *wps_sec_dev_type_list;
@@ -528,6 +538,8 @@
 	size_t model_number_len;
 	const u8 *serial_number;
 	size_t serial_number_len;
+	const u8 *oob_dev_password;
+	size_t oob_dev_password_len;
 
 	/* DS Parameter Set IE */
 	const u8 *ds_params;
@@ -578,6 +590,8 @@
 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);
+int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p,
+			   size_t p2p_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,
@@ -602,6 +616,10 @@
 void p2p_group_update_ies(struct p2p_group *group);
 void p2p_group_force_beacon_update_ies(struct p2p_group *group);
 struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g);
+void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
+			    int max_clients);
+void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf);
+int p2p_group_get_freq(struct p2p_group *group);
 
 
 void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
@@ -633,6 +651,9 @@
 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_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
+				    u8 oper_class, u8 channel,
+				    enum p2p_role_indication role);
 int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
 		     int all_attr);
 
@@ -680,7 +701,7 @@
 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);
+		    const u8 *go_dev_addr, int dev_pw_id);
 void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
 void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
 
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 2734386..98cfb33 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -16,7 +16,8 @@
 
 static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
 						struct p2p_device *peer,
-						const u8 *go_dev_addr)
+						const u8 *go_dev_addr,
+						int dev_pw_id)
 {
 	struct wpabuf *buf;
 	u8 *len;
@@ -85,6 +86,11 @@
 		wpabuf_put_buf(buf, wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (dev_pw_id >= 0) {
+		/* WSC IE in Invitation Request for NFC static handover */
+		p2p_build_wps_ie(p2p, buf, dev_pw_id, 0);
+	}
+
 	return buf;
 }
 
@@ -228,7 +234,8 @@
 		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, &intersection);
+			&go, group_bssid, &op_freq, persistent, &intersection,
+			msg.dev_password_id_present ? msg.dev_password_id : -1);
 	}
 
 	if (op_freq) {
@@ -450,12 +457,14 @@
 
 
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
-		    const u8 *go_dev_addr)
+		    const u8 *go_dev_addr, int dev_pw_id)
 {
 	struct wpabuf *req;
 	int freq;
 
 	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+	if (freq <= 0)
+		freq = dev->oob_go_neg_freq;
 	if (freq <= 0) {
 		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
 			MACSTR " to send Invitation Request",
@@ -463,7 +472,7 @@
 		return -1;
 	}
 
-	req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
+	req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id);
 	if (req == NULL)
 		return -1;
 	if (p2p->state != P2P_IDLE)
@@ -528,7 +537,7 @@
 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, unsigned int pref_freq)
+	       int persistent_group, unsigned int pref_freq, int dev_pw_id)
 {
 	struct p2p_device *dev;
 
@@ -546,9 +555,15 @@
 		p2p->invite_go_dev_addr = NULL;
 	wpa_hexdump_ascii(MSG_DEBUG, "Invitation for SSID",
 			  ssid, ssid_len);
+	if (dev_pw_id >= 0) {
+		p2p_dbg(p2p, "Invitation to use Device Password ID %d",
+			dev_pw_id);
+	}
+	p2p->invite_dev_pw_id = dev_pw_id;
 
 	dev = p2p_get_device(p2p, peer);
-	if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) {
+	if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 &&
+			    dev->oob_go_neg_freq <= 0)) {
 		p2p_dbg(p2p, "Cannot invite unknown P2P Device " MACSTR,
 			MAC2STR(peer));
 		return -1;
@@ -586,5 +601,5 @@
 	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);
+	return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id);
 }
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index 097a31d..d6144a0 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -268,6 +268,19 @@
 		wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u",
 			   *msg->minor_reason_code);
 		break;
+	case P2P_ATTR_OOB_GO_NEG_CHANNEL:
+		if (len < 6) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short OOB GO Neg "
+				   "Channel attribute (length %d)", len);
+			return -1;
+		}
+		msg->oob_go_neg_channel = data;
+		wpa_printf(MSG_DEBUG, "P2P: * OOB GO Neg Channel: "
+			   "Country %c%c(0x%02x) Operating Class %d "
+			   "Channel Number %d Role %d",
+			   data[0], data[1], data[2], data[3], data[4],
+			   data[5]);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
 			   "(length %d)", id, len);
@@ -340,6 +353,7 @@
 		msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id);
 		wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d",
 			   msg->dev_password_id);
+		msg->dev_password_id_present = 1;
 	}
 	if (attr.primary_dev_type) {
 		char devtype[WPS_DEV_TYPE_BUFSIZE];
@@ -367,6 +381,9 @@
 	msg->serial_number = attr.serial_number;
 	msg->serial_number_len = attr.serial_number_len;
 
+	msg->oob_dev_password = attr.oob_dev_password;
+	msg->oob_dev_password_len = attr.oob_dev_password_len;
+
 	return 0;
 }
 
@@ -450,6 +467,33 @@
 }
 
 
+int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p,
+			   size_t p2p_len, struct p2p_message *msg)
+{
+	os_memset(msg, 0, sizeof(*msg));
+
+	msg->wps_attributes = wpabuf_alloc_copy(wsc, wsc_len);
+	if (msg->wps_attributes &&
+	    p2p_parse_wps_ie(msg->wps_attributes, msg)) {
+		p2p_parse_free(msg);
+		return -1;
+	}
+
+	msg->p2p_attributes = wpabuf_alloc_copy(p2p, p2p_len);
+	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_free - Free temporary data from P2P parsing
  * @msg: Parsed attributes
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index a294730..4474c3b 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -379,6 +379,8 @@
 	struct wpa_ptk *ptk;
 	u8 buf[8];
 	int res;
+	u8 *kde, *kde_buf = NULL;
+	size_t kde_len;
 
 	if (wpa_sm_get_network_ctx(sm) == NULL) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info "
@@ -435,15 +437,39 @@
 	os_memcpy(ptk->u.auth.rx_mic_key, buf, 8);
 	sm->tptk_set = 1;
 
+	kde = sm->assoc_wpa_ie;
+	kde_len = sm->assoc_wpa_ie_len;
+
+#ifdef CONFIG_P2P
+	if (sm->p2p) {
+		kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1);
+		if (kde_buf) {
+			u8 *pos;
+			wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE "
+				   "into EAPOL-Key 2/4");
+			os_memcpy(kde_buf, kde, kde_len);
+			kde = kde_buf;
+			pos = kde + kde_len;
+			*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+			*pos++ = RSN_SELECTOR_LEN + 1;
+			RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ);
+			pos += RSN_SELECTOR_LEN;
+			*pos++ = 0x01;
+			kde_len = pos - kde;
+		}
+	}
+#endif /* CONFIG_P2P */
+
 	if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
-				       sm->assoc_wpa_ie, sm->assoc_wpa_ie_len,
-				       ptk))
+				       kde, kde_len, ptk))
 		goto failed;
 
+	os_free(kde_buf);
 	os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN);
 	return;
 
 failed:
+	os_free(kde_buf);
 	wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
 }
 
@@ -1090,6 +1116,14 @@
 		goto failed;
 	}
 
+#ifdef CONFIG_P2P
+	if (ie.ip_addr_alloc) {
+		os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4);
+		wpa_hexdump(MSG_DEBUG, "P2P: IP address info",
+			    sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr));
+	}
+#endif /* CONFIG_P2P */
+
 	if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
 				       NULL, 0, &sm->ptk)) {
 		goto failed;
@@ -2086,6 +2120,10 @@
 #ifdef CONFIG_TDLS
 	wpa_tdls_assoc(sm);
 #endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_P2P
+	os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr));
+#endif /* CONFIG_P2P */
 }
 
 
@@ -2209,6 +2247,7 @@
 		} else
 			sm->ssid_len = 0;
 		sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
+		sm->p2p = config->p2p;
 	} else {
 		sm->network_ctx = NULL;
 		sm->peerkey_enabled = 0;
@@ -2218,6 +2257,7 @@
 		sm->eap_conf_ctx = NULL;
 		sm->ssid_len = 0;
 		sm->wpa_ptk_rekey = 0;
+		sm->p2p = 0;
 	}
 }
 
@@ -2731,3 +2771,16 @@
 	return 1;
 }
 #endif /* CONFIG_PEERKEY */
+
+
+#ifdef CONFIG_P2P
+
+int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf)
+{
+	if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0)
+		return -1;
+	os_memcpy(buf, sm->p2p_ip_addr, 3 * 4);
+	return 0;
+}
+
+#endif /* CONFIG_P2P */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 9884ce1..20b3f62 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -95,6 +95,7 @@
 	const u8 *ssid;
 	size_t ssid_len;
 	int wpa_ptk_rekey;
+	int p2p;
 };
 
 #ifndef CONFIG_NO_WPA
@@ -145,6 +146,8 @@
 
 void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
 
+int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf);
+
 #else /* CONFIG_NO_WPA */
 
 static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index cad6c8d..75cfb47 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -58,6 +58,7 @@
 	u8 ssid[32];
 	size_t ssid_len;
 	int wpa_ptk_rekey;
+	int p2p;
 
 	u8 own_addr[ETH_ALEN];
 	const char *ifname;
@@ -122,6 +123,10 @@
 	u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */
 	size_t assoc_resp_ies_len;
 #endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_P2P
+	u8 p2p_ip_addr[3 * 4];
+#endif /* CONFIG_P2P */
 };
 
 
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index ab8d104..e58bdc4 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -345,6 +345,25 @@
 	}
 #endif /* CONFIG_IEEE80211W */
 
+#ifdef CONFIG_P2P
+	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
+		ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
+			    ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+
+	if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
+	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+		ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+		wpa_hexdump(MSG_DEBUG,
+			    "WPA: IP Address Allocation in EAPOL-Key",
+			    ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+		return 0;
+	}
+#endif /* CONFIG_P2P */
+
 	return 0;
 }
 
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
index 0375767..82b6fa3 100644
--- a/src/rsn_supp/wpa_ie.h
+++ b/src/rsn_supp/wpa_ie.h
@@ -59,6 +59,10 @@
 	size_t supp_oper_classes_len;
 	u8 qosinfo;
 	u16 aid;
+#ifdef CONFIG_P2P
+	const u8 *ip_addr_req;
+	const u8 *ip_addr_alloc;
+#endif /* CONFIG_P2P */
 };
 
 int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index 96685d2..2b35064 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -30,6 +30,7 @@
 };
 
 static char wifi_handover_type[] = "application/vnd.wfa.wsc";
+static char p2p_handover_type[] = "application/vnd.wfa.p2p";
 
 static int ndef_parse_record(const u8 *data, u32 size,
 			     struct ndef_record *record)
@@ -170,85 +171,26 @@
 }
 
 
-struct wpabuf * ndef_build_wifi_hc(int begin)
+static int p2p_filter(struct ndef_record *record)
 {
-	struct wpabuf *hc, *carrier;
-
-	carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type));
-	if (carrier == NULL)
-		return NULL;
-	wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */
-	wpabuf_put_u8(carrier, os_strlen(wifi_handover_type));
-	wpabuf_put_str(carrier, wifi_handover_type);
-
-	hc = ndef_build_record((begin ? FLAG_MESSAGE_BEGIN : 0) |
-			       FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2,
-			       "0", 1, carrier);
-	wpabuf_free(carrier);
-
-	return hc;
+	if (record->type_length != os_strlen(p2p_handover_type))
+		return 0;
+	if (os_memcmp(record->type, p2p_handover_type,
+		      os_strlen(p2p_handover_type)) != 0)
+		return 0;
+	return 1;
 }
 
 
-struct wpabuf * ndef_build_wifi_hr(void)
+struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf)
 {
-	struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr;
-	struct wpabuf *hc;
+	return ndef_parse_records(buf, p2p_filter);
+}
 
-	rn = wpabuf_alloc(2);
-	if (rn == NULL)
-		return NULL;
-	wpabuf_put_be16(rn, os_random() & 0xffff);
 
-	cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2,
-			       NULL, 0, rn);
-	wpabuf_free(rn);
-
-	if (cr == NULL)
-		return NULL;
-
-	ac_payload = wpabuf_alloc(4);
-	if (ac_payload == NULL) {
-		wpabuf_free(cr);
-		return NULL;
-	}
-	wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */
-	wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */
-	wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */
-	wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */
-
-	ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2,
-			       NULL, 0, ac_payload);
-	wpabuf_free(ac_payload);
-	if (ac == NULL) {
-		wpabuf_free(cr);
-		return NULL;
-	}
-
-	hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac));
-	if (hr_payload == NULL) {
-		wpabuf_free(cr);
-		wpabuf_free(ac);
-		return NULL;
-	}
-
-	wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */
-	wpabuf_put_buf(hr_payload, cr);
-	wpabuf_put_buf(hr_payload, ac);
-	wpabuf_free(cr);
-	wpabuf_free(ac);
-
-	hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2,
-			       NULL, 0, hr_payload);
-	wpabuf_free(hr_payload);
-	if (hr == NULL)
-		return NULL;
-
-	hc = ndef_build_wifi_hc(0);
-	if (hc == NULL) {
-		wpabuf_free(hr);
-		return NULL;
-	}
-
-	return wpabuf_concat(hr, hc);
+struct wpabuf * ndef_build_p2p(const struct wpabuf *buf)
+{
+	return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
+				 FLAG_TNF_RFC2046, p2p_handover_type,
+				 os_strlen(p2p_handover_type), NULL, 0, buf);
 }
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 22d7eea..3d019f1 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -18,6 +18,7 @@
 #ifdef CONFIG_WPS_TESTING
 int wps_version_number = 0x20;
 int wps_testing_dummy_cred = 0;
+int wps_corrupt_pkhash = 0;
 #endif /* CONFIG_WPS_TESTING */
 
 
@@ -58,6 +59,10 @@
 	}
 
 #ifdef CONFIG_WPS_NFC
+	if (cfg->pin == NULL &&
+	    cfg->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+		data->dev_pw_id = cfg->dev_pw_id;
+
 	if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) {
 		/* Keep AP PIN as alternative Device Password */
 		data->alt_dev_pw_id = data->dev_pw_id;
@@ -133,6 +138,12 @@
 	data->use_psk_key = cfg->use_psk_key;
 	data->pbc_in_m1 = cfg->pbc_in_m1;
 
+	if (cfg->peer_pubkey_hash) {
+		os_memcpy(data->peer_pubkey_hash, cfg->peer_pubkey_hash,
+			  WPS_OOB_PUBKEY_HASH_LEN);
+		data->peer_pubkey_hash_set = 1;
+	}
+
 	return data;
 }
 
@@ -168,7 +179,6 @@
 	wps_device_data_free(&data->peer_dev);
 	os_free(data->new_ap_settings);
 	dh5_free(data->dh_ctx);
-	os_free(data->nfc_pw_token);
 	os_free(data);
 }
 
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 15137a8..6ccce1a 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -183,6 +183,11 @@
 	 * PBC with the AP.
 	 */
 	int pbc_in_m1;
+
+	/**
+	 * peer_pubkey_hash - Peer public key hash or %NULL if not known
+	 */
+	const u8 *peer_pubkey_hash;
 };
 
 struct wps_data * wps_init(const struct wps_config *cfg);
@@ -801,7 +806,8 @@
 			    struct wps_credential *cred);
 int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
 				   const u8 *pubkey_hash, u16 pw_id,
-				   const u8 *dev_pw, size_t dev_pw_len);
+				   const u8 *dev_pw, size_t dev_pw_len,
+				   int pk_hash_provided_oob);
 int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
 					 const u8 *oob_dev_pw,
 					 size_t oob_dev_pw_len);
@@ -815,7 +821,8 @@
 int wps_pin_str_valid(const char *pin);
 void wps_free_pending_msgs(struct upnp_pending_message *msgs);
 
-struct wpabuf * wps_get_oob_cred(struct wps_context *wps);
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+				 int channel);
 int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr);
 int wps_attr_text(struct wpabuf *data, char *buf, char *end);
 const char * wps_ei_str(enum wps_error_indication ei);
@@ -839,6 +846,9 @@
 					      struct wps_credential *cred);
 struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid,
 					const u8 *addr);
+struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er,
+					struct wps_context *wps, const u8 *uuid,
+					const u8 *addr, struct wpabuf *pubkey);
 
 int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
 char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
@@ -850,15 +860,27 @@
 				       const struct wpabuf *dev_pw);
 struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey,
 				    struct wpabuf *dev_pw);
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey);
 struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
 				  struct wpabuf **privkey,
 				  struct wpabuf **dev_pw);
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+					   struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+					   struct wpabuf *nfc_dh_pubkey,
+					   const u8 *bssid, int freq);
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+					       struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+					       int nfc_dev_pw_id,
+					       struct wpabuf *nfc_dh_pubkey,
+					       struct wpabuf *nfc_dev_pw);
 
 /* ndef.c */
 struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
 struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
-struct wpabuf * ndef_build_wifi_hc(int begin);
-struct wpabuf * ndef_build_wifi_hr(void);
+struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf);
+struct wpabuf * ndef_build_p2p(const struct wpabuf *buf);
 
 #ifdef CONFIG_WPS_STRICT
 int wps_validate_beacon(const struct wpabuf *wps_ie);
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
index 336246e..62d0feb 100644
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -38,8 +38,15 @@
 		wps->wps->dh_ctx = NULL;
 		pubkey = wpabuf_dup(wps->wps->dh_pubkey);
 #ifdef CONFIG_WPS_NFC
-	} else if (wps->dev_pw_id >= 0x10 && wps->wps->ap &&
-		   wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) {
+	} else if ((wps->dev_pw_id >= 0x10 ||
+		    wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) &&
+		   (wps->wps->ap ||
+		    (wps->wps->ap_nfc_dh_pubkey &&
+		     wps->wps->ap_nfc_dev_pw_id ==
+		     DEV_PW_NFC_CONNECTION_HANDOVER &&
+		     wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)) &&
+		   (wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id ||
+		    wps->wps->ap_nfc_dh_pubkey)) {
 		wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys");
 		if (wps->wps->ap_nfc_dh_privkey == NULL) {
 			wpa_printf(MSG_DEBUG,
@@ -392,6 +399,14 @@
 	addr[0] = wpabuf_head(pubkey);
 	hash_len = wpabuf_len(pubkey);
 	sha256_vector(1, addr, &hash_len, pubkey_hash);
+#ifdef CONFIG_WPS_TESTING
+	if (wps_corrupt_pkhash) {
+		wpa_hexdump(MSG_DEBUG, "WPS: Real Public Key Hash",
+			    pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+		wpa_printf(MSG_INFO, "WPS: Testing - corrupt public key hash");
+		pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN - 2]++;
+	}
+#endif /* CONFIG_WPS_TESTING */
 
 	wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
 	wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len);
@@ -399,9 +414,11 @@
 		    pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
 	wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
 	wpabuf_put_be16(msg, dev_pw_id);
-	wpa_hexdump_key(MSG_DEBUG, "WPS: OOB Device Password",
-			dev_pw, dev_pw_len);
-	wpabuf_put_data(msg, dev_pw, dev_pw_len);
+	if (dev_pw) {
+		wpa_hexdump_key(MSG_DEBUG, "WPS: OOB Device Password",
+				dev_pw, dev_pw_len);
+		wpabuf_put_data(msg, dev_pw, dev_pw_len);
+	}
 
 	return 0;
 }
@@ -449,3 +466,23 @@
 	wpabuf_put_data(msg, addr, ETH_ALEN);
 	return 0;
 }
+
+
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * RF Bands (%x)", rf_bands);
+	wpabuf_put_be16(msg, ATTR_RF_BANDS);
+	wpabuf_put_be16(msg, 1);
+	wpabuf_put_u8(msg, rf_bands);
+	return 0;
+}
+
+
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * AP Channel (%u)", ap_channel);
+	wpabuf_put_be16(msg, ATTR_AP_CHANNEL);
+	wpabuf_put_be16(msg, 2);
+	wpabuf_put_be16(msg, ap_channel);
+	return 0;
+}
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index 3999b1b..f4e2e38 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -263,10 +263,13 @@
 		attr->dev_password_id = pos;
 		break;
 	case ATTR_OOB_DEVICE_PASSWORD:
-		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
-		    WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+		if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
 		    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
-		    WPS_OOB_DEVICE_PASSWORD_LEN) {
+		    WPS_OOB_DEVICE_PASSWORD_LEN ||
+		    (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+		     WPS_OOB_DEVICE_PASSWORD_MIN_LEN &&
+		     WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) !=
+		     DEV_PW_NFC_CONNECTION_HANDOVER)) {
 			wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
 				   "Password length %u", len);
 			return -1;
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 4b431ad..abf3a4f 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -9,6 +9,8 @@
 #include "includes.h"
 
 #include "common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_common.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
 #include "crypto/dh_group5.h"
@@ -16,6 +18,7 @@
 #include "crypto/sha256.h"
 #include "crypto/random.h"
 #include "wps_i.h"
+#include "wps_dev_attr.h"
 
 
 void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
@@ -349,7 +352,8 @@
 
 #ifdef CONFIG_WPS_OOB
 
-struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+				 int channel)
 {
 	struct wps_data data;
 	struct wpabuf *plain;
@@ -365,8 +369,10 @@
 	data.wps = wps;
 	data.auth_type = wps->auth_types;
 	data.encr_type = wps->encr_types;
-	if (wps_build_version(plain) ||
-	    wps_build_cred(&data, plain) ||
+	if (wps_build_cred(&data, plain) ||
+	    (rf_band && wps_build_rf_bands_attr(plain, rf_band)) ||
+	    (channel && wps_build_ap_channel(plain, channel)) ||
+	    wps_build_mac_addr(plain, wps->dev.mac_addr) ||
 	    wps_build_wfa_ext(plain, 0, NULL, 0)) {
 		os_free(data.new_psk);
 		wpabuf_free(plain);
@@ -412,8 +418,7 @@
 	if (data == NULL)
 		return NULL;
 
-	if (wps_build_version(data) ||
-	    wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
+	if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
 				 wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
 	    wps_build_wfa_ext(data, 0, NULL, 0)) {
 		wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
@@ -636,12 +641,36 @@
 }
 
 
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey)
+{
+	struct wpabuf *priv = NULL, *pub = NULL;
+	void *dh_ctx;
+
+	dh_ctx = dh5_init(&priv, &pub);
+	if (dh_ctx == NULL)
+		return -1;
+	pub = wpabuf_zeropad(pub, 192);
+	if (pub == NULL) {
+		wpabuf_free(priv);
+		return -1;
+	}
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub);
+	dh5_free(dh_ctx);
+
+	wpabuf_free(*pubkey);
+	*pubkey = pub;
+	wpabuf_free(*privkey);
+	*privkey = priv;
+
+	return 0;
+}
+
+
 struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
 				  struct wpabuf **privkey,
 				  struct wpabuf **dev_pw)
 {
-	struct wpabuf *priv = NULL, *pub = NULL, *pw;
-	void *dh_ctx;
+	struct wpabuf *pw;
 	u16 val;
 
 	pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
@@ -655,22 +684,223 @@
 		return NULL;
 	}
 
-	dh_ctx = dh5_init(&priv, &pub);
-	if (dh_ctx == NULL) {
+	if (wps_nfc_gen_dh(pubkey, privkey) < 0) {
 		wpabuf_free(pw);
 		return NULL;
 	}
-	dh5_free(dh_ctx);
 
 	*id = 0x10 + val % 0xfff0;
-	wpabuf_free(*pubkey);
-	*pubkey = pub;
-	wpabuf_free(*privkey);
-	*privkey = priv;
 	wpabuf_free(*dev_pw);
 	*dev_pw = pw;
 
 	return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw);
 }
 
+
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+					   struct wpabuf *nfc_dh_pubkey)
+{
+	struct wpabuf *msg;
+	void *len;
+
+	if (ctx == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+		   "handover request");
+
+	if (nfc_dh_pubkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+			   "configured");
+		return NULL;
+	}
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return msg;
+	len = wpabuf_put(msg, 2);
+
+	if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+				 nfc_dh_pubkey, NULL, 0) ||
+	    wps_build_uuid_e(msg, ctx->uuid) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+	return msg;
+}
+
+
+static int wps_build_ssid(struct wpabuf *msg, struct wps_context *wps)
+{
+	wpa_printf(MSG_DEBUG, "WPS:  * SSID");
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID in Connection Handover Select",
+			  wps->ssid, wps->ssid_len);
+	wpabuf_put_be16(msg, ATTR_SSID);
+	wpabuf_put_be16(msg, wps->ssid_len);
+	wpabuf_put_data(msg, wps->ssid, wps->ssid_len);
+	return 0;
+}
+
+
+static int wps_build_ap_freq(struct wpabuf *msg, int freq)
+{
+	enum hostapd_hw_mode mode;
+	u8 channel, rf_band;
+	u16 ap_channel;
+
+	if (freq <= 0)
+		return 0;
+
+	mode = ieee80211_freq_to_chan(freq, &channel);
+	if (mode == NUM_HOSTAPD_MODES)
+		return 0; /* Unknown channel */
+
+	if (mode == HOSTAPD_MODE_IEEE80211G || mode == HOSTAPD_MODE_IEEE80211B)
+		rf_band = WPS_RF_24GHZ;
+	else if (mode == HOSTAPD_MODE_IEEE80211A)
+		rf_band = WPS_RF_50GHZ;
+	else
+		return 0; /* Unknown band */
+	ap_channel = channel;
+
+	if (wps_build_rf_bands_attr(msg, rf_band) ||
+	    wps_build_ap_channel(msg, ap_channel))
+		return -1;
+
+	return 0;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+					   struct wpabuf *nfc_dh_pubkey,
+					   const u8 *bssid, int freq)
+{
+	struct wpabuf *msg;
+	void *len;
+
+	if (ctx == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+		   "handover select");
+
+	if (nfc_dh_pubkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+			   "configured");
+		return NULL;
+	}
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return msg;
+	len = wpabuf_put(msg, 2);
+
+	if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+				 nfc_dh_pubkey, NULL, 0) ||
+	    wps_build_ssid(msg, ctx) ||
+	    wps_build_ap_freq(msg, freq) ||
+	    (bssid && wps_build_mac_addr(msg, bssid)) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+	return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+					       struct wpabuf *nfc_dh_pubkey)
+{
+	struct wpabuf *msg;
+
+	if (ctx == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+		   "handover request (P2P)");
+
+	if (nfc_dh_pubkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No NFC DH Public Key configured");
+		return NULL;
+	}
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return msg;
+
+	if (wps_build_manufacturer(&ctx->dev, msg) ||
+	    wps_build_model_name(&ctx->dev, msg) ||
+	    wps_build_model_number(&ctx->dev, msg) ||
+	    wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+				 nfc_dh_pubkey, NULL, 0) ||
+	    wps_build_rf_bands(&ctx->dev, msg, 0) ||
+	    wps_build_serial_number(&ctx->dev, msg) ||
+	    wps_build_uuid_e(msg, ctx->uuid) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+					       int nfc_dev_pw_id,
+					       struct wpabuf *nfc_dh_pubkey,
+					       struct wpabuf *nfc_dev_pw)
+{
+	struct wpabuf *msg;
+	const u8 *dev_pw;
+	size_t dev_pw_len;
+
+	if (ctx == NULL)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+		   "handover select (P2P)");
+
+	if (nfc_dh_pubkey == NULL ||
+	    (nfc_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+	     nfc_dev_pw == NULL)) {
+		wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+			   "configured");
+		return NULL;
+	}
+
+	msg = wpabuf_alloc(1000);
+	if (msg == NULL)
+		return msg;
+
+	if (nfc_dev_pw) {
+		dev_pw = wpabuf_head(nfc_dev_pw);
+		dev_pw_len = wpabuf_len(nfc_dev_pw);
+	} else {
+		dev_pw = NULL;
+		dev_pw_len = 0;
+	}
+
+	if (wps_build_manufacturer(&ctx->dev, msg) ||
+	    wps_build_model_name(&ctx->dev, msg) ||
+	    wps_build_model_number(&ctx->dev, msg) ||
+	    wps_build_oob_dev_pw(msg, nfc_dev_pw_id, nfc_dh_pubkey,
+				 dev_pw, dev_pw_len) ||
+	    wps_build_rf_bands(&ctx->dev, msg, 0) ||
+	    wps_build_serial_number(&ctx->dev, msg) ||
+	    wps_build_uuid_e(msg, ctx->uuid) ||
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
 #endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index 3421ac5..6f8a49f 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -13,6 +13,7 @@
 
 extern int wps_version_number;
 extern int wps_testing_dummy_cred;
+extern int wps_corrupt_pkhash;
 #define WPS_VERSION wps_version_number
 
 #else /* CONFIG_WPS_TESTING */
@@ -155,7 +156,8 @@
 	DEV_PW_MACHINE_SPECIFIED = 0x0002,
 	DEV_PW_REKEY = 0x0003,
 	DEV_PW_PUSHBUTTON = 0x0004,
-	DEV_PW_REGISTRAR_SPECIFIED = 0x0005
+	DEV_PW_REGISTRAR_SPECIFIED = 0x0005,
+	DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007
 };
 
 /* Message Type */
@@ -215,7 +217,9 @@
 	WPS_CFG_SETUP_LOCKED = 15,
 	WPS_CFG_MSG_TIMEOUT = 16,
 	WPS_CFG_REG_SESS_TIMEOUT = 17,
-	WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
+	WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18,
+	WPS_CFG_60G_CHAN_NOT_SUPPORTED = 19,
+	WPS_CFG_PUBLIC_KEY_HASH_MISMATCH = 20
 };
 
 /* Vendor specific Error Indication for WPS event messages */
diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c
index fe736f3..0d01211 100644
--- a/src/wps/wps_dev_attr.c
+++ b/src/wps/wps_dev_attr.c
@@ -85,8 +85,7 @@
 }
 
 
-static int wps_build_serial_number(struct wps_device_data *dev,
-				   struct wpabuf *msg)
+int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg)
 {
 	size_t len;
 	wpa_printf(MSG_DEBUG, "WPS:  * Serial Number");
@@ -220,11 +219,7 @@
 int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg,
 		       u8 rf_band)
 {
-	wpa_printf(MSG_DEBUG, "WPS:  * RF Bands (%x)", dev->rf_bands);
-	wpabuf_put_be16(msg, ATTR_RF_BANDS);
-	wpabuf_put_be16(msg, 1);
-	wpabuf_put_u8(msg, rf_band ? rf_band : dev->rf_bands);
-	return 0;
+	return wps_build_rf_bands_attr(msg, rf_band ? rf_band : dev->rf_bands);
 }
 
 
diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h
index f0169a7..c9034ad 100644
--- a/src/wps/wps_dev_attr.h
+++ b/src/wps/wps_dev_attr.h
@@ -14,6 +14,7 @@
 int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 7b86ff7..9d48ca5 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -514,6 +514,24 @@
 		return -1;
 	}
 
+	if (wps->peer_pubkey_hash_set) {
+		u8 hash[WPS_HASH_LEN];
+		sha256_vector(1, &pk, &pk_len, hash);
+		if (os_memcmp(hash, wps->peer_pubkey_hash,
+			      WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+			wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch");
+			wpa_hexdump(MSG_DEBUG, "WPS: Received public key",
+				    pk, pk_len);
+			wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key "
+				    "hash", hash, WPS_OOB_PUBKEY_HASH_LEN);
+			wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash",
+				    wps->peer_pubkey_hash,
+				    WPS_OOB_PUBKEY_HASH_LEN);
+			wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+			return -1;
+		}
+	}
+
 	wpabuf_free(wps->dh_pubkey_r);
 	wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
 	if (wps->dh_pubkey_r == NULL)
@@ -929,6 +947,38 @@
 		return WPS_CONTINUE;
 	}
 
+#ifdef CONFIG_WPS_NFC
+	if (wps->peer_pubkey_hash_set) {
+		struct wpabuf *decrypted;
+		struct wps_parse_attr eattr;
+
+		decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+						      attr->encr_settings_len);
+		if (decrypted == NULL) {
+			wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt "
+				   "Encrypted Settings attribute");
+			wps->state = SEND_WSC_NACK;
+			return WPS_CONTINUE;
+		}
+
+		wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted "
+			   "Settings attribute");
+		if (wps_parse_msg(decrypted, &eattr) < 0 ||
+		    wps_process_key_wrap_auth(wps, decrypted,
+					      eattr.key_wrap_auth) ||
+		    wps_process_creds(wps, eattr.cred, eattr.cred_len,
+				      eattr.num_cred, attr->version2 != NULL)) {
+			wpabuf_free(decrypted);
+			wps->state = SEND_WSC_NACK;
+			return WPS_CONTINUE;
+		}
+		wpabuf_free(decrypted);
+
+		wps->state = WPS_MSG_DONE;
+		return WPS_CONTINUE;
+	}
+#endif /* CONFIG_WPS_NFC */
+
 	wps->state = SEND_M3;
 	return WPS_CONTINUE;
 }
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index e729617..8e9ee7a 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -2040,8 +2040,7 @@
 	os_memset(&data, 0, sizeof(data));
 	data.wps = wps;
 	data.use_cred = cred;
-	if (wps_build_version(ret) ||
-	    wps_build_cred(&data, ret) ||
+	if (wps_build_cred(&data, ret) ||
 	    wps_build_wfa_ext(ret, 0, NULL, 0)) {
 		wpabuf_free(ret);
 		return NULL;
@@ -2071,4 +2070,29 @@
 	return wps_er_config_token_from_cred(er->wps, ap->ap_settings);
 }
 
+
+struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er,
+					struct wps_context *wps, const u8 *uuid,
+					const u8 *addr, struct wpabuf *pubkey)
+{
+	struct wps_er_ap *ap;
+
+	if (er == NULL)
+		return NULL;
+
+	ap = wps_er_ap_get(er, NULL, uuid, addr);
+	if (ap == NULL)
+		return NULL;
+	if (ap->ap_settings == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the "
+			   "selected AP");
+		return NULL;
+	}
+
+	os_memcpy(wps->ssid, ap->ap_settings->ssid, ap->ap_settings->ssid_len);
+	wps->ssid_len = ap->ap_settings->ssid_len;
+
+	return wps_build_nfc_handover_sel(wps, pubkey, addr, 0);
+}
+
 #endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index dac1250..22070db 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -75,6 +75,9 @@
 	size_t alt_dev_password_len;
 	u16 alt_dev_pw_id;
 
+	u8 peer_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+	int peer_pubkey_hash_set;
+
 	/**
 	 * request_type - Request Type attribute from (Re)AssocReq
 	 */
@@ -173,6 +176,8 @@
 			 size_t dev_pw_len);
 struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
 int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr);
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands);
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel);
 
 /* wps_attr_process.c */
 int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 19490a1..6d879be 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -31,9 +31,11 @@
 struct wps_nfc_pw_token {
 	struct dl_list list;
 	u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+	unsigned int peer_pk_hash_known:1;
 	u16 pw_id;
 	u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1];
 	size_t dev_pw_len;
+	int pk_hash_provided_oob; /* whether own PK hash was provided OOB */
 };
 
 
@@ -1360,10 +1362,23 @@
 		pin_len = 8;
 #ifdef CONFIG_WPS_NFC
 	} else if (wps->nfc_pw_token) {
+		if (wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+		{
+			wpa_printf(MSG_DEBUG, "WPS: Using NFC connection "
+				   "handover and abbreviated WPS handshake "
+				   "without Device Password");
+			return 0;
+		}
 		wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC "
 			   "Password Token");
 		pin = wps->nfc_pw_token->dev_pw;
 		pin_len = wps->nfc_pw_token->dev_pw_len;
+	} else if (wps->dev_pw_id >= 0x10 &&
+		   wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+		   wps->wps->ap_nfc_dev_pw) {
+		wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from own NFC Password Token");
+		pin = wpabuf_head(wps->wps->ap_nfc_dev_pw);
+		pin_len = wpabuf_len(wps->wps->ap_nfc_dev_pw);
 #endif /* CONFIG_WPS_NFC */
 	} else {
 		pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
@@ -1777,6 +1792,7 @@
 static struct wpabuf * wps_build_m2(struct wps_data *wps)
 {
 	struct wpabuf *msg;
+	int config_in_m2 = 0;
 
 	if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0)
 		return NULL;
@@ -1807,14 +1823,41 @@
 	    wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
 	    wps_build_dev_password_id(msg, wps->dev_pw_id) ||
 	    wps_build_os_version(&wps->wps->dev, msg) ||
-	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
-	    wps_build_authenticator(wps, msg)) {
+	    wps_build_wfa_ext(msg, 0, NULL, 0)) {
+		wpabuf_free(msg);
+		return NULL;
+	}
+
+#ifdef CONFIG_WPS_NFC
+	if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob &&
+	    wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+		/*
+		 * Use abbreviated handshake since public key hash allowed
+		 * Enrollee to validate our public key similarly to how Enrollee
+		 * public key was validated. There is no need to validate Device
+		 * Password in this case.
+		 */
+		struct wpabuf *plain = wpabuf_alloc(500);
+		if (plain == NULL ||
+		    wps_build_cred(wps, plain) ||
+		    wps_build_key_wrap_auth(wps, plain) ||
+		    wps_build_encr_settings(wps, msg, plain)) {
+			wpabuf_free(msg);
+			wpabuf_free(plain);
+			return NULL;
+		}
+		wpabuf_free(plain);
+		config_in_m2 = 1;
+	}
+#endif /* CONFIG_WPS_NFC */
+
+	if (wps_build_authenticator(wps, msg)) {
 		wpabuf_free(msg);
 		return NULL;
 	}
 
 	wps->int_reg = 1;
-	wps->state = RECV_M3;
+	wps->state = config_in_m2 ? RECV_DONE : RECV_M3;
 	return msg;
 }
 
@@ -2528,6 +2571,9 @@
 	    wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
 	    wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
 	    wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
+#ifdef CONFIG_WPS_NFC
+	    wps->dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+#endif /* CONFIG_WPS_NFC */
 	    (wps->dev_pw_id != DEV_PW_PUSHBUTTON ||
 	     !wps->wps->registrar->pbc)) {
 		wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d",
@@ -2537,7 +2583,8 @@
 	}
 
 #ifdef CONFIG_WPS_NFC
-	if (wps->dev_pw_id >= 0x10) {
+	if (wps->dev_pw_id >= 0x10 ||
+	    wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
 		struct wps_nfc_pw_token *token;
 		const u8 *addr[1];
 		u8 hash[WPS_HASH_LEN];
@@ -2546,7 +2593,7 @@
 			   wps->dev_pw_id, wps->wps, wps->wps->registrar);
 		token = wps_get_nfc_pw_token(
 			&wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
-		if (token) {
+		if (token && token->peer_pk_hash_known) {
 			wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
 				   "Password Token");
 			dl_list_del(&token->list);
@@ -2558,8 +2605,19 @@
 				      WPS_OOB_PUBKEY_HASH_LEN) != 0) {
 				wpa_printf(MSG_ERROR, "WPS: Public Key hash "
 					   "mismatch");
-				return WPS_FAILURE;
+				wps->state = SEND_M2D;
+				wps->config_error =
+					WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+				return WPS_CONTINUE;
 			}
+		} else if (token) {
+			wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
+				   "Password Token (no peer PK hash)");
+			wps->nfc_pw_token = token;
+		} else if (wps->dev_pw_id >= 0x10 &&
+			   wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+			   wps->wps->ap_nfc_dev_pw) {
+			wpa_printf(MSG_DEBUG, "WPS: Found match with own NFC Password Token");
 		}
 	}
 #endif /* CONFIG_WPS_NFC */
@@ -3493,25 +3551,39 @@
 
 int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
 				   const u8 *pubkey_hash, u16 pw_id,
-				   const u8 *dev_pw, size_t dev_pw_len)
+				   const u8 *dev_pw, size_t dev_pw_len,
+				   int pk_hash_provided_oob)
 {
 	struct wps_nfc_pw_token *token;
 
 	if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
 		return -1;
 
+	if (pw_id == DEV_PW_NFC_CONNECTION_HANDOVER &&
+	    (pubkey_hash == NULL || !pk_hash_provided_oob)) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected NFC Password Token "
+			   "addition - missing public key hash");
+		return -1;
+	}
+
 	wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, pw_id);
 
 	token = os_zalloc(sizeof(*token));
 	if (token == NULL)
 		return -1;
 
-	os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+	token->peer_pk_hash_known = pubkey_hash != NULL;
+	if (pubkey_hash)
+		os_memcpy(token->pubkey_hash, pubkey_hash,
+			  WPS_OOB_PUBKEY_HASH_LEN);
 	token->pw_id = pw_id;
-	wpa_snprintf_hex_uppercase((char *) token->dev_pw,
-				   sizeof(token->dev_pw),
-				   dev_pw, dev_pw_len);
-	token->dev_pw_len = dev_pw_len * 2;
+	token->pk_hash_provided_oob = pk_hash_provided_oob;
+	if (dev_pw) {
+		wpa_snprintf_hex_uppercase((char *) token->dev_pw,
+					   sizeof(token->dev_pw),
+					   dev_pw, dev_pw_len);
+		token->dev_pw_len = dev_pw_len * 2;
+	}
 
 	dl_list_add(&reg->nfc_pw_tokens, &token->list);
 
@@ -3540,8 +3612,7 @@
 	u16 id;
 	size_t dev_pw_len;
 
-	if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
-	    WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+	if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
 	    oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
 	    WPS_OOB_DEVICE_PASSWORD_LEN)
 		return -1;
@@ -3560,7 +3631,7 @@
 	wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len);
 
 	return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw,
-					      dev_pw_len);
+					      dev_pw_len, 0);
 }
 
 
@@ -3570,6 +3641,14 @@
 	wps_registrar_remove_authorized_mac(reg,
 					    (u8 *) "\xff\xff\xff\xff\xff\xff");
 	wps_registrar_selected_registrar_changed(reg, 0);
+
+	/*
+	 * Free the NFC password token if it was used only for a single protocol
+	 * run. The static handover case uses the same password token multiple
+	 * times, so do not free that case here.
+	 */
+	if (token->peer_pk_hash_known)
+		os_free(token);
 }
 
 #endif /* CONFIG_WPS_NFC */
diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c
index e366256..1c6a14b 100644
--- a/src/wps/wps_validate.c
+++ b/src/wps/wps_validate.c
@@ -267,7 +267,7 @@
 		return 0;
 	}
 	val = WPA_GET_BE16(config_error);
-	if (val > 18) {
+	if (val > 20) {
 		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error "
 			   "attribute value 0x%04x", val);
 		return -1;
@@ -290,7 +290,7 @@
 		return 0;
 	}
 	val = WPA_GET_BE16(dev_password_id);
-	if (val >= 0x0006 && val <= 0x000f) {
+	if (val >= 0x0008 && val <= 0x000f) {
 		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID "
 			   "attribute value 0x%04x", val);
 		return -1;
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index a030a0b..9b07460 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -255,6 +255,7 @@
 OBJS += src/p2p/p2p_dev_disc.c
 OBJS += src/p2p/p2p_group.c
 OBJS += src/ap/p2p_hostapd.c
+OBJS += src/utils/bitfield.c
 L_CFLAGS += -DCONFIG_P2P
 NEED_GAS=y
 NEED_OFFCHANNEL=y
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 2c709c3..d1e11a3 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -257,6 +257,7 @@
 OBJS += ../src/p2p/p2p_dev_disc.o
 OBJS += ../src/p2p/p2p_group.o
 OBJS += ../src/ap/p2p_hostapd.o
+OBJS += ../src/utils/bitfield.o
 CFLAGS += -DCONFIG_P2P
 NEED_GAS=y
 NEED_OFFCHANNEL=y
diff --git a/wpa_supplicant/README-WPS b/wpa_supplicant/README-WPS
index 3d07109..18b0cca 100644
--- a/wpa_supplicant/README-WPS
+++ b/wpa_supplicant/README-WPS
@@ -366,11 +366,11 @@
 token is used to enable enrollment of a new station (that was the source
 of the NFC password token).
 
-"nfc_get_handover_req <NDEF> <WPS>" command can be used to build the
-contents of a Handover Request Message for connection handover. The
-first argument selects the format of the output data and the second
-argument selects which type of connection handover is requested (WPS =
-Wi-Fi handover as specified in WSC 2.0).
+"nfc_get_handover_req <NDEF> <WPS-CR>" command can be used to build the
+WPS carrier record for a Handover Request Message for connection
+handover. The first argument selects the format of the output data and
+the second argument selects which type of connection handover is
+requested (WPS-CR = Wi-Fi handover as specified in WSC 2.0).
 
 "nfc_get_handover_sel <NDEF> <WPS> [UUID|BSSID]" command can be used to
 build the contents of a Handover Select Message for connection handover
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 5f69389..ce3efcb 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -15,6 +15,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
 #include "eapol_supp/eapol_supp_sm.h"
+#include "crypto/dh_group5.h"
 #include "ap/hostapd.h"
 #include "ap/ap_config.h"
 #include "ap/ap_drv_ops.h"
@@ -181,6 +182,16 @@
 
 	bss->isolate = !wpa_s->conf->p2p_intra_bss;
 	bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk;
+
+	if (ssid->p2p_group) {
+		os_memcpy(bss->ip_addr_go, wpa_s->parent->conf->ip_addr_go, 4);
+		os_memcpy(bss->ip_addr_mask, wpa_s->parent->conf->ip_addr_mask,
+			  4);
+		os_memcpy(bss->ip_addr_start,
+			  wpa_s->parent->conf->ip_addr_start, 4);
+		os_memcpy(bss->ip_addr_end, wpa_s->parent->conf->ip_addr_end,
+			  4);
+	}
 #endif /* CONFIG_P2P */
 
 	if (ssid->ssid_len == 0) {
@@ -946,6 +957,19 @@
 	return hostapd_wps_nfc_hs_cr(hapd, ndef);
 }
 
+
+int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel)
+{
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface == NULL)
+		return -1;
+	hapd = wpa_s->ap_iface->bss[0];
+	return hostapd_wps_nfc_report_handover(hapd, req, sel);
+}
+
 #endif /* CONFIG_WPS_NFC */
 
 #endif /* CONFIG_WPS */
@@ -1139,3 +1163,48 @@
 
 	return 0;
 }
+
+
+#ifdef CONFIG_WPS_NFC
+int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
+			   const struct wpabuf *pw, const u8 *pubkey_hash)
+{
+	struct hostapd_data *hapd;
+	struct wps_context *wps;
+
+	if (!wpa_s->ap_iface)
+		return -1;
+	hapd = wpa_s->ap_iface->bss[0];
+	wps = hapd->wps;
+
+	if (wpa_s->parent->conf->wps_nfc_dh_pubkey == NULL ||
+	    wpa_s->parent->conf->wps_nfc_dh_privkey == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: No NFC DH key known");
+		return -1;
+	}
+
+	dh5_free(wps->dh_ctx);
+	wpabuf_free(wps->dh_pubkey);
+	wpabuf_free(wps->dh_privkey);
+	wps->dh_privkey = wpabuf_dup(
+		wpa_s->parent->conf->wps_nfc_dh_privkey);
+	wps->dh_pubkey = wpabuf_dup(
+		wpa_s->parent->conf->wps_nfc_dh_pubkey);
+	if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
+		wps->dh_ctx = NULL;
+		wpabuf_free(wps->dh_pubkey);
+		wps->dh_pubkey = NULL;
+		wpabuf_free(wps->dh_privkey);
+		wps->dh_privkey = NULL;
+		return -1;
+	}
+	wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
+	if (wps->dh_ctx == NULL)
+		return -1;
+
+	return wps_registrar_add_nfc_pw_token(hapd->wps->registrar, pubkey_hash,
+					      pw_id,
+					      pw ? wpabuf_head(pw) : NULL,
+					      pw ? wpabuf_len(pw) : 0, 1);
+}
+#endif /* CONFIG_WPS_NFC */
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index c382898..8aa5ffa 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -69,4 +69,10 @@
 }
 #endif /* CONFIG_AP */
 
+int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel);
+int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
+			   const struct wpabuf *pw, const u8 *pubkey_hash);
+
 #endif /* AP_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 884acf6..b43a72a 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "utils/uuid.h"
+#include "utils/ip_addr.h"
 #include "crypto/sha1.h"
 #include "rsn_supp/wpa.h"
 #include "eap_peer/eap.h"
@@ -2971,6 +2972,29 @@
 }
 
 
+#ifdef CONFIG_P2P
+static int wpa_global_config_parse_ipv4(const struct global_parse_data *data,
+					struct wpa_config *config, int line,
+					const char *pos)
+{
+	u32 *dst;
+	struct hostapd_ip_addr addr;
+
+	if (hostapd_parse_ip_addr(pos, &addr) < 0)
+		return -1;
+	if (addr.af != AF_INET)
+		return -1;
+
+	dst = (u32 *) (((u8 *) config) + (long) data->param1);
+	os_memcpy(dst, &addr.u.v4.s_addr, 4);
+	wpa_printf(MSG_DEBUG, "%s = 0x%x", data->name,
+		   WPA_GET_BE32((u8 *) dst));
+
+	return 0;
+}
+#endif /* CONFIG_P2P */
+
+
 static int wpa_config_process_country(const struct global_parse_data *data,
 				      struct wpa_config *config, int line,
 				      const char *pos)
@@ -3275,6 +3299,7 @@
 #define STR(f) _STR(f), NULL, NULL
 #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
 #define BIN(f) #f, wpa_global_config_parse_bin, OFFSET(f), NULL, NULL
+#define IPV4(f) #f, wpa_global_config_parse_ipv4, OFFSET(f), NULL, NULL
 
 static const struct global_parse_data global_fields[] = {
 #ifdef CONFIG_CTRL_IFACE
@@ -3333,6 +3358,10 @@
 	{ INT(p2p_disabled), 0 },
 	{ INT(p2p_no_group_iface), 0 },
 	{ INT_RANGE(p2p_ignore_shared_freq, 0, 1), 0 },
+	{ IPV4(ip_addr_go), 0 },
+	{ IPV4(ip_addr_mask), 0 },
+	{ IPV4(ip_addr_start), 0 },
+	{ IPV4(ip_addr_end), 0 },
 #endif /* CONFIG_P2P */
 	{ FUNC(country), CFG_CHANGED_COUNTRY },
 	{ INT(bss_max_count), 0 },
@@ -3379,6 +3408,7 @@
 #undef STR
 #undef STR_RANGE
 #undef BIN
+#undef IPV4
 #define NUM_GLOBAL_FIELDS ARRAY_SIZE(global_fields)
 
 
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index a98a5aa..e7bdaa5 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -948,6 +948,11 @@
 	 * specified TDLS peers.
 	 */
 	int tdls_external_control;
+
+	u8 ip_addr_go[4];
+	u8 ip_addr_mask[4];
+	u8 ip_addr_start[4];
+	u8 ip_addr_end[4];
 };
 
 
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 68157d2..9a3cbea 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -364,6 +364,10 @@
 		wps_testing_dummy_cred = atoi(value);
 		wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
 			   wps_testing_dummy_cred);
+	} else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) {
+		wps_corrupt_pkhash = atoi(value);
+		wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
+			   wps_corrupt_pkhash);
 #endif /* CONFIG_WPS_TESTING */
 	} else if (os_strcasecmp(cmd, "ampdu") == 0) {
 		if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0)
@@ -813,7 +817,8 @@
 	else if (hwaddr_aton(cmd, bssid))
 		return -1;
 
-	return wpas_wps_start_nfc(wpa_s, _bssid);
+	return wpas_wps_start_nfc(wpa_s, NULL, _bssid, NULL, 0, 0, NULL, NULL,
+				  0, 0);
 }
 
 
@@ -885,6 +890,15 @@
 	size_t len;
 	struct wpabuf *buf;
 	int ret;
+	char *freq;
+	int forced_freq = 0;
+
+	freq = strstr(pos, " freq=");
+	if (freq) {
+		*freq = '\0';
+		freq += 6;
+		forced_freq = atoi(freq);
+	}
 
 	len = os_strlen(pos);
 	if (len & 0x01)
@@ -899,7 +913,7 @@
 		return -1;
 	}
 
-	ret = wpas_wps_nfc_tag_read(wpa_s, buf);
+	ret = wpas_wps_nfc_tag_read(wpa_s, buf, forced_freq);
 	wpabuf_free(buf);
 
 	return ret;
@@ -908,12 +922,12 @@
 
 static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
 					      char *reply, size_t max_len,
-					      int cr)
+					      int ndef)
 {
 	struct wpabuf *buf;
 	int res;
 
-	buf = wpas_wps_nfc_handover_req(wpa_s, cr);
+	buf = wpas_wps_nfc_handover_req(wpa_s, ndef);
 	if (buf == NULL)
 		return -1;
 
@@ -928,23 +942,59 @@
 }
 
 
+static int wpas_ctrl_nfc_get_handover_req_p2p(struct wpa_supplicant *wpa_s,
+					      char *reply, size_t max_len,
+					      int ndef)
+{
+	struct wpabuf *buf;
+	int res;
+
+	buf = wpas_p2p_nfc_handover_req(wpa_s, ndef);
+	if (buf == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: Could not generate NFC handover request");
+		return -1;
+	}
+
+	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+					 wpabuf_len(buf));
+	reply[res++] = '\n';
+	reply[res] = '\0';
+
+	wpabuf_free(buf);
+
+	return res;
+}
+
+
 static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s,
 					  char *cmd, char *reply,
 					  size_t max_len)
 {
 	char *pos;
+	int ndef;
 
 	pos = os_strchr(cmd, ' ');
 	if (pos == NULL)
 		return -1;
 	*pos++ = '\0';
 
-	if (os_strcmp(cmd, "NDEF") != 0)
+	if (os_strcmp(cmd, "WPS") == 0)
+		ndef = 0;
+	else if (os_strcmp(cmd, "NDEF") == 0)
+		ndef = 1;
+	else
 		return -1;
 
 	if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+		if (!ndef)
+			return -1;
 		return wpas_ctrl_nfc_get_handover_req_wps(
-			wpa_s, reply, max_len, os_strcmp(pos, "WPS-CR") == 0);
+			wpa_s, reply, max_len, ndef);
+	}
+
+	if (os_strcmp(pos, "P2P-CR") == 0) {
+		return wpas_ctrl_nfc_get_handover_req_p2p(
+			wpa_s, reply, max_len, ndef);
 	}
 
 	return -1;
@@ -973,6 +1023,28 @@
 }
 
 
+static int wpas_ctrl_nfc_get_handover_sel_p2p(struct wpa_supplicant *wpa_s,
+					      char *reply, size_t max_len,
+					      int ndef, int tag)
+{
+	struct wpabuf *buf;
+	int res;
+
+	buf = wpas_p2p_nfc_handover_sel(wpa_s, ndef, tag);
+	if (buf == NULL)
+		return -1;
+
+	res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+					 wpabuf_len(buf));
+	reply[res++] = '\n';
+	reply[res] = '\0';
+
+	wpabuf_free(buf);
+
+	return res;
+}
+
+
 static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s,
 					  char *cmd, char *reply,
 					  size_t max_len)
@@ -996,11 +1068,23 @@
 	if (pos2)
 		*pos2++ = '\0';
 	if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+		if (!ndef)
+			return -1;
 		return wpas_ctrl_nfc_get_handover_sel_wps(
 			wpa_s, reply, max_len, ndef,
 			os_strcmp(pos, "WPS-CR") == 0, pos2);
 	}
 
+	if (os_strcmp(pos, "P2P-CR") == 0) {
+		return wpas_ctrl_nfc_get_handover_sel_p2p(
+			wpa_s, reply, max_len, ndef, 0);
+	}
+
+	if (os_strcmp(pos, "P2P-CR-TAG") == 0) {
+		return wpas_ctrl_nfc_get_handover_sel_p2p(
+			wpa_s, reply, max_len, ndef, 1);
+	}
+
 	return -1;
 }
 
@@ -1067,39 +1151,60 @@
 	struct wpabuf *req, *sel;
 	int ret;
 	char *pos, *role, *type, *pos2;
+	char *freq;
+	int forced_freq = 0;
+
+	freq = strstr(cmd, " freq=");
+	if (freq) {
+		*freq = '\0';
+		freq += 6;
+		forced_freq = atoi(freq);
+	}
 
 	role = cmd;
 	pos = os_strchr(role, ' ');
-	if (pos == NULL)
+	if (pos == NULL) {
+		wpa_printf(MSG_DEBUG, "NFC: Missing type in handover report");
 		return -1;
+	}
 	*pos++ = '\0';
 
 	type = pos;
 	pos = os_strchr(type, ' ');
-	if (pos == NULL)
+	if (pos == NULL) {
+		wpa_printf(MSG_DEBUG, "NFC: Missing request message in handover report");
 		return -1;
+	}
 	*pos++ = '\0';
 
 	pos2 = os_strchr(pos, ' ');
-	if (pos2 == NULL)
+	if (pos2 == NULL) {
+		wpa_printf(MSG_DEBUG, "NFC: Missing select message in handover report");
 		return -1;
+	}
 	*pos2++ = '\0';
 
 	len = os_strlen(pos);
-	if (len & 0x01)
+	if (len & 0x01) {
+		wpa_printf(MSG_DEBUG, "NFC: Invalid request message length in handover report");
 		return -1;
+	}
 	len /= 2;
 
 	req = wpabuf_alloc(len);
-	if (req == NULL)
+	if (req == NULL) {
+		wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for request message");
 		return -1;
+	}
 	if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
+		wpa_printf(MSG_DEBUG, "NFC: Invalid request message hexdump in handover report");
 		wpabuf_free(req);
 		return -1;
 	}
 
 	len = os_strlen(pos2);
 	if (len & 0x01) {
+		wpa_printf(MSG_DEBUG, "NFC: Invalid select message length in handover report");
 		wpabuf_free(req);
 		return -1;
 	}
@@ -1107,17 +1212,34 @@
 
 	sel = wpabuf_alloc(len);
 	if (sel == NULL) {
+		wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for select message");
 		wpabuf_free(req);
 		return -1;
 	}
 	if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
+		wpa_printf(MSG_DEBUG, "NFC: Invalid select message hexdump in handover report");
 		wpabuf_free(req);
 		wpabuf_free(sel);
 		return -1;
 	}
 
+	wpa_printf(MSG_DEBUG, "NFC: Connection handover reported - role=%s type=%s req_len=%d sel_len=%d",
+		   role, type, (int) wpabuf_len(req), (int) wpabuf_len(sel));
+
 	if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "WPS") == 0) {
 		ret = wpas_wps_nfc_report_handover(wpa_s, req, sel);
+	} else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0)
+	{
+		ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel);
+		if (ret < 0)
+			ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel);
+	} else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0)
+	{
+		ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel, 0);
+	} else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0)
+	{
+		ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel,
+						   forced_freq);
 	} else {
 		wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
 			   "reported: role=%s type=%s", role, type);
@@ -1126,6 +1248,9 @@
 	wpabuf_free(req);
 	wpabuf_free(sel);
 
+	if (ret)
+		wpa_printf(MSG_DEBUG, "NFC: Failed to process reported handover messages");
+
 	return ret;
 }
 
@@ -4594,6 +4719,16 @@
 		return 0;
 	}
 
+#ifdef CONFIG_WPS_NFC
+	if (os_strcmp(cmd, "nfc_tag") == 0)
+		return wpas_p2p_nfc_tag_enabled(wpa_s, !!atoi(param));
+#endif /* CONFIG_WPS_NFC */
+
+	if (os_strcmp(cmd, "disable_ip_addr_req") == 0) {
+		wpa_s->p2p_disable_ip_addr_req = !!atoi(param);
+		return 0;
+	}
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
 		   cmd);
 
@@ -5270,11 +5405,13 @@
 	wpa_s->global->p2p_disabled = 0;
 	wpa_s->global->p2p_per_sta_psk = 0;
 	wpa_s->conf->num_sec_device_types = 0;
+	wpa_s->p2p_disable_ip_addr_req = 0;
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WPS_TESTING
 	wps_version_number = 0x20;
 	wps_testing_dummy_cred = 0;
+	wps_corrupt_pkhash = 0;
 #endif /* CONFIG_WPS_TESTING */
 #ifdef CONFIG_WPS
 	wpa_s->wps_fragment_size = 0;
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 72d2d5b..ea94768 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -68,9 +68,25 @@
 }
 
 
+static struct wpa_bss * wpa_supplicant_get_new_bss(
+	struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+	struct wpa_bss *bss = NULL;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (ssid->ssid_len > 0)
+		bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
+	if (!bss)
+		bss = wpa_bss_get_bssid(wpa_s, bssid);
+
+	return bss;
+}
+
+
 static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ssid *ssid, *old_ssid;
+	struct wpa_bss *bss;
 	int res;
 
 	if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid)
@@ -119,6 +135,18 @@
 		eapol_sm_invalidate_cached_session(wpa_s->eapol);
 	old_ssid = wpa_s->current_ssid;
 	wpa_s->current_ssid = ssid;
+
+	bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
+	if (!bss) {
+		wpa_supplicant_update_scan_results(wpa_s);
+
+		/* Get the BSS from the new scan results */
+		bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
+	}
+
+	if (bss)
+		wpa_s->current_bss = bss;
+
 	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
 	wpa_supplicant_initiate_eapol(wpa_s);
 	if (old_ssid != wpa_s->current_ssid)
@@ -1766,21 +1794,6 @@
 }
 
 
-static struct wpa_bss * wpa_supplicant_get_new_bss(
-	struct wpa_supplicant *wpa_s, const u8 *bssid)
-{
-	struct wpa_bss *bss = NULL;
-	struct wpa_ssid *ssid = wpa_s->current_ssid;
-
-	if (ssid->ssid_len > 0)
-		bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
-	if (!bss)
-		bss = wpa_bss_get_bssid(wpa_s, bssid);
-
-	return bss;
-}
-
-
 static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
 {
 	const u8 *bss_wpa = NULL, *bss_rsn = NULL;
@@ -1850,20 +1863,6 @@
 				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 			return;
 		}
-		if (wpa_s->current_ssid) {
-			struct wpa_bss *bss = NULL;
-
-			bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
-			if (!bss) {
-				wpa_supplicant_update_scan_results(wpa_s);
-
-				/* Get the BSS from the new scan results */
-				bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
-			}
-
-			if (bss)
-				wpa_s->current_bss = bss;
-		}
 
 #ifdef ANDROID
 		if (wpa_s->conf->ap_scan == 1) {
@@ -2779,6 +2778,58 @@
 }
 
 
+static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s,
+					     union wpa_event_data *event)
+{
+#ifdef CONFIG_P2P
+	struct wpa_supplicant *ifs;
+#endif /* CONFIG_P2P */
+	struct wpa_freq_range_list *list;
+	char *str = NULL;
+
+	list = &event->freq_range;
+
+	if (list->num)
+		str = freq_range_list_str(list);
+	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AVOID_FREQ "ranges=%s",
+		str ? str : "");
+
+#ifdef CONFIG_P2P
+	if (freq_range_list_parse(&wpa_s->global->p2p_go_avoid_freq, str)) {
+		wpa_dbg(wpa_s, MSG_ERROR, "%s: Failed to parse freq range",
+			__func__);
+	} else {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event");
+		wpas_p2p_update_channel_list(wpa_s);
+	}
+
+	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+		int freq;
+		if (!ifs->current_ssid ||
+		    !ifs->current_ssid->p2p_group ||
+		    (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
+		     ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
+			continue;
+
+		freq = ifs->current_ssid->frequency;
+		if (!freq_range_list_includes(list, freq)) {
+			wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating frequency %d MHz in safe range",
+				freq);
+			continue;
+		}
+
+		wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating in unsafe frequency %d MHz",
+			freq);
+		/* TODO: Consider using CSA or removing the group within
+		 * wpa_supplicant */
+		wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+	}
+#endif /* CONFIG_P2P */
+
+	os_free(str);
+}
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -3270,6 +3321,9 @@
 		wpas_wps_start_pbc(wpa_s, NULL, 0);
 #endif /* CONFIG_WPS */
 		break;
+	case EVENT_AVOID_FREQUENCIES:
+		wpa_supplicant_notify_avoid_freq(wpa_s, data);
+		break;
 	case EVENT_CONNECT_FAILED_REASON:
 #ifdef CONFIG_AP
 		if (!wpa_s->ap_iface || !data)
diff --git a/wpa_supplicant/examples/p2p-action.sh b/wpa_supplicant/examples/p2p-action.sh
index 8759f54..797d43a 100755
--- a/wpa_supplicant/examples/p2p-action.sh
+++ b/wpa_supplicant/examples/p2p-action.sh
@@ -34,13 +34,26 @@
 	    # start with -z to avoid that
 	    dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \
 		-i $GIFNAME \
-		-F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z
+		-F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z -p 0
 	fi
     fi
     if [ "$4" = "client" ]; then
 	kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
 	rm /var/run/dhclient.leases-$GIFNAME
 	kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+	ipaddr=`echo "$*" | sed 's/.* ip_addr=\([^ ]*\).*/\1/'`
+	ipmask=`echo "$*" | sed 's/.* ip_mask=\([^ ]*\).*/\1/'`
+	goipaddr=`echo "$*" | sed 's/.* go_ip_addr=\([^ ]*\).*/\1/'`
+	if echo "$ipaddr$ipmask$goipaddr" | grep -q ' '; then
+	    ipaddr=""
+	    ipmask=""
+	    goipaddr=""
+	fi
+	if [ -n "$ipaddr" ]; then
+	    sudo ifconfig $GIFNAME "$ipaddr" netmask "$ipmask"
+	    sudo ip ro re default via "$goipaddr"
+	    exit 0
+	fi
 	dhclient -pf /var/run/dhclient-$GIFNAME.pid \
 	    -lf /var/run/dhclient.leases-$GIFNAME \
 	    -nw \
diff --git a/wpa_supplicant/examples/p2p-nfc.py b/wpa_supplicant/examples/p2p-nfc.py
new file mode 100644
index 0000000..848f79f
--- /dev/null
+++ b/wpa_supplicant/examples/p2p-nfc.py
@@ -0,0 +1,581 @@
+#!/usr/bin/python
+#
+# Example nfcpy to wpa_supplicant wrapper for P2P NFC operations
+# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import sys
+import time
+import random
+import threading
+import argparse
+
+import nfc
+import nfc.ndef
+import nfc.llcp
+import nfc.handover
+
+import logging
+
+import wpaspy
+
+wpas_ctrl = '/var/run/wpa_supplicant'
+ifname = None
+init_on_touch = False
+in_raw_mode = False
+prev_tcgetattr = 0
+include_wps_req = True
+include_p2p_req = True
+no_input = False
+srv = None
+continue_loop = True
+terminate_now = False
+
+def wpas_connect():
+    ifaces = []
+    if os.path.isdir(wpas_ctrl):
+        try:
+            ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
+        except OSError, error:
+            print "Could not find wpa_supplicant: ", error
+            return None
+
+    if len(ifaces) < 1:
+        print "No wpa_supplicant control interface found"
+        return None
+
+    for ctrl in ifaces:
+        if ifname:
+            if ifname not in ctrl:
+                continue
+        try:
+            print "Trying to use control interface " + ctrl
+            wpas = wpaspy.Ctrl(ctrl)
+            return wpas
+        except Exception, e:
+            pass
+    return None
+
+
+def wpas_tag_read(message):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return
+    cmd = "WPS_NFC_TAG_READ " + str(message).encode("hex")
+    global force_freq
+    if force_freq:
+        cmd = cmd + " freq=" + force_freq
+    if "FAIL" in wpas.request(cmd):
+        return False
+    return True
+
+
+def wpas_get_handover_req():
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    res = wpas.request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip().decode("hex")
+    if "FAIL" in res:
+        return None
+    return res
+
+def wpas_get_handover_req_wps():
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip().decode("hex")
+
+
+def wpas_get_handover_sel(tag=False):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    if tag:
+        return wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip().decode("hex")
+    return wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip().decode("hex")
+
+
+def wpas_get_handover_sel_wps():
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR");
+    if "FAIL" in res:
+        return None
+    return res.rstrip().decode("hex")
+
+
+def wpas_report_handover(req, sel, type):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    cmd = "NFC_REPORT_HANDOVER " + type + " P2P " + str(req).encode("hex") + " " + str(sel).encode("hex")
+    global force_freq
+    if force_freq:
+        cmd = cmd + " freq=" + force_freq
+    return wpas.request(cmd)
+
+
+def wpas_report_handover_wsc(req, sel, type):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    cmd = "NFC_REPORT_HANDOVER " + type + " WPS " + str(req).encode("hex") + " " + str(sel).encode("hex")
+    if force_freq:
+        cmd = cmd + " freq=" + force_freq
+    return wpas.request(cmd)
+
+
+def p2p_handover_client(llc):
+    message = nfc.ndef.HandoverRequestMessage(version="1.2")
+    message.nonce = random.randint(0, 0xffff)
+
+    global include_p2p_req
+    if include_p2p_req:
+        data = wpas_get_handover_req()
+        if (data == None):
+            print "Could not get handover request carrier record from wpa_supplicant"
+            return
+        print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
+        datamsg = nfc.ndef.Message(data)
+        message.add_carrier(datamsg[0], "active", datamsg[1:])
+
+    global include_wps_req
+    if include_wps_req:
+        print "Handover request (pre-WPS):"
+        try:
+            print message.pretty()
+        except Exception, e:
+            print e
+
+        data = wpas_get_handover_req_wps()
+        if data:
+            print "Add WPS request in addition to P2P"
+            datamsg = nfc.ndef.Message(data)
+            message.add_carrier(datamsg[0], "active", datamsg[1:])
+
+    print "Handover request:"
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+    print str(message).encode("hex")
+
+    client = nfc.handover.HandoverClient(llc)
+    try:
+        print "Trying handover";
+        client.connect()
+        print "Connected for handover"
+    except nfc.llcp.ConnectRefused:
+        print "Handover connection refused"
+        client.close()
+        return
+    except Exception, e:
+        print "Other exception: " + str(e)
+        client.close()
+        return
+
+    print "Sending handover request"
+
+    if not client.send(message):
+        print "Failed to send handover request"
+
+    print "Receiving handover response"
+    message = client._recv()
+    if message is None:
+        print "No response received"
+        client.close()
+        return
+    if message.type != "urn:nfc:wkt:Hs":
+        print "Response was not Hs - received: " + message.type
+        client.close()
+        return
+
+    print "Received message"
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+    print str(message).encode("hex")
+    message = nfc.ndef.HandoverSelectMessage(message)
+    print "Handover select received"
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+
+    for carrier in message.carriers:
+        print "Remote carrier type: " + carrier.type
+        if carrier.type == "application/vnd.wfa.p2p":
+            print "P2P carrier type match - send to wpa_supplicant"
+            wpas_report_handover(data, carrier.record, "INIT")
+            break
+
+    print "Remove peer"
+    client.close()
+    print "Done with handover"
+    global only_one
+    if only_one:
+        print "only_one -> stop loop"
+        global continue_loop
+        continue_loop = False
+
+    global no_wait
+    if no_wait:
+        print "Trying to exit.."
+        global terminate_now
+        terminate_now = True
+
+
+class HandoverServer(nfc.handover.HandoverServer):
+    def __init__(self, llc):
+        super(HandoverServer, self).__init__(llc)
+        self.sent_carrier = None
+        self.ho_server_processing = False
+        self.success = False
+
+    def process_request(self, request):
+        self.ho_server_processing = True
+        clear_raw_mode()
+        print "HandoverServer - request received"
+        try:
+            print "Parsed handover request: " + request.pretty()
+        except Exception, e:
+            print e
+
+        sel = nfc.ndef.HandoverSelectMessage(version="1.2")
+
+        found = False
+
+        for carrier in request.carriers:
+            print "Remote carrier type: " + carrier.type
+            if carrier.type == "application/vnd.wfa.p2p":
+                print "P2P carrier type match - add P2P carrier record"
+                found = True
+                self.received_carrier = carrier.record
+                print "Carrier record:"
+                try:
+                    print carrier.record.pretty()
+                except Exception, e:
+                    print e
+                data = wpas_get_handover_sel()
+                if data is None:
+                    print "Could not get handover select carrier record from wpa_supplicant"
+                    continue
+                print "Handover select carrier record from wpa_supplicant:"
+                print data.encode("hex")
+                self.sent_carrier = data
+                wpas_report_handover(self.received_carrier, self.sent_carrier,
+                                     "RESP")
+
+                message = nfc.ndef.Message(data);
+                sel.add_carrier(message[0], "active", message[1:])
+                break
+
+        for carrier in request.carriers:
+            if found:
+                break
+            print "Remote carrier type: " + carrier.type
+            if carrier.type == "application/vnd.wfa.wsc":
+                print "WSC carrier type match - add WSC carrier record"
+                found = True
+                self.received_carrier = carrier.record
+                print "Carrier record:"
+                try:
+                    print carrier.record.pretty()
+                except Exception, e:
+                    print e
+                data = wpas_get_handover_sel_wps()
+                if data is None:
+                    print "Could not get handover select carrier record from wpa_supplicant"
+                    continue
+                print "Handover select carrier record from wpa_supplicant:"
+                print data.encode("hex")
+                self.sent_carrier = data
+                wpas_report_handover_wsc(self.received_carrier,
+                                         self.sent_carrier, "RESP")
+
+                message = nfc.ndef.Message(data);
+                sel.add_carrier(message[0], "active", message[1:])
+                found = True
+                break
+
+        print "Handover select:"
+        try:
+            print sel.pretty()
+        except Exception, e:
+            print e
+        print str(sel).encode("hex")
+
+        print "Sending handover select"
+        self.success = True
+        return sel
+
+
+def clear_raw_mode():
+    import sys, tty, termios
+    global prev_tcgetattr, in_raw_mode
+    if not in_raw_mode:
+        return
+    fd = sys.stdin.fileno()
+    termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
+    in_raw_mode = False
+
+
+def getch():
+    import sys, tty, termios, select
+    global prev_tcgetattr, in_raw_mode
+    fd = sys.stdin.fileno()
+    prev_tcgetattr = termios.tcgetattr(fd)
+    ch = None
+    try:
+        tty.setraw(fd)
+        in_raw_mode = True
+        [i, o, e] = select.select([fd], [], [], 0.05)
+        if i:
+            ch = sys.stdin.read(1)
+    finally:
+        termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
+        in_raw_mode = False
+    return ch
+
+
+def p2p_tag_read(tag):
+    success = False
+    if len(tag.ndef.message):
+        for record in tag.ndef.message:
+            print "record type " + record.type
+            if record.type == "application/vnd.wfa.wsc":
+                print "WPS tag - send to wpa_supplicant"
+                success = wpas_tag_read(tag.ndef.message)
+                break
+            if record.type == "application/vnd.wfa.p2p":
+                print "P2P tag - send to wpa_supplicant"
+                success = wpas_tag_read(tag.ndef.message)
+                break
+    else:
+        print "Empty tag"
+
+    return success
+
+
+def rdwr_connected_p2p_write(tag):
+    print "Tag found - writing"
+    global p2p_sel_data
+    tag.ndef.message = str(p2p_sel_data)
+    print "Done - remove tag"
+    global only_one
+    if only_one:
+        global continue_loop
+        continue_loop = False
+    global p2p_sel_wait_remove
+    return p2p_sel_wait_remove
+
+def wps_write_p2p_handover_sel(clf, wait_remove=True):
+    print "Write P2P handover select"
+    data = wpas_get_handover_sel(tag=True)
+    if (data == None):
+        print "Could not get P2P handover select from wpa_supplicant"
+        return
+
+    global p2p_sel_wait_remove
+    p2p_sel_wait_remove = wait_remove
+    global p2p_sel_data
+    p2p_sel_data = nfc.ndef.HandoverSelectMessage(version="1.2")
+    message = nfc.ndef.Message(data);
+    p2p_sel_data.add_carrier(message[0], "active", message[1:])
+    print "Handover select:"
+    try:
+        print p2p_sel_data.pretty()
+    except Exception, e:
+        print e
+    print str(p2p_sel_data).encode("hex")
+
+    print "Touch an NFC tag"
+    clf.connect(rdwr={'on-connect': rdwr_connected_p2p_write})
+
+
+def rdwr_connected(tag):
+    global only_one, no_wait
+    print "Tag connected: " + str(tag)
+
+    if tag.ndef:
+        print "NDEF tag: " + tag.type
+        try:
+            print tag.ndef.message.pretty()
+        except Exception, e:
+            print e
+        success = p2p_tag_read(tag)
+        if only_one and success:
+            global continue_loop
+            continue_loop = False
+    else:
+        print "Not an NDEF tag - remove tag"
+
+    return not no_wait
+
+
+def llcp_worker(llc):
+    global init_on_touch
+    if init_on_touch:
+            print "Starting handover client"
+            p2p_handover_client(llc)
+            return
+
+    global no_input
+    if no_input:
+        print "Wait for handover to complete"
+    else:
+        print "Wait for handover to complete - press 'i' to initiate ('w' for WPS only, 'p' for P2P only)"
+    global srv
+    global wait_connection
+    while not wait_connection and srv.sent_carrier is None:
+        if srv.ho_server_processing:
+            time.sleep(0.025)
+        elif no_input:
+            time.sleep(0.5)
+        else:
+            global include_wps_req, include_p2p_req
+            res = getch()
+            if res == 'i':
+                include_wps_req = True
+                include_p2p_req = True
+            elif res == 'p':
+                include_wps_req = False
+                include_p2p_req = True
+            elif res == 'w':
+                include_wps_req = True
+                include_p2p_req = False
+            else:
+                continue
+            clear_raw_mode()
+            print "Starting handover client"
+            p2p_handover_client(llc)
+            return
+            
+    clear_raw_mode()
+    print "Exiting llcp_worker thread"
+
+def llcp_startup(clf, llc):
+    print "Start LLCP server"
+    global srv
+    srv = HandoverServer(llc)
+    return llc
+
+def llcp_connected(llc):
+    print "P2P LLCP connected"
+    global wait_connection
+    wait_connection = False
+    global init_on_touch
+    if not init_on_touch:
+        global srv
+        srv.start()
+    if init_on_touch or not no_input:
+        threading.Thread(target=llcp_worker, args=(llc,)).start()
+    return True
+
+def terminate_loop():
+    global terminate_now
+    return terminate_now
+
+def main():
+    clf = nfc.ContactlessFrontend()
+
+    parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for P2P and WPS NFC operations')
+    parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+                        action='store_const', dest='loglevel',
+                        help='verbose debug output')
+    parser.add_argument('-q', const=logging.WARNING, action='store_const',
+                        dest='loglevel', help='be quiet')
+    parser.add_argument('--only-one', '-1', action='store_true',
+                        help='run only one operation and exit')
+    parser.add_argument('--init-on-touch', '-I', action='store_true',
+                        help='initiate handover on touch')
+    parser.add_argument('--no-wait', action='store_true',
+                        help='do not wait for tag to be removed before exiting')
+    parser.add_argument('--ifname', '-i',
+                        help='network interface name')
+    parser.add_argument('--no-wps-req', '-N', action='store_true',
+                        help='do not include WPS carrier record in request')
+    parser.add_argument('--no-input', '-a', action='store_true',
+                        help='do not use stdout input to initiate handover')
+    parser.add_argument('--tag-read-only', '-t', action='store_true',
+                        help='tag read only (do not allow connection handover)')
+    parser.add_argument('--freq', '-f',
+                        help='forced frequency of operating channel in MHz')
+    parser.add_argument('command', choices=['write-p2p-sel'],
+                        nargs='?')
+    args = parser.parse_args()
+
+    global only_one
+    only_one = args.only_one
+
+    global no_wait
+    no_wait = args.no_wait
+
+    global force_freq
+    force_freq = args.freq
+
+    logging.basicConfig(level=args.loglevel)
+
+    global init_on_touch
+    init_on_touch = args.init_on_touch
+
+    if args.ifname:
+        global ifname
+        ifname = args.ifname
+        print "Selected ifname " + ifname
+
+    if args.no_wps_req:
+        global include_wps_req
+        include_wps_req = False
+
+    if args.no_input:
+        global no_input
+        no_input = True
+
+    clf = nfc.ContactlessFrontend()
+    global wait_connection
+
+    try:
+        if not clf.open("usb"):
+            print "Could not open connection with an NFC device"
+            raise SystemExit
+
+        if args.command == "write-p2p-sel":
+            wps_write_p2p_handover_sel(clf, wait_remove=not args.no_wait)
+            raise SystemExit
+
+        global continue_loop
+        while continue_loop:
+            print "Waiting for a tag or peer to be touched"
+            wait_connection = True
+            try:
+                if args.tag_read_only:
+                    if not clf.connect(rdwr={'on-connect': rdwr_connected}):
+                        break
+                else:
+                    if not clf.connect(rdwr={'on-connect': rdwr_connected},
+                                       llcp={'on-startup': llcp_startup,
+                                             'on-connect': llcp_connected},
+                                       terminate=terminate_loop):
+                        break
+            except Exception, e:
+                print "clf.connect failed"
+
+            global srv
+            if only_one and srv and srv.success:
+                raise SystemExit
+
+    except KeyboardInterrupt:
+        raise SystemExit
+    finally:
+        clf.close()
+
+    raise SystemExit
+
+if __name__ == '__main__':
+    main()
diff --git a/wpa_supplicant/examples/wps-nfc.py b/wpa_supplicant/examples/wps-nfc.py
index d6dec85..35d1270 100755
--- a/wpa_supplicant/examples/wps-nfc.py
+++ b/wpa_supplicant/examples/wps-nfc.py
@@ -10,7 +10,8 @@
 import sys
 import time
 import random
-import StringIO
+import threading
+import argparse
 
 import nfc
 import nfc.ndef
@@ -18,11 +19,13 @@
 import nfc.handover
 
 import logging
-logging.basicConfig()
 
 import wpaspy
 
 wpas_ctrl = '/var/run/wpa_supplicant'
+srv = None
+continue_loop = True
+terminate_now = False
 
 def wpas_connect():
     ifaces = []
@@ -50,7 +53,7 @@
     wpas = wpas_connect()
     if (wpas == None):
         return False
-    if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + message.encode("hex")):
+    if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
         return False
     return True
 
@@ -71,7 +74,10 @@
     wpas = wpas_connect()
     if (wpas == None):
         return None
-    return wpas.request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + uuid).rstrip().decode("hex")
+    ret = wpas.request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + uuid)
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
 
 
 def wpas_get_password_token():
@@ -93,8 +99,12 @@
     if (wpas == None):
         return None
     if uuid is None:
-        return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex")
-    return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + uuid).rstrip().decode("hex")
+        res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+    else:
+	res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + uuid).rstrip()
+    if "FAIL" in res:
+	return None
+    return res.decode("hex")
 
 
 def wpas_report_handover(req, sel, type):
@@ -107,12 +117,19 @@
 
 
 class HandoverServer(nfc.handover.HandoverServer):
-    def __init__(self):
-        super(HandoverServer, self).__init__()
+    def __init__(self, llc):
+        super(HandoverServer, self).__init__(llc)
+        self.sent_carrier = None
+        self.ho_server_processing = False
+        self.success = False
 
     def process_request(self, request):
+        self.ho_server_processing = True
         print "HandoverServer - request received"
-        print "Parsed handover request: " + request.pretty()
+        try:
+            print "Parsed handover request: " + request.pretty()
+        except Exception, e:
+            print e
 
         sel = nfc.ndef.HandoverSelectMessage(version="1.2")
 
@@ -120,7 +137,6 @@
             print "Remote carrier type: " + carrier.type
             if carrier.type == "application/vnd.wfa.wsc":
                 print "WPS carrier type match - add WPS carrier record"
-                self.received_carrier = carrier.record
                 data = wpas_get_handover_sel(self.uuid)
                 if data is None:
                     print "Could not get handover select carrier record from wpa_supplicant"
@@ -128,52 +144,24 @@
                 print "Handover select carrier record from wpa_supplicant:"
                 print data.encode("hex")
                 self.sent_carrier = data
+                wpas_report_handover(carrier.record, self.sent_carrier, "RESP")
 
                 message = nfc.ndef.Message(data);
                 sel.add_carrier(message[0], "active", message[1:])
 
         print "Handover select:"
-        print sel.pretty()
+        try:
+            print sel.pretty()
+        except Exception, e:
+            print e
         print str(sel).encode("hex")
 
         print "Sending handover select"
+        self.success = True
         return sel
 
 
-def wps_handover_resp(peer, uuid):
-    if uuid is None:
-        print "Trying to handle WPS handover"
-    else:
-        print "Trying to handle WPS handover with AP " + uuid
-
-    srv = HandoverServer()
-    srv.sent_carrier = None
-    srv.uuid = uuid
-
-    nfc.llcp.activate(peer);
-
-    try:
-        print "Trying handover";
-        srv.start()
-        print "Wait for disconnect"
-        while nfc.llcp.connected():
-            time.sleep(0.1)
-        print "Disconnected after handover"
-    except nfc.llcp.ConnectRefused:
-        print "Handover connection refused"
-        nfc.llcp.shutdown()
-        return
-
-    if srv.sent_carrier:
-        wpas_report_handover(srv.received_carrier, srv.sent_carrier, "RESP")
-
-    print "Remove peer"
-    nfc.llcp.shutdown()
-    print "Done with handover"
-    time.sleep(1)
-
-
-def wps_handover_init(peer):
+def wps_handover_init(llc):
     print "Trying to initiate WPS handover"
 
     data = wpas_get_handover_req()
@@ -181,30 +169,26 @@
         print "Could not get handover request carrier record from wpa_supplicant"
         return
     print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
-    record = nfc.ndef.Record()
-    f = StringIO.StringIO(data)
-    record._read(f)
-    record = nfc.ndef.HandoverCarrierRecord(record)
-    print "Parsed handover request carrier record:"
-    print record.pretty()
 
     message = nfc.ndef.HandoverRequestMessage(version="1.2")
     message.nonce = random.randint(0, 0xffff)
-    message.add_carrier(record, "active")
+    datamsg = nfc.ndef.Message(data)
+    message.add_carrier(datamsg[0], "active", datamsg[1:])
 
     print "Handover request:"
-    print message.pretty()
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+    print str(message).encode("hex")
 
-    nfc.llcp.activate(peer);
-
-    client = nfc.handover.HandoverClient()
+    client = nfc.handover.HandoverClient(llc)
     try:
         print "Trying handover";
         client.connect()
         print "Connected for handover"
     except nfc.llcp.ConnectRefused:
         print "Handover connection refused"
-        nfc.llcp.shutdown()
         client.close()
         return
 
@@ -217,42 +201,53 @@
     message = client._recv()
     if message is None:
         print "No response received"
-        nfc.llcp.shutdown()
         client.close()
         return
     if message.type != "urn:nfc:wkt:Hs":
         print "Response was not Hs - received: " + message.type
-        nfc.llcp.shutdown()
         client.close()
         return
 
     print "Received message"
-    print message.pretty()
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+    print str(message).encode("hex")
     message = nfc.ndef.HandoverSelectMessage(message)
     print "Handover select received"
-    print message.pretty()
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
 
     for carrier in message.carriers:
         print "Remote carrier type: " + carrier.type
         if carrier.type == "application/vnd.wfa.wsc":
             print "WPS carrier type match - send to wpa_supplicant"
             wpas_report_handover(data, carrier.record, "INIT")
-            wifi = nfc.ndef.WifiConfigRecord(carrier.record)
-            print wifi.pretty()
+            # nfcpy does not support the new format..
+            #wifi = nfc.ndef.WifiConfigRecord(carrier.record)
+            #print wifi.pretty()
 
     print "Remove peer"
-    nfc.llcp.shutdown()
     client.close()
     print "Done with handover"
+    global only_one
+    if only_one:
+        global continue_loop
+        continue_loop = False
 
+    global no_wait
+    if no_wait:
+        print "Trying to exit.."
+        global terminate_now
+        terminate_now = True
 
 def wps_tag_read(tag, wait_remove=True):
     success = False
     if len(tag.ndef.message):
-        message = nfc.ndef.Message(tag.ndef.message)
-        print "message type " + message.type
-
-        for record in message:
+        for record in tag.ndef.message:
             print "record type " + record.type
             if record.type == "application/vnd.wfa.wsc":
                 print "WPS tag - send to wpa_supplicant"
@@ -269,166 +264,190 @@
     return success
 
 
+def rdwr_connected_write(tag):
+    print "Tag found - writing"
+    global write_data
+    tag.ndef.message = str(write_data)
+    print "Done - remove tag"
+    global only_one
+    if only_one:
+        global continue_loop
+        continue_loop = False
+    global write_wait_remove
+    while write_wait_remove and tag.is_present:
+        time.sleep(0.1)
+
 def wps_write_config_tag(clf, id=None, wait_remove=True):
     print "Write WPS config token"
-    data = wpas_get_config_token(id)
-    if (data == None):
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_config_token(id)
+    if write_data == None:
         print "Could not get WPS config token from wpa_supplicant"
         sys.exit(1)
         return
-
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while wait_remove and tag.is_present:
-        time.sleep(0.1)
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
 
-def wps_write_er_config_tag(clf, uuid):
+def wps_write_er_config_tag(clf, uuid, wait_remove=True):
     print "Write WPS ER config token"
-    data = wpas_get_er_config_token(uuid)
-    if (data == None):
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_er_config_token(uuid)
+    if write_data == None:
         print "Could not get WPS config token from wpa_supplicant"
         return
 
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while tag.is_present:
-        time.sleep(0.1)
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
 
 def wps_write_password_tag(clf, wait_remove=True):
     print "Write WPS password token"
-    data = wpas_get_password_token()
-    if (data == None):
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_password_token()
+    if write_data == None:
         print "Could not get WPS password token from wpa_supplicant"
         return
 
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while wait_remove and tag.is_present:
-        time.sleep(0.1)
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
 
-def find_peer(clf):
-    while True:
-        if nfc.llcp.connected():
-            print "LLCP connected"
-        general_bytes = nfc.llcp.startup({})
-        peer = clf.listen(ord(os.urandom(1)) + 250, general_bytes)
-        if isinstance(peer, nfc.DEP):
-            print "listen -> DEP";
-            if peer.general_bytes.startswith("Ffm"):
-                print "Found DEP"
-                return peer
-            print "mismatch in general_bytes"
-            print peer.general_bytes
+def rdwr_connected(tag):
+    global only_one, no_wait
+    print "Tag connected: " + str(tag)
 
-        peer = clf.poll(general_bytes)
-        if isinstance(peer, nfc.DEP):
-            print "poll -> DEP";
-            if peer.general_bytes.startswith("Ffm"):
-                print "Found DEP"
-                return peer
-            print "mismatch in general_bytes"
-            print peer.general_bytes
+    if tag.ndef:
+        print "NDEF tag: " + tag.type
+        try:
+            print tag.ndef.message.pretty()
+        except Exception, e:
+            print e
+        success = wps_tag_read(tag, not only_one)
+        if only_one and success:
+            global continue_loop
+            continue_loop = False
+    else:
+        print "Not an NDEF tag - remove tag"
 
-        if peer:
-            print "Found tag"
-            return peer
+    return not no_wait
 
 
+def llcp_worker(llc):
+    global arg_uuid
+    if arg_uuid is None:
+        wps_handover_init(llc)
+        print "Exiting llcp_worker thread"
+        return
+
+    global srv
+    global wait_connection
+    while not wait_connection and srv.sent_carrier is None:
+        if srv.ho_server_processing:
+            time.sleep(0.025)
+
+def llcp_startup(clf, llc):
+    global arg_uuid
+    if arg_uuid:
+        print "Start LLCP server"
+        global srv
+        srv = HandoverServer(llc)
+        if arg_uuid is "ap":
+            print "Trying to handle WPS handover"
+            srv.uuid = None
+        else:
+            print "Trying to handle WPS handover with AP " + arg_uuid
+            srv.uuid = arg_uuid
+    return llc
+
+def llcp_connected(llc):
+    print "P2P LLCP connected"
+    global wait_connection
+    wait_connection = False
+    global arg_uuid
+    if arg_uuid:
+        global srv
+        srv.start()
+    else:
+        threading.Thread(target=llcp_worker, args=(llc,)).start()
+    print "llcp_connected returning"
+    return True
+
+
+def terminate_loop():
+    global terminate_now
+    return terminate_now
+
 def main():
     clf = nfc.ContactlessFrontend()
 
+    parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for WPS NFC operations')
+    parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+                        action='store_const', dest='loglevel',
+                        help='verbose debug output')
+    parser.add_argument('-q', const=logging.WARNING, action='store_const',
+                        dest='loglevel', help='be quiet')
+    parser.add_argument('--only-one', '-1', action='store_true',
+                        help='run only one operation and exit')
+    parser.add_argument('--no-wait', action='store_true',
+                        help='do not wait for tag to be removed before exiting')
+    parser.add_argument('--uuid',
+                        help='UUID of an AP (used for WPS ER operations)')
+    parser.add_argument('--id',
+                        help='network id (used for WPS ER operations)')
+    parser.add_argument('command', choices=['write-config',
+                                            'write-er-config',
+                                            'write-password'],
+                        nargs='?')
+    args = parser.parse_args()
+
+    global arg_uuid
+    arg_uuid = args.uuid
+
+    global only_one
+    only_one = args.only_one
+
+    global no_wait
+    no_wait = args.no_wait
+
+    logging.basicConfig(level=args.loglevel)
+
     try:
-        arg_uuid = None
-        if len(sys.argv) > 1 and sys.argv[1] != '-1':
-            arg_uuid = sys.argv[1]
-
-        if len(sys.argv) > 1 and sys.argv[1] == '-1':
-            only_one = True
-        else:
-            only_one = False
-
-        if len(sys.argv) > 1 and sys.argv[1] == "write-config":
-            wps_write_config_tag(clf)
+        if not clf.open("usb"):
+            print "Could not open connection with an NFC device"
             raise SystemExit
 
-        if len(sys.argv) > 1 and sys.argv[1] == "write-config-no-wait":
-            wps_write_config_tag(clf, wait_remove=False)
+        if args.command == "write-config":
+            wps_write_config_tag(clf, id=args.id, wait_remove=not args.no_wait)
             raise SystemExit
 
-        if len(sys.argv) > 2 and sys.argv[1] == "write-config-id":
-            wps_write_config_tag(clf, sys.argv[2])
+        if args.command == "write-er-config":
+            wps_write_er_config_tag(clf, args.uuid, wait_remove=not args.no_wait)
             raise SystemExit
 
-        if len(sys.argv) > 2 and sys.argv[1] == "write-er-config":
-            wps_write_er_config_tag(clf, sys.argv[2])
+        if args.command == "write-password":
+            wps_write_password_tag(clf, wait_remove=not args.no_wait)
             raise SystemExit
 
-        if len(sys.argv) > 1 and sys.argv[1] == "write-password":
-            wps_write_password_tag(clf)
-            raise SystemExit
-
-        if len(sys.argv) > 1 and sys.argv[1] == "write-password-no-wait":
-            wps_write_password_tag(clf, wait_remove=False)
-            raise SystemExit
-
-        while True:
+        global continue_loop
+        while continue_loop:
             print "Waiting for a tag or peer to be touched"
-
-            tag = find_peer(clf)
-            if isinstance(tag, nfc.DEP):
-                if arg_uuid is None:
-                    wps_handover_init(tag)
-                elif arg_uuid is "ap":
-                    wps_handover_resp(tag, None)
-                else:
-                    wps_handover_resp(tag, arg_uuid)
-                if only_one:
+            wait_connection = True
+            try:
+                if not clf.connect(rdwr={'on-connect': rdwr_connected},
+                                   llcp={'on-startup': llcp_startup,
+                                         'on-connect': llcp_connected},
+                                   terminate=terminate_loop):
                     break
-                continue
+            except Exception, e:
+                print "clf.connect failed"
 
-            if tag.ndef:
-                success = wps_tag_read(tag, not only_one)
-                if only_one:
-                    if not success:
-                        sys.exit(1)
-                    break
-                continue
-
-            print "Not an NDEF tag - remove tag"
-            if only_one:
-                sys.exit(1)
-            while tag.is_present:
-                time.sleep(0.1)
+            global srv
+            if only_one and srv and srv.success:
+                raise SystemExit
 
     except KeyboardInterrupt:
         raise SystemExit
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 889a949..8d6c381 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -20,6 +20,7 @@
 #include "ap/ap_config.h"
 #include "ap/sta_info.h"
 #include "ap/ap_drv_ops.h"
+#include "ap/wps_hostapd.h"
 #include "ap/p2p_hostapd.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "rsn_supp/wpa.h"
@@ -103,13 +104,15 @@
 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_join_start(struct wpa_supplicant *wpa_s, int freq,
+			       const u8 *ssid, size_t ssid_len);
 static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
 				   const u8 *ssid, size_t ssid_len);
 static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
 static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
 			 const u8 *dev_addr, enum p2p_wps_method wps_method,
-			 int auto_join, const u8 *ssid, size_t ssid_len);
+			 int auto_join, int freq,
+			 const u8 *ssid, size_t ssid_len);
 static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
@@ -580,6 +583,10 @@
 		bssid = wpa_s->bssid;
 
 	bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len);
+	if (bss == NULL && wpa_s->go_params &&
+	    !is_zero_ether_addr(wpa_s->go_params->peer_device_addr))
+		bss = wpa_bss_get_p2p_dev_addr(
+			wpa_s, wpa_s->go_params->peer_device_addr);
 	if (bss == NULL) {
 		u8 iface_addr[ETH_ALEN];
 		if (p2p_get_interface_addr(wpa_s->global->p2p, bssid,
@@ -1101,15 +1108,29 @@
 static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
 				    struct p2p_go_neg_results *res)
 {
-	wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR,
-		   MAC2STR(res->peer_interface_addr));
+	wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR
+		   " dev_addr " MACSTR " wps_method %d",
+		   MAC2STR(res->peer_interface_addr),
+		   MAC2STR(res->peer_device_addr), res->wps_method);
 	wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start WPS Enrollee for SSID",
 			  res->ssid, res->ssid_len);
 	wpa_supplicant_ap_deinit(wpa_s);
 	wpas_copy_go_neg_results(wpa_s, res);
-	if (res->wps_method == WPS_PBC)
+	if (res->wps_method == WPS_PBC) {
 		wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1);
-	else {
+#ifdef CONFIG_WPS_NFC
+	} else if (res->wps_method == WPS_NFC) {
+		wpas_wps_start_nfc(wpa_s, res->peer_device_addr,
+				   res->peer_interface_addr,
+				   wpa_s->parent->p2p_oob_dev_pw,
+				   wpa_s->parent->p2p_oob_dev_pw_id, 1,
+				   wpa_s->parent->p2p_oob_dev_pw_id ==
+				   DEV_PW_NFC_CONNECTION_HANDOVER ?
+				   wpa_s->parent->p2p_peer_oob_pubkey_hash :
+				   NULL,
+				   NULL, 0, 0);
+#endif /* CONFIG_WPS_NFC */
+	} else {
 		u16 dev_pw_id = DEV_PW_DEFAULT;
 		if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
 			dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
@@ -1233,10 +1254,24 @@
 			   "filtering");
 		return;
 	}
-	if (params->wps_method == WPS_PBC)
+	if (params->wps_method == WPS_PBC) {
 		wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr,
 					  params->peer_device_addr);
-	else if (wpa_s->p2p_pin[0])
+#ifdef CONFIG_WPS_NFC
+	} else if (params->wps_method == WPS_NFC) {
+		if (wpa_s->parent->p2p_oob_dev_pw_id !=
+		    DEV_PW_NFC_CONNECTION_HANDOVER &&
+		    !wpa_s->parent->p2p_oob_dev_pw) {
+			wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
+			return;
+		}
+		wpas_ap_wps_add_nfc_pw(
+			wpa_s, wpa_s->parent->p2p_oob_dev_pw_id,
+			wpa_s->parent->p2p_oob_dev_pw,
+			wpa_s->parent->p2p_peer_oob_pk_hash_known ?
+			wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL);
+#endif /* CONFIG_WPS_NFC */
+	} else if (wpa_s->p2p_pin[0])
 		wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
 					  wpa_s->p2p_pin, NULL, 0, 0);
 	os_free(wpa_s->go_params);
@@ -1345,6 +1380,11 @@
 	d->dtim_period = s->dtim_period;
 	d->disassoc_low_ack = s->disassoc_low_ack;
 	d->disable_scan_offload = s->disable_scan_offload;
+
+	if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey) {
+		d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey);
+		d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
+	}
 }
 
 
@@ -2791,7 +2831,7 @@
 		wpa_s->pending_pd_before_join = 0;
 		wpa_printf(MSG_DEBUG, "P2P: Starting pending "
 			   "join-existing-group operation");
-		wpas_p2p_join_start(wpa_s);
+		wpas_p2p_join_start(wpa_s, 0, NULL, 0);
 		return;
 	}
 
@@ -2835,7 +2875,7 @@
 		wpa_printf(MSG_DEBUG, "P2P: Starting pending "
 			   "join-existing-group operation (no ACK for PD "
 			   "Req attempts)");
-		wpas_p2p_join_start(wpa_s);
+		wpas_p2p_join_start(wpa_s, 0, NULL, 0);
 		return;
 	}
 
@@ -2861,7 +2901,8 @@
 				  const u8 *go_dev_addr, const u8 *ssid,
 				  size_t ssid_len, int *go, u8 *group_bssid,
 				  int *force_freq, int persistent_group,
-				  const struct p2p_channels *channels)
+				  const struct p2p_channels *channels,
+				  int dev_pw_id)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *s;
@@ -2880,6 +2921,21 @@
 				   "authorized invitation");
 			goto accept_inv;
 		}
+
+#ifdef CONFIG_WPS_NFC
+		if (dev_pw_id >= 0 && wpa_s->parent->p2p_nfc_tag_enabled &&
+		    dev_pw_id == wpa_s->parent->p2p_oob_dev_pw_id) {
+			wpa_printf(MSG_DEBUG, "P2P: Accept invitation based on local enabled NFC Tag");
+			wpa_s->parent->p2p_wps_method = WPS_NFC;
+			wpa_s->parent->pending_join_wps_method = WPS_NFC;
+			os_memcpy(wpa_s->parent->pending_join_dev_addr,
+				  go_dev_addr, ETH_ALEN);
+			os_memcpy(wpa_s->parent->pending_join_iface_addr,
+				  bssid, ETH_ALEN);
+			goto accept_inv;
+		}
+#endif /* CONFIG_WPS_NFC */
+
 		/*
 		 * Do not accept the invitation automatically; notify user and
 		 * request approval.
@@ -3004,7 +3060,7 @@
 		} else if (bssid) {
 			wpa_s->user_initiated_pd = 0;
 			wpas_p2p_join(wpa_s, bssid, go_dev_addr,
-				      wpa_s->p2p_wps_method, 0,
+				      wpa_s->p2p_wps_method, 0, op_freq,
 				      ssid, ssid_len);
 		}
 		return;
@@ -3190,6 +3246,8 @@
 static int wpas_p2p_disallowed_freq(struct wpa_global *global,
 				    unsigned int freq)
 {
+	if (freq_range_list_includes(&global->p2p_go_avoid_freq, freq))
+		return 1;
 	return freq_range_list_includes(&global->p2p_disallow_freq, freq);
 }
 
@@ -3846,6 +3904,9 @@
 	}
 	eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL);
 
+	wpabuf_free(wpa_s->p2p_oob_dev_pw);
+	wpa_s->p2p_oob_dev_pw = NULL;
+
 	/* TODO: remove group interface from the driver if this wpa_s instance
 	 * is on top of a P2P group interface */
 }
@@ -3940,7 +4001,9 @@
 			   go_intent, own_interface_addr, force_freq,
 			   persistent_group, ssid ? ssid->ssid : NULL,
 			   ssid ? ssid->ssid_len : 0,
-			   wpa_s->p2p_pd_before_go_neg, pref_freq);
+			   wpa_s->p2p_pd_before_go_neg, pref_freq,
+			   wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
+			   0);
 }
 
 
@@ -3957,7 +4020,9 @@
 	return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
 			     go_intent, own_interface_addr, force_freq,
 			     persistent_group, ssid ? ssid->ssid : NULL,
-			     ssid ? ssid->ssid_len : 0, pref_freq);
+			     ssid ? ssid->ssid_len : 0, pref_freq,
+			     wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
+			     0);
 }
 
 
@@ -4244,7 +4309,7 @@
 
 start:
 	/* Start join operation immediately */
-	wpas_p2p_join_start(wpa_s);
+	wpas_p2p_join_start(wpa_s, 0, NULL, 0);
 }
 
 
@@ -4345,11 +4410,12 @@
 
 static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
 			 const u8 *dev_addr, enum p2p_wps_method wps_method,
-			 int auto_join, const u8 *ssid, size_t ssid_len)
+			 int auto_join, int op_freq,
+			 const u8 *ssid, size_t ssid_len)
 {
 	wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
-		   MACSTR " dev " MACSTR ")%s",
-		   MAC2STR(iface_addr), MAC2STR(dev_addr),
+		   MACSTR " dev " MACSTR " op_freq=%d)%s",
+		   MAC2STR(iface_addr), MAC2STR(dev_addr), op_freq,
 		   auto_join ? " (auto_join)" : "");
 	if (ssid && ssid_len) {
 		wpa_printf(MSG_DEBUG, "P2P: Group SSID specified: %s",
@@ -4366,12 +4432,13 @@
 	wpas_p2p_stop_find(wpa_s);
 
 	wpa_s->p2p_join_scan_count = 0;
-	wpas_p2p_join_scan_req(wpa_s, 0, ssid, ssid_len);
+	wpas_p2p_join_scan_req(wpa_s, op_freq, ssid, ssid_len);
 	return 0;
 }
 
 
-static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
+			       const u8 *ssid, size_t ssid_len)
 {
 	struct wpa_supplicant *group;
 	struct p2p_go_neg_results res;
@@ -4399,17 +4466,25 @@
 	group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg;
 
 	os_memset(&res, 0, sizeof(res));
+	os_memcpy(res.peer_device_addr, wpa_s->pending_join_dev_addr, ETH_ALEN);
 	os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
 		  ETH_ALEN);
 	res.wps_method = wpa_s->pending_join_wps_method;
-	bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->pending_join_iface_addr);
-	if (bss) {
-		res.freq = bss->freq;
-		res.ssid_len = bss->ssid_len;
-		os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
-		wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency "
-			   "from BSS table: %d MHz (SSID %s)", bss->freq,
-			   wpa_ssid_txt(bss->ssid, bss->ssid_len));
+	if (freq && ssid && ssid_len) {
+		res.freq = freq;
+		res.ssid_len = ssid_len;
+		os_memcpy(res.ssid, ssid, ssid_len);
+	} else {
+		bss = wpa_bss_get_bssid_latest(wpa_s,
+					       wpa_s->pending_join_iface_addr);
+		if (bss) {
+			res.freq = bss->freq;
+			res.ssid_len = bss->ssid_len;
+			os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
+			wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)",
+				   bss->freq,
+				   wpa_ssid_txt(bss->ssid, bss->ssid_len));
+		}
 	}
 
 	if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
@@ -4621,7 +4696,7 @@
 		}
 		wpa_s->user_initiated_pd = 1;
 		if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
-				  auto_join, NULL, 0) < 0)
+				  auto_join, freq, NULL, 0) < 0)
 			return -1;
 		return ret;
 	}
@@ -5233,6 +5308,7 @@
 		cfg->max_clients = wpa_s->conf->max_num_sta;
 	os_memcpy(cfg->ssid, ssid->ssid, ssid->ssid_len);
 	cfg->ssid_len = ssid->ssid_len;
+	cfg->freq = ssid->frequency;
 	cfg->cb_ctx = wpa_s;
 	cfg->ie_update = wpas_p2p_ie_update;
 	cfg->idle_update = wpas_p2p_idle_update;
@@ -5662,7 +5738,7 @@
 
 	return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
 			  ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr,
-			  1, pref_freq);
+			  1, pref_freq, -1);
 }
 
 
@@ -5736,7 +5812,7 @@
 
 	return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
 			  ssid->ssid, ssid->ssid_len, force_freq,
-			  go_dev_addr, persistent, pref_freq);
+			  go_dev_addr, persistent, pref_freq, -1);
 }
 
 
@@ -5748,6 +5824,8 @@
 	int network_id = -1;
 	int persistent;
 	int freq;
+	u8 ip[3 * 4];
+	char ip_addr[100];
 
 	if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) {
 		eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
@@ -5772,23 +5850,33 @@
 
 	freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
 		(int) wpa_s->assoc_freq;
+
+	ip_addr[0] = '\0';
+	if (wpa_sm_get_p2p_ip_addr(wpa_s->wpa, ip) == 0) {
+		os_snprintf(ip_addr, sizeof(ip_addr), " ip_addr=%u.%u.%u.%u "
+			    "ip_mask=%u.%u.%u.%u go_ip_addr=%u.%u.%u.%u",
+			    ip[0], ip[1], ip[2], ip[3],
+			    ip[4], ip[5], ip[6], ip[7],
+			    ip[8], ip[9], ip[10], ip[11]);
+	}
+
 	if (ssid->passphrase == NULL && ssid->psk_set) {
 		char psk[65];
 		wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
 		wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
 			       "%s client ssid=\"%s\" freq=%d psk=%s "
-			       "go_dev_addr=" MACSTR "%s",
+			       "go_dev_addr=" MACSTR "%s%s",
 			       wpa_s->ifname, ssid_txt, freq, psk,
 			       MAC2STR(go_dev_addr),
-			       persistent ? " [PERSISTENT]" : "");
+			       persistent ? " [PERSISTENT]" : "", ip_addr);
 	} else {
 		wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
 			       "%s client ssid=\"%s\" freq=%d "
-			       "passphrase=\"%s\" go_dev_addr=" MACSTR "%s",
+			       "passphrase=\"%s\" go_dev_addr=" MACSTR "%s%s",
 			       wpa_s->ifname, ssid_txt, freq,
 			       ssid->passphrase ? ssid->passphrase : "",
 			       MAC2STR(go_dev_addr),
-			       persistent ? " [PERSISTENT]" : "");
+			       persistent ? " [PERSISTENT]" : "", ip_addr);
 	}
 
 	if (persistent)
@@ -6920,3 +7008,570 @@
 	wpa_s->p2p_last_4way_hs_fail = ssid;
 	return 0;
 }
+
+
+#ifdef CONFIG_WPS_NFC
+
+static struct wpabuf * wpas_p2p_nfc_handover(int ndef, struct wpabuf *wsc,
+					     struct wpabuf *p2p)
+{
+	struct wpabuf *ret;
+	size_t wsc_len;
+
+	if (p2p == NULL) {
+		wpabuf_free(wsc);
+		wpa_printf(MSG_DEBUG, "P2P: No p2p buffer for handover");
+		return NULL;
+	}
+
+	wsc_len = wsc ? wpabuf_len(wsc) : 0;
+	ret = wpabuf_alloc(2 + wsc_len + 2 + wpabuf_len(p2p));
+	if (ret == NULL) {
+		wpabuf_free(wsc);
+		wpabuf_free(p2p);
+		return NULL;
+	}
+
+	wpabuf_put_be16(ret, wsc_len);
+	if (wsc)
+		wpabuf_put_buf(ret, wsc);
+	wpabuf_put_be16(ret, wpabuf_len(p2p));
+	wpabuf_put_buf(ret, p2p);
+
+	wpabuf_free(wsc);
+	wpabuf_free(p2p);
+	wpa_hexdump_buf(MSG_DEBUG,
+			"P2P: Generated NFC connection handover message", ret);
+
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_p2p(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL) {
+			wpa_printf(MSG_DEBUG, "P2P: Failed to NDEF encapsulate handover request");
+			return NULL;
+		}
+		ret = tmp;
+	}
+
+	return ret;
+}
+
+
+static int wpas_p2p_cli_freq(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid **ssid, u8 *go_dev_addr)
+{
+	struct wpa_supplicant *iface;
+
+	if (go_dev_addr)
+		os_memset(go_dev_addr, 0, ETH_ALEN);
+	if (ssid)
+		*ssid = NULL;
+	for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+		if (iface->wpa_state < WPA_ASSOCIATING ||
+		    iface->current_ssid == NULL || iface->assoc_freq == 0 ||
+		    !iface->current_ssid->p2p_group ||
+		    iface->current_ssid->mode != WPAS_MODE_INFRA)
+			continue;
+		if (ssid)
+			*ssid = iface->current_ssid;
+		if (go_dev_addr)
+			os_memcpy(go_dev_addr, iface->go_dev_addr, ETH_ALEN);
+		return iface->assoc_freq;
+	}
+	return 0;
+}
+
+
+struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
+					  int ndef)
+{
+	struct wpabuf *wsc, *p2p;
+	struct wpa_ssid *ssid;
+	u8 go_dev_addr[ETH_ALEN];
+	int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: P2P disabled - cannot build handover request");
+		return NULL;
+	}
+
+	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+	    wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+			   &wpa_s->conf->wps_nfc_dh_privkey) < 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No DH key available for handover request");
+		return NULL;
+	}
+
+	if (cli_freq == 0) {
+		wsc = wps_build_nfc_handover_req_p2p(
+			wpa_s->parent->wps, wpa_s->conf->wps_nfc_dh_pubkey);
+	} else
+		wsc = NULL;
+	p2p = p2p_build_nfc_handover_req(wpa_s->global->p2p, cli_freq,
+					 go_dev_addr, ssid ? ssid->ssid : NULL,
+					 ssid ? ssid->ssid_len : 0);
+
+	return wpas_p2p_nfc_handover(ndef, wsc, p2p);
+}
+
+
+struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+					  int ndef, int tag)
+{
+	struct wpabuf *wsc, *p2p;
+	struct wpa_ssid *ssid;
+	u8 go_dev_addr[ETH_ALEN];
+	int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return NULL;
+
+	if (!tag && wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+	    wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+			   &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+		return NULL;
+
+	if (cli_freq == 0) {
+		wsc = wps_build_nfc_handover_sel_p2p(
+			wpa_s->parent->wps,
+			tag ? wpa_s->conf->wps_nfc_dev_pw_id :
+			DEV_PW_NFC_CONNECTION_HANDOVER,
+			wpa_s->conf->wps_nfc_dh_pubkey,
+			tag ? wpa_s->conf->wps_nfc_dev_pw : NULL);
+	} else
+		wsc = NULL;
+	p2p = p2p_build_nfc_handover_sel(wpa_s->global->p2p, cli_freq,
+					 go_dev_addr, ssid ? ssid->ssid : NULL,
+					 ssid ? ssid->ssid_len : 0);
+
+	return wpas_p2p_nfc_handover(ndef, wsc, p2p);
+}
+
+
+static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s,
+				   struct p2p_nfc_params *params)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Initiate join-group based on NFC "
+		   "connection handover (freq=%d)",
+		   params->go_freq);
+
+	if (params->go_freq && params->go_ssid_len) {
+		wpa_s->p2p_wps_method = WPS_NFC;
+		wpa_s->pending_join_wps_method = WPS_NFC;
+		os_memset(wpa_s->pending_join_iface_addr, 0, ETH_ALEN);
+		os_memcpy(wpa_s->pending_join_dev_addr, params->go_dev_addr,
+			  ETH_ALEN);
+		return wpas_p2p_join_start(wpa_s, params->go_freq,
+					   params->go_ssid,
+					   params->go_ssid_len);
+	}
+
+	return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+				WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent,
+				params->go_freq, -1, 0, 1, 1);
+}
+
+
+static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s,
+				  struct p2p_nfc_params *params, int tag)
+{
+	int res, persistent;
+	struct wpa_ssid *ssid;
+
+	wpa_printf(MSG_DEBUG, "P2P: Authorize join-group based on NFC "
+		   "connection handover");
+	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		ssid = wpa_s->current_ssid;
+		if (ssid == NULL)
+			continue;
+		if (ssid->mode != WPAS_MODE_P2P_GO)
+			continue;
+		if (wpa_s->ap_iface == NULL)
+			continue;
+		break;
+	}
+	if (wpa_s == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: Could not find GO interface");
+		return -1;
+	}
+
+	if (wpa_s->parent->p2p_oob_dev_pw_id !=
+	    DEV_PW_NFC_CONNECTION_HANDOVER &&
+	    !wpa_s->parent->p2p_oob_dev_pw) {
+		wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
+		return -1;
+	}
+	res = wpas_ap_wps_add_nfc_pw(
+		wpa_s, wpa_s->parent->p2p_oob_dev_pw_id,
+		wpa_s->parent->p2p_oob_dev_pw,
+		wpa_s->parent->p2p_peer_oob_pk_hash_known ?
+		wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL);
+	if (res)
+		return res;
+
+	if (!tag) {
+		wpa_printf(MSG_DEBUG, "P2P: Negotiated handover - wait for peer to join without invitation");
+		return 0;
+	}
+
+	if (!params->peer ||
+	    !(params->peer->dev_capab & P2P_DEV_CAPAB_INVITATION_PROCEDURE))
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "P2P: Static handover - invite peer " MACSTR
+		   " to join", MAC2STR(params->peer->p2p_device_addr));
+
+	wpa_s->global->p2p_invite_group = wpa_s;
+	persistent = ssid->p2p_persistent_group &&
+		wpas_p2p_get_persistent(wpa_s->parent,
+					params->peer->p2p_device_addr,
+					ssid->ssid, ssid->ssid_len);
+	wpa_s->parent->pending_invite_ssid_id = -1;
+
+	return p2p_invite(wpa_s->global->p2p, params->peer->p2p_device_addr,
+			  P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr,
+			  ssid->ssid, ssid->ssid_len, ssid->frequency,
+			  wpa_s->global->p2p_dev_addr, persistent, 0,
+			  wpa_s->parent->p2p_oob_dev_pw_id);
+}
+
+
+static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s,
+				    struct p2p_nfc_params *params,
+				    int forced_freq)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Initiate GO Negotiation based on NFC "
+		   "connection handover");
+	return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+				WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent,
+				forced_freq, -1, 0, 1, 1);
+}
+
+
+static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s,
+				    struct p2p_nfc_params *params,
+				    int forced_freq)
+{
+	int res;
+
+	wpa_printf(MSG_DEBUG, "P2P: Authorize GO Negotiation based on NFC "
+		   "connection handover");
+	res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+			       WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent,
+			       forced_freq, -1, 0, 1, 1);
+	if (res)
+		return res;
+
+	res = wpas_p2p_listen(wpa_s, 60);
+	if (res) {
+		p2p_unauthorize(wpa_s->global->p2p,
+				params->peer->p2p_device_addr);
+	}
+
+	return res;
+}
+
+
+static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s,
+					    const struct wpabuf *data,
+					    int sel, int tag, int forced_freq)
+{
+	const u8 *pos, *end;
+	u16 len, id;
+	struct p2p_nfc_params params;
+	int res;
+
+	os_memset(&params, 0, sizeof(params));
+	params.sel = sel;
+
+	wpa_hexdump_buf(MSG_DEBUG, "P2P: Received NFC tag payload", data);
+
+	pos = wpabuf_head(data);
+	end = pos + wpabuf_len(data);
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of WSC "
+			   "attributes");
+		return -1;
+	}
+	len = WPA_GET_BE16(pos);
+	pos += 2;
+	if (pos + len > end) {
+		wpa_printf(MSG_DEBUG, "P2P: Not enough data for WSC "
+			   "attributes");
+		return -1;
+	}
+	params.wsc_attr = pos;
+	params.wsc_len = len;
+	pos += len;
+
+	if (end - pos < 2) {
+		wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of P2P "
+			   "attributes");
+		return -1;
+	}
+	len = WPA_GET_BE16(pos);
+	pos += 2;
+	if (pos + len > end) {
+		wpa_printf(MSG_DEBUG, "P2P: Not enough data for P2P "
+			   "attributes");
+		return -1;
+	}
+	params.p2p_attr = pos;
+	params.p2p_len = len;
+	pos += len;
+
+	wpa_hexdump(MSG_DEBUG, "P2P: WSC attributes",
+		    params.wsc_attr, params.wsc_len);
+	wpa_hexdump(MSG_DEBUG, "P2P: P2P attributes",
+		    params.p2p_attr, params.p2p_len);
+	if (pos < end) {
+		wpa_hexdump(MSG_DEBUG,
+			    "P2P: Ignored extra data after P2P attributes",
+			    pos, end - pos);
+	}
+
+	res = p2p_process_nfc_connection_handover(wpa_s->global->p2p, &params);
+	if (res)
+		return res;
+
+	if (params.next_step == NO_ACTION)
+		return 0;
+
+	if (params.next_step == BOTH_GO) {
+		wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_BOTH_GO "peer=" MACSTR,
+			MAC2STR(params.peer->p2p_device_addr));
+		return 0;
+	}
+
+	if (params.next_step == PEER_CLIENT) {
+		if (!is_zero_ether_addr(params.go_dev_addr)) {
+			wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
+				"peer=" MACSTR " freq=%d go_dev_addr=" MACSTR
+				" ssid=\"%s\"",
+				MAC2STR(params.peer->p2p_device_addr),
+				params.go_freq,
+				MAC2STR(params.go_dev_addr),
+				wpa_ssid_txt(params.go_ssid,
+					     params.go_ssid_len));
+		} else {
+			wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
+				"peer=" MACSTR " freq=%d",
+				MAC2STR(params.peer->p2p_device_addr),
+				params.go_freq);
+		}
+		return 0;
+	}
+
+	if (wpas_p2p_cli_freq(wpa_s, NULL, NULL)) {
+		wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_WHILE_CLIENT "peer="
+			MACSTR, MAC2STR(params.peer->p2p_device_addr));
+		return 0;
+	}
+
+	wpabuf_free(wpa_s->p2p_oob_dev_pw);
+	wpa_s->p2p_oob_dev_pw = NULL;
+
+	if (params.oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+		wpa_printf(MSG_DEBUG, "P2P: No peer OOB Dev Pw "
+			   "received");
+		return -1;
+	}
+
+	id = WPA_GET_BE16(params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN);
+	wpa_printf(MSG_DEBUG, "P2P: Peer OOB Dev Pw %u", id);
+	wpa_hexdump(MSG_DEBUG, "P2P: Peer OOB Public Key hash",
+		    params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
+	os_memcpy(wpa_s->p2p_peer_oob_pubkey_hash,
+		  params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
+	wpa_s->p2p_peer_oob_pk_hash_known = 1;
+
+	if (tag) {
+		if (id < 0x10) {
+			wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid "
+				   "peer OOB Device Password Id %u", id);
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "P2P: Static handover - use peer OOB "
+			   "Device Password Id %u", id);
+		wpa_hexdump_key(MSG_DEBUG, "P2P: Peer OOB Device Password",
+				params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
+				params.oob_dev_pw_len -
+				WPS_OOB_PUBKEY_HASH_LEN - 2);
+		wpa_s->p2p_oob_dev_pw_id = id;
+		wpa_s->p2p_oob_dev_pw = wpabuf_alloc_copy(
+			params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
+			params.oob_dev_pw_len -
+			WPS_OOB_PUBKEY_HASH_LEN - 2);
+		if (wpa_s->p2p_oob_dev_pw == NULL)
+			return -1;
+
+		if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+		    wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+				   &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+			return -1;
+	} else {
+		wpa_printf(MSG_DEBUG, "P2P: Using abbreviated WPS handshake "
+			   "without Device Password");
+		wpa_s->p2p_oob_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+	}
+
+	switch (params.next_step) {
+	case NO_ACTION:
+	case BOTH_GO:
+	case PEER_CLIENT:
+		/* already covered above */
+		return 0;
+	case JOIN_GROUP:
+		return wpas_p2p_nfc_join_group(wpa_s, &params);
+	case AUTH_JOIN:
+		return wpas_p2p_nfc_auth_join(wpa_s, &params, tag);
+	case INIT_GO_NEG:
+		return wpas_p2p_nfc_init_go_neg(wpa_s, &params, forced_freq);
+	case RESP_GO_NEG:
+		/* TODO: use own OOB Dev Pw */
+		return wpas_p2p_nfc_resp_go_neg(wpa_s, &params, forced_freq);
+	}
+
+	return -1;
+}
+
+
+int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s,
+			     const struct wpabuf *data, int forced_freq)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+
+	return wpas_p2p_nfc_connection_handover(wpa_s, data, 1, 1, forced_freq);
+}
+
+
+int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
+				 const struct wpabuf *req,
+				 const struct wpabuf *sel, int forced_freq)
+{
+	struct wpabuf *tmp;
+	int ret;
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "NFC: P2P connection handover reported");
+
+	wpa_hexdump_ascii(MSG_DEBUG, "NFC: Req",
+			  wpabuf_head(req), wpabuf_len(req));
+	wpa_hexdump_ascii(MSG_DEBUG, "NFC: Sel",
+			  wpabuf_head(sel), wpabuf_len(sel));
+	if (forced_freq)
+		wpa_printf(MSG_DEBUG, "NFC: Forced freq %d", forced_freq);
+	tmp = ndef_parse_p2p(init ? sel : req);
+	if (tmp == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: Could not parse NDEF");
+		return -1;
+	}
+
+	ret = wpas_p2p_nfc_connection_handover(wpa_s, tmp, init, 0,
+					       forced_freq);
+	wpabuf_free(tmp);
+
+	return ret;
+}
+
+
+int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled)
+{
+	const u8 *if_addr;
+	int go_intent = wpa_s->conf->p2p_go_intent;
+	struct wpa_supplicant *iface;
+
+	if (wpa_s->global->p2p == NULL)
+		return -1;
+
+	if (!enabled) {
+		wpa_printf(MSG_DEBUG, "P2P: Disable use of own NFC Tag");
+		for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
+		{
+			if (!iface->ap_iface)
+				continue;
+			hostapd_wps_nfc_token_disable(iface->ap_iface->bss[0]);
+		}
+		p2p_set_authorized_oob_dev_pw_id(wpa_s->global->p2p, 0,
+						 0, NULL);
+		if (wpa_s->p2p_nfc_tag_enabled)
+			wpas_p2p_remove_pending_group_interface(wpa_s);
+		wpa_s->p2p_nfc_tag_enabled = 0;
+		return 0;
+	}
+
+	if (wpa_s->global->p2p_disabled)
+		return -1;
+
+	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
+	    wpa_s->conf->wps_nfc_dh_privkey == NULL ||
+	    wpa_s->conf->wps_nfc_dev_pw == NULL ||
+	    wpa_s->conf->wps_nfc_dev_pw_id < 0x10) {
+		wpa_printf(MSG_DEBUG, "P2P: NFC password token not configured "
+			   "to allow static handover cases");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "P2P: Enable use of own NFC Tag");
+
+	wpa_s->p2p_oob_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+	wpabuf_free(wpa_s->p2p_oob_dev_pw);
+	wpa_s->p2p_oob_dev_pw = wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
+	if (wpa_s->p2p_oob_dev_pw == NULL)
+		return -1;
+	wpa_s->p2p_peer_oob_pk_hash_known = 0;
+
+	wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
+
+	if (wpa_s->create_p2p_iface) {
+		enum wpa_driver_if_type iftype;
+		/* Prepare to add a new interface for the group */
+		iftype = WPA_IF_P2P_GROUP;
+		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_addr = wpa_s->pending_interface_addr;
+	} else
+		if_addr = wpa_s->own_addr;
+
+	wpa_s->p2p_nfc_tag_enabled = enabled;
+
+	for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+		struct hostapd_data *hapd;
+		if (iface->ap_iface == NULL)
+			continue;
+		hapd = iface->ap_iface->bss[0];
+		wpabuf_free(hapd->conf->wps_nfc_dh_pubkey);
+		hapd->conf->wps_nfc_dh_pubkey =
+			wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+		wpabuf_free(hapd->conf->wps_nfc_dh_privkey);
+		hapd->conf->wps_nfc_dh_privkey =
+			wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+		wpabuf_free(hapd->conf->wps_nfc_dev_pw);
+		hapd->conf->wps_nfc_dev_pw =
+			wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
+		hapd->conf->wps_nfc_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+
+		if (hostapd_wps_nfc_token_enable(iface->ap_iface->bss[0]) < 0) {
+			wpa_dbg(iface, MSG_DEBUG,
+				"P2P: Failed to enable NFC Tag for GO");
+		}
+	}
+	p2p_set_authorized_oob_dev_pw_id(
+		wpa_s->global->p2p, wpa_s->conf->wps_nfc_dev_pw_id, go_intent,
+		if_addr);
+
+	return 0;
+}
+
+#endif /* CONFIG_WPS_NFC */
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 7abfb12..685313c 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -148,6 +148,16 @@
 			 const u8 *psk, size_t psk_len);
 void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer,
 			    int iface_addr);
+struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
+					  int ndef);
+struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+					  int ndef, int tag);
+int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s,
+			     const struct wpabuf *data, int forced_freq);
+int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
+				 const struct wpabuf *req,
+				 const struct wpabuf *sel, int forced_freq);
+int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled);
 
 #ifdef CONFIG_P2P
 int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s);
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index aa9c32d..35f5693 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1565,6 +1565,8 @@
 				"disallows" : "allows");
 		}
 	}
+
+	os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_HS20
@@ -3927,6 +3929,7 @@
 	os_free(global->params.override_ctrl_interface);
 
 	os_free(global->p2p_disallow_freq.range);
+	os_free(global->p2p_go_avoid_freq.range);
 	os_free(global->add_psk);
 
 	os_free(global);
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 078856e..267c226 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -12,6 +12,7 @@
 #include "utils/list.h"
 #include "common/defs.h"
 #include "common/sae.h"
+#include "wps/wps_defs.h"
 #include "config_ssid.h"
 
 extern const char *wpa_supplicant_version;
@@ -252,6 +253,7 @@
 	int p2p_disabled;
 	int cross_connection;
 	struct wpa_freq_range_list p2p_disallow_freq;
+	struct wpa_freq_range_list p2p_go_avoid_freq;
 	enum wpa_conc_pref {
 		WPA_CONC_PREF_NOT_SET,
 		WPA_CONC_PREF_STA,
@@ -712,6 +714,9 @@
 	unsigned int p2p_go_group_formation_completed:1;
 	unsigned int waiting_presence_resp;
 	int p2p_first_connection_timeout;
+	unsigned int p2p_nfc_tag_enabled:1;
+	unsigned int p2p_peer_oob_pk_hash_known:1;
+	unsigned int p2p_disable_ip_addr_req:1;
 	int p2p_persistent_go_freq;
 	int p2p_persistent_id;
 	int p2p_go_intent;
@@ -721,6 +726,12 @@
 	struct wpa_radio_work *p2p_scan_work;
 	struct wpa_radio_work *p2p_listen_work;
 	struct wpa_radio_work *p2p_send_action_work;
+
+	u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */
+	struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group
+					* formation */
+	u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+	u8 p2p_ip_addr_info[3 * 4];
 #endif /* CONFIG_P2P */
 
 	struct wpa_ssid *bgscan_ssid;
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index ad0895b..e8a4b35 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -948,6 +948,22 @@
 		conf.ssid = ssid->ssid;
 		conf.ssid_len = ssid->ssid_len;
 		conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey;
+#ifdef CONFIG_P2P
+		if (ssid->p2p_group && wpa_s->current_bss &&
+		    !wpa_s->p2p_disable_ip_addr_req) {
+			struct wpabuf *p2p;
+			p2p = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss,
+							  P2P_IE_VENDOR_TYPE);
+			if (p2p) {
+				u8 group_capab;
+				group_capab = p2p_get_group_capab(p2p);
+				if (group_capab &
+				    P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION)
+					conf.p2p = 1;
+				wpabuf_free(p2p);
+			}
+		}
+#endif /* CONFIG_P2P */
 	}
 	wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL);
 }
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 038c7fa..537aac3 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -911,7 +911,8 @@
 
 
 static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
-					      int registrar, const u8 *bssid)
+					      int registrar, const u8 *dev_addr,
+					      const u8 *bssid)
 {
 	struct wpa_ssid *ssid;
 
@@ -931,6 +932,11 @@
 		return NULL;
 	}
 
+#ifdef CONFIG_P2P
+	if (dev_addr)
+		os_memcpy(ssid->go_p2p_dev_addr, dev_addr, ETH_ALEN);
+#endif /* CONFIG_P2P */
+
 	if (bssid) {
 #ifndef CONFIG_P2P
 		struct wpa_bss *bss;
@@ -1011,13 +1017,17 @@
 
 
 static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
-			     struct wpa_ssid *selected, const u8 *bssid)
+			     struct wpa_ssid *selected, const u8 *bssid,
+			     int freq)
 {
 	struct wpa_bss *bss;
 
 	wpa_s->after_wps = 0;
 	wpa_s->known_wps_freq = 0;
-	if (bssid) {
+	if (freq) {
+		wpa_s->after_wps = 5;
+		wpa_s->wps_freq = freq;
+	} else if (bssid) {
 		bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
 		if (bss && bss->freq > 0) {
 			wpa_s->known_wps_freq = 1;
@@ -1044,7 +1054,7 @@
 {
 	struct wpa_ssid *ssid;
 	wpas_clear_wps(wpa_s);
-	ssid = wpas_wps_add_network(wpa_s, 0, bssid);
+	ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid);
 	if (ssid == NULL)
 		return -1;
 	ssid->temporary = 1;
@@ -1067,24 +1077,47 @@
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
 			       wpa_s, NULL);
-	wpas_wps_reassoc(wpa_s, ssid, bssid);
+	wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
 	return 0;
 }
 
 
-int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
-		       const char *pin, int p2p_group, u16 dev_pw_id)
+static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s,
+				 const u8 *dev_addr, const u8 *bssid,
+				 const char *pin, int p2p_group, u16 dev_pw_id,
+				 const u8 *peer_pubkey_hash,
+				 const u8 *ssid_val, size_t ssid_len, int freq)
 {
 	struct wpa_ssid *ssid;
-	char val[128];
+	char val[128 + 2 * WPS_OOB_PUBKEY_HASH_LEN];
 	unsigned int rpin = 0;
+	char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10];
 
 	wpas_clear_wps(wpa_s);
-	ssid = wpas_wps_add_network(wpa_s, 0, bssid);
-	if (ssid == NULL)
+	if (bssid && is_zero_ether_addr(bssid))
+		bssid = NULL;
+	ssid = wpas_wps_add_network(wpa_s, 0, dev_addr, bssid);
+	if (ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Could not add network");
 		return -1;
+	}
 	ssid->temporary = 1;
 	ssid->p2p_group = p2p_group;
+	if (ssid_val) {
+		ssid->ssid = os_malloc(ssid_len);
+		if (ssid->ssid) {
+			os_memcpy(ssid->ssid, ssid_val, ssid_len);
+			ssid->ssid_len = ssid_len;
+		}
+	}
+	if (peer_pubkey_hash) {
+		os_memcpy(hash, " pkhash=", 8);
+		wpa_snprintf_hex_uppercase(hash + 8, sizeof(hash) - 8,
+					   peer_pubkey_hash,
+					   WPS_OOB_PUBKEY_HASH_LEN);
+	} else {
+		hash[0] = '\0';
+	}
 #ifdef CONFIG_P2P
 	if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
 		ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
@@ -1098,25 +1131,38 @@
 	}
 #endif /* CONFIG_P2P */
 	if (pin)
-		os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"",
-			    pin, dev_pw_id);
-	else {
+		os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u%s\"",
+			    pin, dev_pw_id, hash);
+	else if (pin == NULL && dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+		os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"",
+			    dev_pw_id, hash);
+	} else {
 		rpin = wps_generate_pin();
-		os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"",
-			    rpin, dev_pw_id);
+		os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"",
+			    rpin, dev_pw_id, hash);
 	}
-	if (wpa_config_set(ssid, "phase1", val, 0) < 0)
+	if (wpa_config_set(ssid, "phase1", val, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Failed to set phase1 '%s'", val);
 		return -1;
+	}
 	if (wpa_s->wps_fragment_size)
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
 			       wpa_s, NULL);
 	wpa_s->wps_ap_iter = 1;
-	wpas_wps_reassoc(wpa_s, ssid, bssid);
+	wpas_wps_reassoc(wpa_s, ssid, bssid, freq);
 	return rpin;
 }
 
 
+int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+		       const char *pin, int p2p_group, u16 dev_pw_id)
+{
+	return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group,
+				     dev_pw_id, NULL, NULL, 0, 0);
+}
+
+
 /* Cancel the wps pbc/pin requests */
 int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
 {
@@ -1163,7 +1209,7 @@
 	if (!pin)
 		return -1;
 	wpas_clear_wps(wpa_s);
-	ssid = wpas_wps_add_network(wpa_s, 1, bssid);
+	ssid = wpas_wps_add_network(wpa_s, 1, NULL, bssid);
 	if (ssid == NULL)
 		return -1;
 	ssid->temporary = 1;
@@ -1191,7 +1237,7 @@
 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
 			       wpa_s, NULL);
-	wpas_wps_reassoc(wpa_s, ssid, bssid);
+	wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
 	return 0;
 }
 
@@ -1411,6 +1457,20 @@
 }
 
 
+#ifdef CONFIG_WPS_ER
+static void wpas_wps_nfc_clear(struct wps_context *wps)
+{
+	wps->ap_nfc_dev_pw_id = 0;
+	wpabuf_free(wps->ap_nfc_dh_pubkey);
+	wps->ap_nfc_dh_pubkey = NULL;
+	wpabuf_free(wps->ap_nfc_dh_privkey);
+	wps->ap_nfc_dh_privkey = NULL;
+	wpabuf_free(wps->ap_nfc_dev_pw);
+	wps->ap_nfc_dev_pw = NULL;
+}
+#endif /* CONFIG_WPS_ER */
+
+
 void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
 {
 	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
@@ -1424,6 +1484,7 @@
 #ifdef CONFIG_WPS_ER
 	wps_er_deinit(wpa_s->wps_er, NULL, NULL);
 	wpa_s->wps_er = NULL;
+	wpas_wps_nfc_clear(wpa_s->wps);
 #endif /* CONFIG_WPS_ER */
 
 	wps_registrar_deinit(wpa_s->wps->registrar);
@@ -2070,15 +2131,32 @@
 }
 
 
-int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *go_dev_addr,
+		       const u8 *bssid,
+		       const struct wpabuf *dev_pw, u16 dev_pw_id,
+		       int p2p_group, const u8 *peer_pubkey_hash,
+		       const u8 *ssid, size_t ssid_len, int freq)
 {
 	struct wps_context *wps = wpa_s->wps;
 	char pw[32 * 2 + 1];
 
+	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
+		dev_pw = wpa_s->conf->wps_nfc_dev_pw;
+		dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+	}
+
 	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
-	    wpa_s->conf->wps_nfc_dh_privkey == NULL ||
-	    wpa_s->conf->wps_nfc_dev_pw == NULL)
+	    wpa_s->conf->wps_nfc_dh_privkey == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Missing DH params - "
+			   "cannot start NFC-triggered connection");
 		return -1;
+	}
+
+	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: Missing Device Password (id=%u) - "
+			   "cannot start NFC-triggered connection", dev_pw_id);
+		return -1;
+	}
 
 	dh5_free(wps->dh_ctx);
 	wpabuf_free(wps->dh_pubkey);
@@ -2091,6 +2169,7 @@
 		wps->dh_pubkey = NULL;
 		wpabuf_free(wps->dh_privkey);
 		wps->dh_privkey = NULL;
+		wpa_printf(MSG_DEBUG, "WPS: Failed to get DH priv/pub key");
 		return -1;
 	}
 	wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
@@ -2099,14 +2178,19 @@
 		wps->dh_pubkey = NULL;
 		wpabuf_free(wps->dh_privkey);
 		wps->dh_privkey = NULL;
+		wpa_printf(MSG_DEBUG, "WPS: Failed to initialize DH context");
 		return -1;
 	}
 
-	wpa_snprintf_hex_uppercase(pw, sizeof(pw),
-				   wpabuf_head(wpa_s->conf->wps_nfc_dev_pw),
-				   wpabuf_len(wpa_s->conf->wps_nfc_dev_pw));
-	return wpas_wps_start_pin(wpa_s, bssid, pw, 0,
-				  wpa_s->conf->wps_nfc_dev_pw_id);
+	if (dev_pw) {
+		wpa_snprintf_hex_uppercase(pw, sizeof(pw),
+					   wpabuf_head(dev_pw),
+					   wpabuf_len(dev_pw));
+	}
+	return wpas_wps_start_dev_pw(wpa_s, go_dev_addr, bssid,
+				     dev_pw ? pw : NULL,
+				     p2p_group, dev_pw_id, peer_pubkey_hash,
+				     ssid, ssid_len, freq);
 }
 
 
@@ -2130,6 +2214,12 @@
 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
 		return 0;
 
+	if (!wpa_s->wps_ap_channel && attr->ap_channel) {
+		wpa_s->wps_ap_channel = WPA_GET_BE16(attr->ap_channel);
+		wpa_printf(MSG_DEBUG, "WPS: Credential container indicated AP Channel %d",
+			   wpa_s->wps_ap_channel);
+	}
+
 	wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
 		   "based on the received credential added");
 	wpa_s->normal_scans = 0;
@@ -2199,7 +2289,7 @@
 
 
 int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
-			  const struct wpabuf *data)
+			  const struct wpabuf *data, int forced_freq)
 {
 	const struct wpabuf *wps = data;
 	struct wpabuf *tmp = NULL;
@@ -2212,6 +2302,15 @@
 		/* Assume this contains full NDEF record */
 		tmp = ndef_parse_wifi(data);
 		if (tmp == NULL) {
+#ifdef CONFIG_P2P
+			tmp = ndef_parse_p2p(data);
+			if (tmp) {
+				ret = wpas_p2p_nfc_tag_process(wpa_s, tmp,
+							       forced_freq);
+				wpabuf_free(tmp);
+				return ret;
+			}
+#endif /* CONFIG_P2P */
 			wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
 			return -1;
 		}
@@ -2224,15 +2323,34 @@
 }
 
 
-struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, int cr)
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
+					  int ndef)
 {
-	if (cr)
-		return ndef_build_wifi_hc(1);
-	return ndef_build_wifi_hr();
+	struct wpabuf *ret;
+
+	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+	    wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+			   &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+		return NULL;
+
+	ret = wps_build_nfc_handover_req(wpa_s->wps,
+					 wpa_s->conf->wps_nfc_dh_pubkey);
+
+	if (ndef && ret) {
+		struct wpabuf *tmp;
+		tmp = ndef_build_wifi(ret);
+		wpabuf_free(ret);
+		if (tmp == NULL)
+			return NULL;
+		ret = tmp;
+	}
+
+	return ret;
 }
 
 
 #ifdef CONFIG_WPS_NFC
+
 static struct wpabuf *
 wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef,
 			     const char *uuid)
@@ -2241,8 +2359,9 @@
 	struct wpabuf *ret;
 	u8 u[UUID_LEN], *use_uuid = NULL;
 	u8 addr[ETH_ALEN], *use_addr = NULL;
+	struct wps_context *wps = wpa_s->wps;
 
-	if (!wpa_s->wps_er)
+	if (wps == NULL)
 		return NULL;
 
 	if (uuid == NULL)
@@ -2254,11 +2373,23 @@
 	else
 		return NULL;
 
-	/*
-	 * Handover Select carrier record for WPS uses the same format as
-	 * configuration token.
-	 */
-	ret = wps_er_nfc_config_token(wpa_s->wps_er, use_uuid, use_addr);
+	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL) {
+		if (wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+				   &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+			return NULL;
+	}
+
+	wpas_wps_nfc_clear(wps);
+	wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+	wps->ap_nfc_dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+	wps->ap_nfc_dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+	if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+		wpas_wps_nfc_clear(wps);
+		return NULL;
+	}
+
+	ret = wps_er_nfc_handover_sel(wpa_s->wps_er, wpa_s->wps, use_uuid,
+				      use_addr, wpa_s->conf->wps_nfc_dh_pubkey);
 	if (ndef && ret) {
 		struct wpabuf *tmp;
 		tmp = ndef_build_wifi(ret);
@@ -2301,17 +2432,121 @@
 				 const struct wpabuf *data)
 {
 	struct wpabuf *wps;
-	int ret;
+	int ret = -1;
+	u16 wsc_len;
+	const u8 *pos;
+	struct wpabuf msg;
+	struct wps_parse_attr attr;
+	u16 dev_pw_id;
+	const u8 *bssid = NULL;
+	int freq = 0;
 
 	wps = ndef_parse_wifi(data);
 	if (wps == NULL)
 		return -1;
 	wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
 		   "payload from NFC connection handover");
-	wpa_hexdump_buf_key(MSG_DEBUG, "WPS: NFC payload", wps);
-	ret = wpas_wps_nfc_tag_process(wpa_s, wps);
-	wpabuf_free(wps);
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+	if (wpabuf_len(wps) < 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Select "
+			   "Message");
+		goto out;
+	}
+	pos = wpabuf_head(wps);
+	wsc_len = WPA_GET_BE16(pos);
+	if (wsc_len > wpabuf_len(wps) - 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+			   "in Wi-Fi Handover Select Message", wsc_len);
+		goto out;
+	}
+	pos += 2;
 
+	wpa_hexdump(MSG_DEBUG,
+		    "WPS: WSC attributes in Wi-Fi Handover Select Message",
+		    pos, wsc_len);
+	if (wsc_len < wpabuf_len(wps) - 2) {
+		wpa_hexdump(MSG_DEBUG,
+			    "WPS: Ignore extra data after WSC attributes",
+			    pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+	}
+
+	wpabuf_set(&msg, pos, wsc_len);
+	ret = wps_parse_msg(&msg, &attr);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+			   "Wi-Fi Handover Select Message");
+		goto out;
+	}
+
+	if (attr.oob_dev_password == NULL ||
+	    attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+		wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+			   "included in Wi-Fi Handover Select Message");
+		ret = -1;
+		goto out;
+	}
+
+	if (attr.ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No SSID included in Wi-Fi Handover "
+			   "Select Message");
+		ret = -1;
+		goto out;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", attr.ssid, attr.ssid_len);
+
+	if (attr.mac_addr) {
+		bssid = attr.mac_addr;
+		wpa_printf(MSG_DEBUG, "WPS: MAC Address (BSSID): " MACSTR,
+			   MAC2STR(bssid));
+	}
+
+	if (attr.rf_bands)
+		wpa_printf(MSG_DEBUG, "WPS: RF Bands: %d", *attr.rf_bands);
+
+	if (attr.ap_channel) {
+		u16 chan = WPA_GET_BE16(attr.ap_channel);
+
+		wpa_printf(MSG_DEBUG, "WPS: AP Channel: %d", chan);
+
+		if (chan >= 1 && chan <= 13 &&
+		    (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_24GHZ))
+			freq = 2407 + 5 * chan;
+		else if (chan == 14 &&
+			 (attr.rf_bands == NULL ||
+			  *attr.rf_bands & WPS_RF_24GHZ))
+			freq = 2484;
+		else if (chan >= 30 &&
+			 (attr.rf_bands == NULL ||
+			  *attr.rf_bands & WPS_RF_50GHZ))
+			freq = 5000 + 5 * chan;
+
+		if (freq) {
+			wpa_printf(MSG_DEBUG,
+				   "WPS: AP indicated channel %u -> %u MHz",
+				   chan, freq);
+		}
+	}
+
+	wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+		    attr.oob_dev_password, attr.oob_dev_password_len);
+	dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+				 WPS_OOB_PUBKEY_HASH_LEN);
+	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+			   "%u in Wi-Fi Handover Select Message", dev_pw_id);
+		ret = -1;
+		goto out;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPS: AP Public Key hash",
+		    attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+	ret = wpas_wps_start_nfc(wpa_s, NULL, bssid, NULL, dev_pw_id, 0,
+				 attr.oob_dev_password,
+				 attr.ssid, attr.ssid_len, freq);
+
+out:
+	wpabuf_free(wps);
 	return ret;
 }
 
@@ -2326,6 +2561,100 @@
 	return wpas_wps_nfc_rx_handover_sel(wpa_s, sel);
 }
 
+
+int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel)
+{
+	struct wpabuf *wps;
+	int ret = -1;
+	u16 wsc_len;
+	const u8 *pos;
+	struct wpabuf msg;
+	struct wps_parse_attr attr;
+	u16 dev_pw_id;
+
+	/*
+	 * Enrollee/station is always initiator of the NFC connection handover,
+	 * so use the request message here to find Enrollee public key hash.
+	 */
+	wps = ndef_parse_wifi(req);
+	if (wps == NULL)
+		return -1;
+	wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+		   "payload from NFC connection handover");
+	wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+	if (wpabuf_len(wps) < 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
+			   "Message");
+		goto out;
+	}
+	pos = wpabuf_head(wps);
+	wsc_len = WPA_GET_BE16(pos);
+	if (wsc_len > wpabuf_len(wps) - 2) {
+		wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+			   "in rt Wi-Fi Handover Request Message", wsc_len);
+		goto out;
+	}
+	pos += 2;
+
+	wpa_hexdump(MSG_DEBUG,
+		    "WPS: WSC attributes in Wi-Fi Handover Request Message",
+		    pos, wsc_len);
+	if (wsc_len < wpabuf_len(wps) - 2) {
+		wpa_hexdump(MSG_DEBUG,
+			    "WPS: Ignore extra data after WSC attributes",
+			    pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+	}
+
+	wpabuf_set(&msg, pos, wsc_len);
+	ret = wps_parse_msg(&msg, &attr);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+			   "Wi-Fi Handover Request Message");
+		goto out;
+	}
+
+	if (attr.oob_dev_password == NULL ||
+	    attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+		wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+			   "included in Wi-Fi Handover Request Message");
+		ret = -1;
+		goto out;
+	}
+
+	if (attr.uuid_e == NULL) {
+		wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
+			   "Handover Request Message");
+		ret = -1;
+		goto out;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
+
+	wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+		    attr.oob_dev_password, attr.oob_dev_password_len);
+	dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+				 WPS_OOB_PUBKEY_HASH_LEN);
+	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+		wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+			   "%u in Wi-Fi Handover Request Message", dev_pw_id);
+		ret = -1;
+		goto out;
+	}
+	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
+		    attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+	ret = wps_registrar_add_nfc_pw_token(wpa_s->wps->registrar,
+					     attr.oob_dev_password,
+					     DEV_PW_NFC_CONNECTION_HANDOVER,
+					     NULL, 0, 1);
+
+out:
+	wpabuf_free(wps);
+	return ret;
+}
+
 #endif /* CONFIG_WPS_NFC */
 
 
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index 3fcfbbe..86e9d09 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -64,10 +64,15 @@
 struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
 					  int ndef, const char *id_str);
 struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef);
-int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *dev_addr,
+		       const u8 *bssid,
+		       const struct wpabuf *dev_pw, u16 dev_pw_id,
+		       int p2p_group, const u8 *peer_pubkey_hash,
+		       const u8 *ssid, size_t ssid_len, int freq);
 int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
-			  const struct wpabuf *data);
-struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, int cr);
+			  const struct wpabuf *data, int forced_freq);
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
+					  int ndef);
 struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
 					  int ndef, int cr, const char *uuid);
 int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
@@ -77,6 +82,9 @@
 int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
 				 const struct wpabuf *req,
 				 const struct wpabuf *sel);
+int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+				    const struct wpabuf *req,
+				    const struct wpabuf *sel);
 void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
 			     struct wpa_scan_results *scan_res);
 void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);