[wpa_supplicant] Cumulative patch from commit dd2daf084

Bug: 156933657
Test: Confirm random dialog token usage from logs
Test: Verify Passpoint ANQP functionality and Passpoint association
Test: act.py -c ../WifiDppConfig.json -tc WifiDppTest
Test: Connect to Passpoint, Open, WPA2, WPA3 networks and run traffic
Test: Regression test passed (Bug: 156969218)

dd2daf084 HE: Process HE 6 GHz band capab from associating HE STA
db603634a HE: Add 6 GHz Band Capabilities element in Beacon and response frames
88911a0aa HE: Add HE 6 GHz Band Capabilities into ieee802_11_parse_elems()
b2c0b83c6 HE: Remove VHT Operation Information from HE Operation element
e297a5bfd HE: Define 6 GHz band capability elements
39f29f250 defconfig: Enable TDLS
025ab330b ACS: Channel selection based freqlist
4ae3f3972 Add a helper function for recognizing BIP enum wpa_alg values
d3cab56c0 Rename WPA_ALG_IGTK to use the correct cipher name for BIP
bd1aebbd0 hostapd: Extend RESET_PN for BIGTK
df49c53f4 Fix a typo in a comment
4294d221d D-Bus: Increase introspection buffer size
79488da57 wolfssl: Do not hardcode include directory in wpa_supplicant build
eb595b3e3 wolfssl: Fix crypto_bignum_rand() implementation
6a28c4dbc wolfssl: Fix compiler warnings on size_t printf format use
f2dbaa8ac SAE: Fix a typo in a comment
038899290 wpa_gui: Fix build with Inkscape 1.0
3e1a13010 nl80211: Change AKM suite limit from warning to debug print
5a04a76aa Ignore Management frames while AP interface is not fully enabled
c82535edd Move deauthentication at AP start to be after beacon configuration
094c8a621 Remove unnecessary key clearing at AP start with nl80211
04030e8c0 nl80211: Remove AP mode interface from bridge for STA-mode-scan
7adea21d2 dpp-nfc: Enable hostapd beaconing for listen state
134ad50b0 dpp-nfc: Clean up debug prints when handover select is received
5d49c1bf7 dpp-nfc: Do not indicate a single channel 1 by default
d0e2d8091 dpp-nfc: Make handover request collision detection more robust
8791e7461 dpp-nfc: Write debug info to summary log
1e0bc897a dpp-nfc: Collision detection for handover request
9ad3fe934 dpp-nfc: Start handover server regardless of init-on-touch setting
24efcdf74 dpp-nfc: Own MAC address fetching from hostapd
8f96f2c3b dpp-nfc: Be more graceful when wpa_supplicant is not available
0b04d3c57 dpp-nfc: Allow wpa_supplicant control interface directory to be set
69dfbe6a9 dpp-nfc: Use Configurator/Enrollee parameters with tag reading
f85fb349f dpp-nfc: More robust determination of the script directory
98e4b3840 DPP2: Chirping in hostapd Enrollee
95471fc3e Handle hostapd_for_each_interface() at the process termination
99809c7a4 nl80211: Disable offchannel-ok in AP mode only if beaconing
0f58c88fc DPP2: Fix CONFIG_DPP2=y build with OpenSSL 1.0.2
44f786678 Clean up GET_CAPABILITY handling of 'strict' argument
3790f3a6e Use per-interface type driver key_mgmt capabilities when possible
8d7502809 Allow per interface type AKM capabilities to be fetched
b67bedf2e nl80211: Fetch information on supported AKMs from the driver
6fffb320f nl80211: Remove QCA vendor specific AKM capability handling
db59827a3 DPP2: Extend TCP encapsulation case to support Configurator as Initiator
0086c1452 DPP: Extend NFC bootstrapping script for more control by caller
890ae336c DPP2: Clean up CONFIG_DPP2 use with configurator connectivity IE
670e15337 DPP2: Fix DPP_CHIRP listen parameter value validation
7f20a3ebd DPP2: Reconfiguration support in Controller
6dcb8aaf1 DPP2: Reconfig Announcement relaying from AP to Controller
3b4f7dfaa DPP2: Fix Presence Announcement processing in Controller
5e2d877cc DPP: Mark internal-to-file functions static
3aaf269f6 DPP: Move TCP encapsulation into a separate source code file
21c612017 DPP: Move configurator backup into a separate source code file
fdbbb7406 DPP: Move authentication functionality into a separate source code file
182f6ae90 DPP2: Remove reconfigured network
3e48c5d4b DPP2: Reconfig Authentication Confirm processing
24b01c706 DPP2: Reconfig Authentication Response processing and Confirm generation
65e94351d DPP2: Reconfig Authentication Request processing and Response generation
3774b6bd0 DPP2: Reconfig Authentication Request generation and transmission
66ac616cd DPP2: Process received Reconfig Announcement frame
0c043d9de DPP2: Reconfig Announcement transmission
92492dd3a DPP2: Extend connector matching for reconfiguration
961435097 DPP2: Move connStatus object building into a helper function
94f73f90e DPP: Move signed connector checking into a helper function
94a28a494 DPP: Move parsing of own connector into a helper function
d4ae12355 DPP: Move PKEX functionality into a separate source code file
87b657261 DPP: Move crypto routines into a separate source code file
16626dff9 DPP2: Derive bk ("base key")
76029c6e1 DPP: Use EVP_PKEY_get0_EC_KEY() when a const reference is sufficient
0a488ef35 DPP: Track ending time for remain-on-channel operations
481fdfc46 DPP2: Fix URI version parser
7dd768c3c DPP2: Version information in bootstrapping info URI
cbafc8ef4 Fix truncated control interface command detection
5a0718a19 DPP2: Report MUD URL and bandSupport in control interface events
769139c49 DPP2: Do not include Protocol Version in Auth Req when testing v1
fad64b416 DPP: Move dppCon signing to a set of helper functions
12c8eacf7 DPP: Allow version number to be overridden for testing purposes
c3c38bc8b DPP2: Detect PFS downgrade attack while processing EAPOL-Key msg 3/4
9561925b4 DPP2: Detect PFS downgrade attack while processing EAPOL-Key msg 2/4
68422fedb DPP2: Parse DPP KDE in EAPOL-Key Key Data field
143e3d8bc DPP2: Add DPP KDE into EAPOL-Key msg 2/4 when using DPP AKM
b11a12401 DPP2: Add DPP KDE into EAPOL-Key msg 3/4 when using DPP AKM
85d545699 DPP2: Indicate if PFS was used in control interface STATUS
1f5f00008 DPP2: Try to negotiate PFS only if AP supports version 2 or newer
f6c22dcde Use a local pointer to simply current_ssid accesses in sme_associate()
42acf1292 DPP2: Add Protocol Version attribute to network introduction messages
96b6dd21a Increase wpa_supplicant control interface buffer size
a7d6098fb Add PRINTF_FORMAT for printf wrapper functions

Change-Id: I26a15ac578ce81d1e35f426085661e5c7b43b6ef
diff --git a/wpa_supplicant/examples/dpp-nfc.py b/wpa_supplicant/examples/dpp-nfc.py
index f49da34..1883545 100755
--- a/wpa_supplicant/examples/dpp-nfc.py
+++ b/wpa_supplicant/examples/dpp-nfc.py
@@ -8,6 +8,7 @@
 # See README for more details.
 
 import os
+import struct
 import sys
 import time
 import threading
@@ -18,7 +19,7 @@
 
 import logging
 
-scriptsdir = os.path.dirname(os.path.realpath("dpp-nfc.py"))
+scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
 sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy'))
 import wpaspy
 
@@ -33,12 +34,18 @@
 terminate_now = False
 summary_file = None
 success_file = None
+my_crn_ready = False
+my_crn = None
+peer_crn = None
+hs_sent = False
+mutex = threading.Lock()
 
 def summary(txt):
-    print(txt)
-    if summary_file:
-        with open(summary_file, 'a') as f:
-            f.write(txt + "\n")
+    with mutex:
+        print(txt)
+        if summary_file:
+            with open(summary_file, 'a') as f:
+                f.write(txt + "\n")
 
 def success_report(txt):
     summary(txt)
@@ -52,11 +59,11 @@
         try:
             ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
         except OSError as error:
-            print("Could not find wpa_supplicant: ", error)
+            summary("Could not find wpa_supplicant: %s", str(error))
             return None
 
     if len(ifaces) < 1:
-        print("No wpa_supplicant control interface found")
+        summary("No wpa_supplicant control interface found")
         return None
 
     for ctrl in ifaces:
@@ -64,7 +71,7 @@
             if ifname not in ctrl:
                 continue
         try:
-            print("Trying to use control interface " + ctrl)
+            summary("Trying to use control interface " + ctrl)
             wpas = wpaspy.Ctrl(ctrl)
             return wpas
         except Exception as e:
@@ -77,38 +84,46 @@
         return False
     peer_id = wpas.request("DPP_NFC_URI " + uri)
     if "FAIL" in peer_id:
-        print("Could not parse DPP URI from NFC URI record")
+        summary("Could not parse DPP URI from NFC URI record")
         return False
     peer_id = int(peer_id)
-    print("peer_id=%d" % peer_id)
+    summary("peer_id=%d for URI from NFC Tag: %s" % (peer_id, uri))
     cmd = "DPP_AUTH_INIT peer=%d" % peer_id
+    global enrollee_only, configurator_only, config_params
+    if enrollee_only:
+        cmd += " role=enrollee"
+    elif configurator_only:
+        cmd += " role=configurator"
+    if config_params:
+        cmd += " " + config_params
+    summary("Initiate DPP authentication: " + cmd)
     res = wpas.request(cmd)
     if "OK" not in res:
-        print("Failed to initiate DPP Authentication")
+        summary("Failed to initiate DPP Authentication")
         return False
-    print("DPP Authentication initiated")
+    summary("DPP Authentication initiated")
     return True
 
 def dpp_hs_tag_read(record):
     wpas = wpas_connect()
     if wpas is None:
         return False
-    print(record)
+    summary(record)
     if len(record.data) < 5:
-        print("Too short DPP HS")
+        summary("Too short DPP HS")
         return False
     if record.data[0] != 0:
-        print("Unexpected URI Identifier Code")
+        summary("Unexpected URI Identifier Code")
         return False
     uribuf = record.data[1:]
     try:
         uri = uribuf.decode()
     except:
-        print("Invalid URI payload")
+        summary("Invalid URI payload")
         return False
-    print("URI: " + uri)
+    summary("URI: " + uri)
     if not uri.startswith("DPP:"):
-        print("Not a DPP URI")
+        summary("Not a DPP URI")
         return False
     return dpp_nfc_uri_process(uri)
 
@@ -124,7 +139,7 @@
         try:
             [name, value] = l.split('=', 1)
         except ValueError:
-            logger.info("Ignore unexpected status line: " + l)
+            summary("Ignore unexpected status line: %s" % l)
             continue
         vals[name] = value
     return vals
@@ -136,7 +151,10 @@
     return None
 
 def own_addr(wpas):
-    return get_status_field(wpas, "address")
+    addr = get_status_field(wpas, "address")
+    if addr is None:
+        addr = get_status_field(wpas, "bssid[0]")
+    return addr
 
 def dpp_bootstrap_gen(wpas, type="qrcode", chan=None, mac=None, info=None,
                       curve=None, key=None):
@@ -146,7 +164,10 @@
     if mac:
         if mac is True:
             mac = own_addr(wpas)
-        cmd += " mac=" + mac.replace(':', '')
+        if mac is None:
+            summary("Could not determine local MAC address for bootstrap info")
+        else:
+            cmd += " mac=" + mac.replace(':', '')
     if info:
         cmd += " info=" + info
     if curve:
@@ -158,12 +179,23 @@
         raise Exception("Failed to generate bootstrapping info")
     return int(res)
 
-def wpas_get_nfc_uri(start_listen=True):
+def wpas_get_nfc_uri(start_listen=True, pick_channel=False):
     wpas = wpas_connect()
     if wpas is None:
         return None
     global own_id, chanlist
-    own_id = dpp_bootstrap_gen(wpas, type="nfc-uri", chan=chanlist, mac=True)
+    chan = chanlist
+    if chan is None and get_status_field(wpas, "bssid[0]"):
+        freq = get_status_field(wpas, "freq")
+        if freq:
+            freq = int(freq)
+            if freq >= 2412 and freq <= 2462:
+                chan = "81/%d" % ((freq - 2407) / 5)
+                summary("Use current AP operating channel (%d MHz) as the URI channel list (%s)" % (freq, chan))
+    if chan is None and pick_channel:
+        chan = "81/6"
+        summary("Use channel 2437 MHz since no other preference provided")
+    own_id = dpp_bootstrap_gen(wpas, type="nfc-uri", chan=chan, mac=True)
     res = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
     if "FAIL" in res:
         return None
@@ -189,14 +221,22 @@
 
 def dpp_handover_client(llc):
     uri = wpas_get_nfc_uri(start_listen=False)
+    if uri is None:
+        summary("Cannot start handover client - no bootstrap URI available")
+        return
     uri = ndef.UriRecord(uri)
-    print("NFC URI record for DPP: " + str(uri))
+    summary("NFC URI record for DPP: " + str(uri))
     carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
-    hr = ndef.HandoverRequestRecord(version="1.4", crn=os.urandom(2))
+    crn = os.urandom(2)
+    hr = ndef.HandoverRequestRecord(version="1.4", crn=crn)
     hr.add_alternative_carrier('active', carrier.name)
     message = [hr, carrier]
-    print("NFC Handover Request message for DPP: " + str(message))
+    summary("NFC Handover Request message for DPP: " + str(message))
 
+    global peer_crn
+    if peer_crn is not None:
+        summary("NFC handover request from peer was already received - do not send own")
+        return
     client = nfc.handover.HandoverClient(llc)
     try:
         summary("Trying to initiate NFC connection handover")
@@ -211,76 +251,108 @@
         client.close()
         return
 
+    if peer_crn is not None:
+        summary("NFC handover request from peer was already received - do not send own")
+        client.close()
+        return
+
     summary("Sending handover request")
 
+    global my_crn, my_crn_ready, hs_sent
+    my_crn_ready = True
+
     if not client.send_records(message):
+        my_crn_ready = False
         summary("Failed to send handover request")
         client.close()
         return
 
+    my_crn, = struct.unpack('>H', crn)
+
     summary("Receiving handover response")
-    message = client.recv_records(timeout=3.0)
+    try:
+        message = client.recv_records(timeout=3.0)
+    except Exception as e:
+        # This is fine if we are the handover selector
+        if hs_sent:
+            summary("Client receive failed as expected since I'm the handover server: %s" % str(e))
+        else:
+            summary("Client receive failed: %s" % str(e))
+        message = None
     if message is None:
-        summary("No response received")
+        if hs_sent:
+            summary("No response received as expected since I'm the handover server")
+        else:
+            summary("No response received")
         client.close()
         return
-    print("Received message: " + str(message))
+    summary("Received message: " + str(message))
     if len(message) < 1 or \
        not isinstance(message[0], ndef.HandoverSelectRecord):
         summary("Response was not Hs - received: " + message.type)
         client.close()
         return
 
-    print("Received message")
-    print("alternative carriers: " + str(message[0].alternative_carriers))
+    summary("Received handover select message")
+    summary("alternative carriers: " + str(message[0].alternative_carriers))
 
     dpp_found = False
     for carrier in message:
         if isinstance(carrier, ndef.HandoverSelectRecord):
             continue
-        print("Remote carrier type: " + carrier.type)
+        summary("Remote carrier type: " + carrier.type)
         if carrier.type == "application/vnd.wfa.dpp":
             if len(carrier.data) == 0 or carrier.data[0] != 0:
-                print("URI Identifier Code 'None' not seen")
+                summary("URI Identifier Code 'None' not seen")
                 continue
-            print("DPP carrier type match - send to wpa_supplicant")
+            summary("DPP carrier type match - send to wpa_supplicant")
             dpp_found = True
             uri = carrier.data[1:].decode("utf-8")
-            print("DPP URI: " + uri)
+            summary("DPP URI: " + uri)
             res = wpas_report_handover_sel(uri)
             if res is None or "FAIL" in res:
                 summary("DPP handover report rejected")
                 break
 
             success_report("DPP handover reported successfully (initiator)")
-            print("peer_id=" + res)
+            summary("peer_id=" + res)
             peer_id = int(res)
-            # TODO: Single Configurator instance
             wpas = wpas_connect()
             if wpas is None:
                 break
-            res = wpas.request("DPP_CONFIGURATOR_ADD")
-            if "FAIL" in res:
-                print("Failed to initiate Configurator")
-                break
-            conf_id = int(res)
+
+            global enrollee_only
+            global config_params
+            if enrollee_only:
+                extra = " role=enrollee"
+            elif config_params:
+                extra = " role=configurator " + config_params
+            else:
+                # TODO: Single Configurator instance
+                res = wpas.request("DPP_CONFIGURATOR_ADD")
+                if "FAIL" in res:
+                    summary("Failed to initiate Configurator")
+                    break
+                conf_id = int(res)
+                extra = " conf=sta-dpp configurator=%d" % conf_id
             global own_id
-            print("Initiate DPP authentication")
-            cmd = "DPP_AUTH_INIT peer=%d own=%d conf=sta-dpp configurator=%d" % (peer_id, own_id, conf_id)
+            summary("Initiate DPP authentication")
+            cmd = "DPP_AUTH_INIT peer=%d own=%d" % (peer_id, own_id)
+            cmd += extra
             res = wpas.request(cmd)
             if "FAIL" in res:
-                print("Failed to initiate DPP authentication")
+                summary("Failed to initiate DPP authentication")
             break
 
     if not dpp_found:
-        print("DPP carrier not seen in response - allow peer to initiate a new handover with different parameters")
+        summary("DPP carrier not seen in response - allow peer to initiate a new handover with different parameters")
         client.close()
-        print("Returning from dpp_handover_client")
+        summary("Returning from dpp_handover_client")
         return
 
-    print("Remove peer")
+    summary("Remove peer")
     client.close()
-    print("Done with handover")
+    summary("Done with handover")
     global only_one
     if only_one:
         print("only_one -> stop loop")
@@ -293,7 +365,7 @@
         global terminate_now
         terminate_now = True
 
-    print("Returning from dpp_handover_client")
+    summary("Returning from dpp_handover_client")
 
 class HandoverServer(nfc.handover.HandoverServer):
     def __init__(self, llc):
@@ -305,10 +377,45 @@
 
     def process_handover_request_message(self, records):
         self.ho_server_processing = True
+        global in_raw_mode
+        was_in_raw_mode = in_raw_mode
         clear_raw_mode()
-        print("\nHandoverServer - request received: " + str(records))
+        if was_in_raw_mode:
+            print("\n")
+        summary("HandoverServer - request received: " + str(records))
 
-        carrier = None
+        global my_crn, peer_crn, my_crn_ready
+
+        for carrier in records:
+            if not isinstance(carrier, ndef.HandoverRequestRecord):
+                continue
+            if carrier.collision_resolution_number:
+                peer_crn = carrier.collision_resolution_number
+                summary("peer_crn: %d" % peer_crn)
+
+        if my_crn is None and my_crn_ready:
+            summary("Still trying to send own handover request - wait a moment to see if that succeeds before checking crn values")
+            for i in range(10):
+                if my_crn is not None:
+                    break
+                time.sleep(0.01)
+        if my_crn is not None:
+            summary("my_crn: %d" % my_crn)
+
+        if my_crn is not None and peer_crn is not None:
+            if my_crn == peer_crn:
+                summary("Same crn used - automatic collision resolution failed")
+                # TODO: Should generate a new Handover Request message
+                return ''
+            if ((my_crn & 1) == (peer_crn & 1) and my_crn > peer_crn) or \
+               ((my_crn & 1) != (peer_crn & 1) and my_crn < peer_crn):
+                summary("I'm the Handover Selector Device")
+                pass
+            else:
+                summary("Peer is the Handover Selector device")
+                summary("Ignore the received request.")
+                return ''
+
         hs = ndef.HandoverSelectRecord('1.4')
         sel = [hs]
 
@@ -317,25 +424,24 @@
         for carrier in records:
             if isinstance(carrier, ndef.HandoverRequestRecord):
                 continue
-            print("Remote carrier type: " + carrier.type)
+            summary("Remote carrier type: " + carrier.type)
             if carrier.type == "application/vnd.wfa.dpp":
-                print("DPP carrier type match - add DPP carrier record")
+                summary("DPP carrier type match - add DPP carrier record")
                 if len(carrier.data) == 0 or carrier.data[0] != 0:
-                    print("URI Identifier Code 'None' not seen")
+                    summary("URI Identifier Code 'None' not seen")
                     continue
                 uri = carrier.data[1:].decode("utf-8")
-                print("Received DPP URI: " + uri)
+                summary("Received DPP URI: " + uri)
 
-                data = wpas_get_nfc_uri(start_listen=False)
-                print("Own URI (pre-processing): %s" % data)
+                data = wpas_get_nfc_uri(start_listen=False, pick_channel=True)
+                summary("Own URI (pre-processing): %s" % data)
 
                 res = wpas_report_handover_req(uri)
                 if res is None or "FAIL" in res:
-                    print("DPP handover request processing failed")
+                    summary("DPP handover request processing failed")
                     continue
 
                 found = True
-                self.received_carrier = carrier
 
                 wpas = wpas_connect()
                 if wpas is None:
@@ -344,25 +450,42 @@
                 data = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
                 if "FAIL" in data:
                     continue
-                print("Own URI (post-processing): %s" % data)
+                summary("Own URI (post-processing): %s" % data)
                 uri = ndef.UriRecord(data)
-                print("Own bootstrapping NFC URI record: " + str(uri))
+                summary("Own bootstrapping NFC URI record: " + str(uri))
 
                 info = wpas.request("DPP_BOOTSTRAP_INFO %d" % own_id)
                 freq = None
                 for line in info.splitlines():
                     if line.startswith("use_freq="):
                         freq = int(line.split('=')[1])
-                if freq is None:
-                    print("No channel negotiated over NFC - use channel 1")
-                    freq = 2412
-                res = wpas.request("DPP_LISTEN %d" % freq)
+                if freq is None or freq == 0:
+                    summary("No channel negotiated over NFC - use channel 6")
+                    freq = 2437
+                else:
+                    summary("Negotiated channel: %d MHz" % freq)
+                if get_status_field(wpas, "bssid[0]"):
+                    summary("Own AP freq: %s MHz" % str(get_status_field(wpas, "freq")))
+                    if get_status_field(wpas, "beacon_set", extra="DRIVER") is None:
+                        summary("Enable beaconing to have radio ready for RX")
+                        wpas.request("DISABLE")
+                        wpas.request("SET start_disabled 0")
+                        wpas.request("ENABLE")
+                cmd = "DPP_LISTEN %d" % freq
+                global enrollee_only
+                global configurator_only
+                if enrollee_only:
+                    cmd += " role=enrollee"
+                elif configurator_only:
+                    cmd += " role=configurator"
+                summary(cmd)
+                res = wpas.request(cmd)
                 if "OK" not in res:
-                    print("Failed to start DPP listen")
+                    summary("Failed to start DPP listen")
                     break
 
                 carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
-                print("Own DPP carrier record: " + str(carrier))
+                summary("Own DPP carrier record: " + str(carrier))
                 hs.add_alternative_carrier('active', carrier.name)
                 sel = [hs, carrier]
                 break
@@ -372,6 +495,8 @@
             self.success = True
         else:
             self.try_own = True
+        global hs_sent
+        hs_sent = True
         return sel
 
 def clear_raw_mode():
@@ -403,22 +528,22 @@
 def dpp_tag_read(tag):
     success = False
     for record in tag.ndef.records:
-        print(record)
-        print("record type " + record.type)
+        summary(record)
+        summary("record type " + record.type)
         if record.type == "application/vnd.wfa.dpp":
             summary("DPP HS tag - send to wpa_supplicant")
             success = dpp_hs_tag_read(record)
             break
         if isinstance(record, ndef.UriRecord):
-            print("URI record: uri=" + record.uri)
-            print("URI record: iri=" + record.iri)
+            summary("URI record: uri=" + record.uri)
+            summary("URI record: iri=" + record.iri)
             if record.iri.startswith("DPP:"):
-                print("DPP URI")
+                summary("DPP URI")
                 if not dpp_nfc_uri_process(record.iri):
                     break
                 success = True
             else:
-                print("Ignore unknown URI")
+                summary("Ignore unknown URI")
             break
 
     if success:
@@ -429,15 +554,15 @@
 def rdwr_connected_write_tag(tag):
     summary("Tag found - writing - " + str(tag))
     if not tag.ndef.is_writeable:
-        print("Not a writable tag")
+        summary("Not a writable tag")
         return
     global dpp_tag_data
     if tag.ndef.capacity < len(dpp_tag_data):
-        print("Not enough room for the message")
+        summary("Not enough room for the message")
         return
     tag.ndef.records = dpp_tag_data
     success_report("Tag write succeeded")
-    print("Done - remove tag")
+    summary("Done - remove tag")
     global only_one
     if only_one:
         global continue_loop
@@ -446,7 +571,7 @@
     return dpp_sel_wait_remove
 
 def write_nfc_uri(clf, wait_remove=True):
-    print("Write NFC URI record")
+    summary("Write NFC URI record")
     data = wpas_get_nfc_uri()
     if data is None:
         summary("Could not get NFC URI from wpa_supplicant")
@@ -454,17 +579,17 @@
 
     global dpp_sel_wait_remove
     dpp_sel_wait_remove = wait_remove
-    print("URI: %s" % data)
+    summary("URI: %s" % data)
     uri = ndef.UriRecord(data)
-    print(uri)
+    summary(uri)
 
-    print("Touch an NFC tag")
+    summary("Touch an NFC tag")
     global dpp_tag_data
     dpp_tag_data = [uri]
     clf.connect(rdwr={'on-connect': rdwr_connected_write_tag})
 
 def write_nfc_hs(clf, wait_remove=True):
-    print("Write NFC Handover Select record on a tag")
+    summary("Write NFC Handover Select record on a tag")
     data = wpas_get_nfc_uri()
     if data is None:
         summary("Could not get NFC URI from wpa_supplicant")
@@ -472,19 +597,19 @@
 
     global dpp_sel_wait_remove
     dpp_sel_wait_remove = wait_remove
-    print("URI: %s" % data)
+    summary("URI: %s" % data)
     uri = ndef.UriRecord(data)
-    print(uri)
+    summary(uri)
     carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
     hs = ndef.HandoverSelectRecord('1.4')
     hs.add_alternative_carrier('active', carrier.name)
-    print(hs)
-    print(carrier)
+    summary(hs)
+    summary(carrier)
 
-    print("Touch an NFC tag")
+    summary("Touch an NFC tag")
     global dpp_tag_data
     dpp_tag_data = [hs, carrier]
-    print(dpp_tag_data)
+    summary(dpp_tag_data)
     clf.connect(rdwr={'on-connect': rdwr_connected_write_tag})
 
 def rdwr_connected(tag):
@@ -492,8 +617,8 @@
     summary("Tag connected: " + str(tag))
 
     if tag.ndef:
-        print("NDEF tag: " + tag.type)
-        print(tag.ndef.records)
+        summary("NDEF tag: " + tag.type)
+        summary(tag.ndef.records)
         success = dpp_tag_read(tag)
         if only_one and success:
             global continue_loop
@@ -507,14 +632,14 @@
 def llcp_worker(llc):
     global init_on_touch
     if init_on_touch:
-        print("Starting handover client")
+        summary("Starting handover client")
         dpp_handover_client(llc)
-        print("Exiting llcp_worker thread (init_in_touch)")
+        summary("Exiting llcp_worker thread (init_in_touch)")
         return
 
     global no_input
     if no_input:
-        print("Wait for handover to complete")
+        summary("Wait for handover to complete")
     else:
         print("Wait for handover to complete - press 'i' to initiate")
     global srv
@@ -522,9 +647,9 @@
     while not wait_connection and srv.sent_carrier is None:
         if srv.try_own:
             srv.try_own = False
-            print("Try to initiate another handover with own parameters")
+            summary("Try to initiate another handover with own parameters")
             dpp_handover_client(llc)
-            print("Exiting llcp_worker thread (retry with own parameters)")
+            summary("Exiting llcp_worker thread (retry with own parameters)")
             return
         if srv.ho_server_processing:
             time.sleep(0.025)
@@ -535,34 +660,40 @@
             if res != 'i':
                 continue
             clear_raw_mode()
-            print("Starting handover client")
+            summary("Starting handover client")
             dpp_handover_client(llc)
-            print("Exiting llcp_worker thread (manual init)")
+            summary("Exiting llcp_worker thread (manual init)")
             return
 
+    global in_raw_mode
+    was_in_raw_mode = in_raw_mode
     clear_raw_mode()
-    print("\rExiting llcp_worker thread")
+    if was_in_raw_mode:
+        print("\r")
+    summary("Exiting llcp_worker thread")
 
 def llcp_startup(llc):
-    print("Start LLCP server")
+    summary("Start LLCP server")
     global srv
     srv = HandoverServer(llc)
     return llc
 
 def llcp_connected(llc):
-    print("P2P LLCP connected")
-    global wait_connection
+    summary("P2P LLCP connected")
+    global wait_connection, my_crn, peer_crn, my_crn_ready, hs_sent
     wait_connection = False
-    global init_on_touch
-    if not init_on_touch:
-        global srv
-        srv.start()
+    my_crn_ready = False
+    my_crn = None
+    peer_crn = None
+    hs_sent = False
+    global srv
+    srv.start()
     if init_on_touch or not no_input:
         threading.Thread(target=llcp_worker, args=(llc,)).start()
     return True
 
 def llcp_release(llc):
-    print("LLCP release")
+    summary("LLCP release")
     return True
 
 def terminate_loop():
@@ -592,17 +723,25 @@
                         help='tag read only (do not allow connection handover)')
     parser.add_argument('--handover-only', action='store_true',
                         help='connection handover only (do not allow tag read)')
+    parser.add_argument('--enrollee', action='store_true',
+                        help='run as Enrollee-only')
+    parser.add_argument('--configurator', action='store_true',
+                        help='run as Configurator-only')
+    parser.add_argument('--config-params', default='',
+                        help='configurator parameters')
+    parser.add_argument('--ctrl', default='/var/run/wpa_supplicant',
+                        help='wpa_supplicant/hostapd control interface')
     parser.add_argument('--summary',
                         help='summary file for writing status updates')
     parser.add_argument('--success',
                         help='success file for writing success update')
     parser.add_argument('--device', default='usb', help='NFC device to open')
-    parser.add_argument('--chan', default='81/1', help='channel list')
+    parser.add_argument('--chan', default=None, help='channel list')
     parser.add_argument('command', choices=['write-nfc-uri',
                                             'write-nfc-hs'],
                         nargs='?')
     args = parser.parse_args()
-    print(args)
+    summary(args)
 
     global only_one
     only_one = args.only_one
@@ -618,10 +757,23 @@
     global init_on_touch
     init_on_touch = args.init_on_touch
 
+    global enrollee_only
+    enrollee_only = args.enrollee
+
+    global configurator_only
+    configurator_only = args.configurator
+
+    global config_params
+    config_params = args.config_params
+
     if args.ifname:
         global ifname
         ifname = args.ifname
-        print("Selected ifname " + ifname)
+        summary("Selected ifname " + ifname)
+
+    if args.ctrl:
+        global wpas_ctrl
+        wpas_ctrl = args.ctrl
 
     if args.summary:
         global summary_file
@@ -640,7 +792,7 @@
 
     try:
         if not clf.open(args.device):
-            print("Could not open connection with an NFC device")
+            summary("Could not open connection with an NFC device")
             raise SystemExit
 
         if args.command == "write-nfc-uri":
@@ -653,8 +805,12 @@
 
         global continue_loop
         while continue_loop:
+            global in_raw_mode
+            was_in_raw_mode = in_raw_mode
             clear_raw_mode()
-            print("\rWaiting for a tag or peer to be touched")
+            if was_in_raw_mode:
+                print("\r")
+            summary("Waiting for a tag or peer to be touched")
             wait_connection = True
             try:
                 if args.tag_read_only:
@@ -674,7 +830,7 @@
                                        terminate=terminate_loop):
                         break
             except Exception as e:
-                print("clf.connect failed: " + str(e))
+                summary("clf.connect failed: " + str(e))
                 break
 
             global srv