Imported Upstream version 1.24
[platform/upstream/connman.git] / plugins / wifi.c
index fb70b57..ef4dd95 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2014  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
 #include <connman/device.h>
 #include <connman/rtnl.h>
 #include <connman/technology.h>
+#include <connman/service.h>
+#include <connman/peer.h>
 #include <connman/log.h>
 #include <connman/option.h>
 #include <connman/storage.h>
 #include <include/setting.h>
+#include <connman/provision.h>
 
 #include <gsupplicant/gsupplicant.h>
 
 #define CLEANUP_TIMEOUT   8    /* in seconds */
 #define INACTIVE_TIMEOUT  12   /* in seconds */
-#define MAXIMUM_RETRIES   4
+#define FAVORITE_MAXIMUM_RETRIES 2
 
 #define BGSCAN_DEFAULT "simple:30:-45:300"
 #define AUTOSCAN_DEFAULT "exponential:3:300"
 
-struct connman_technology *wifi_technology = NULL;
+#define P2P_FIND_TIMEOUT 30
+
+static struct connman_technology *wifi_technology = NULL;
+static struct connman_technology *p2p_technology = NULL;
 
 struct hidden_params {
        char ssid[32];
        unsigned int ssid_len;
        char *identity;
        char *passphrase;
+       char *security;
+       GSupplicantScanParams *scan_params;
+       gpointer user_data;
 };
 
 /**
@@ -89,33 +98,59 @@ struct wifi_data {
        GSList *networks;
        GSupplicantInterface *interface;
        GSupplicantState state;
-       connman_bool_t connected;
-       connman_bool_t disconnecting;
-       connman_bool_t tethering;
-       connman_bool_t bridged;
+       bool connected;
+       bool disconnecting;
+       bool tethering;
+       bool bridged;
+       bool interface_ready;
        const char *bridge;
        int index;
        unsigned flags;
        unsigned int watch;
        int retries;
        struct hidden_params *hidden;
+       bool postpone_hidden;
        /**
         * autoscan "emulation".
         */
        struct autoscan_params *autoscan;
+
+       GSupplicantScanParams *scan_params;
+       unsigned int p2p_find_timeout;
 };
 
 static GList *iface_list = NULL;
 
+static void start_autoscan(struct connman_device *device);
+
+static int p2p_tech_probe(struct connman_technology *technology)
+{
+       p2p_technology = technology;
+
+       return 0;
+}
+
+static void p2p_tech_remove(struct connman_technology *technology)
+{
+       p2p_technology = NULL;
+}
+
+static struct connman_technology_driver p2p_tech_driver = {
+       .name           = "p2p",
+       .type           = CONNMAN_SERVICE_TYPE_P2P,
+       .probe          = p2p_tech_probe,
+       .remove         = p2p_tech_remove,
+};
+
 static void handle_tethering(struct wifi_data *wifi)
 {
-       if (wifi->tethering == FALSE)
+       if (!wifi->tethering)
                return;
 
-       if (wifi->bridge == NULL)
+       if (!wifi->bridge)
                return;
 
-       if (wifi->bridged == TRUE)
+       if (wifi->bridged)
                return;
 
        DBG("index %d bridge %s", wifi->index, wifi->bridge);
@@ -123,7 +158,7 @@ static void handle_tethering(struct wifi_data *wifi)
        if (connman_inet_add_to_bridge(wifi->index, wifi->bridge) < 0)
                return;
 
-       wifi->bridged = TRUE;
+       wifi->bridged = true;
 }
 
 static void wifi_newlink(unsigned flags, unsigned change, void *user_data)
@@ -131,11 +166,11 @@ static void wifi_newlink(unsigned flags, unsigned change, void *user_data)
        struct connman_device *device = user_data;
        struct wifi_data *wifi = connman_device_get_data(device);
 
-       DBG("index %d flags %d change %d", wifi->index, flags, change);
-
-       if (!change)
+       if (!wifi)
                return;
 
+       DBG("index %d flags %d change %d", wifi->index, flags, change);
+
        if ((wifi->flags & IFF_UP) != (flags & IFF_UP)) {
                if (flags & IFF_UP)
                        DBG("interface up");
@@ -162,14 +197,9 @@ static int wifi_probe(struct connman_device *device)
        DBG("device %p", device);
 
        wifi = g_try_new0(struct wifi_data, 1);
-       if (wifi == NULL)
+       if (!wifi)
                return -ENOMEM;
 
-       wifi->connected = FALSE;
-       wifi->disconnecting = FALSE;
-       wifi->tethering = FALSE;
-       wifi->bridged = FALSE;
-       wifi->bridge = NULL;
        wifi->state = G_SUPPLICANT_STATE_INACTIVE;
 
        connman_device_set_data(device, wifi);
@@ -191,7 +221,7 @@ static void remove_networks(struct connman_device *device,
 {
        GSList *list;
 
-       for (list = wifi->networks; list != NULL; list = list->next) {
+       for (list = wifi->networks; list; list = list->next) {
                struct connman_network *network = list->data;
 
                connman_device_remove_network(device, network);
@@ -202,14 +232,14 @@ static void remove_networks(struct connman_device *device,
        wifi->networks = NULL;
 }
 
-static void stop_autoscan(struct connman_device *device)
+static void reset_autoscan(struct connman_device *device)
 {
        struct wifi_data *wifi = connman_device_get_data(device);
        struct autoscan_params *autoscan;
 
        DBG("");
 
-       if (wifi == NULL || wifi->autoscan == NULL)
+       if (!wifi || !wifi->autoscan)
                return;
 
        autoscan = wifi->autoscan;
@@ -225,65 +255,492 @@ static void stop_autoscan(struct connman_device *device)
        connman_device_unref(device);
 }
 
+static void stop_autoscan(struct connman_device *device)
+{
+       const struct wifi_data *wifi = connman_device_get_data(device);
+
+       if (!wifi || !wifi->autoscan)
+               return;
+
+       reset_autoscan(device);
+
+       connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_WIFI, false);
+}
+
+static void check_p2p_technology(void)
+{
+       bool p2p_exists = false;
+       GList *list;
+
+       for (list = iface_list; list; list = list->next) {
+               struct wifi_data *w = list->data;
+
+               if (w->interface &&
+                               g_supplicant_interface_has_p2p(w->interface))
+                       p2p_exists = true;
+       }
+
+       if (!p2p_exists)
+               connman_technology_driver_unregister(&p2p_tech_driver);
+}
+
 static void wifi_remove(struct connman_device *device)
 {
        struct wifi_data *wifi = connman_device_get_data(device);
 
        DBG("device %p wifi %p", device, wifi);
 
-       if (wifi == NULL)
+       if (!wifi)
                return;
 
        stop_autoscan(device);
 
        iface_list = g_list_remove(iface_list, wifi);
 
+       check_p2p_technology();
+
+       if (wifi->p2p_find_timeout) {
+               g_source_remove(wifi->p2p_find_timeout);
+               connman_device_unref(wifi->device);
+       }
+
        remove_networks(device, wifi);
 
-       connman_device_set_powered(device, FALSE);
+       connman_device_set_powered(device, false);
        connman_device_set_data(device, NULL);
        connman_device_unref(wifi->device);
        connman_rtnl_remove_watch(wifi->watch);
 
        g_supplicant_interface_set_data(wifi->interface, NULL);
 
+       if (wifi->scan_params)
+               g_supplicant_free_scan_params(wifi->scan_params);
+
        g_free(wifi->autoscan);
        g_free(wifi->identifier);
        g_free(wifi);
 }
 
+static bool is_duplicate(GSList *list, gchar *ssid, int ssid_len)
+{
+       GSList *iter;
+
+       for (iter = list; iter; iter = g_slist_next(iter)) {
+               struct scan_ssid *scan_ssid = iter->data;
+
+               if (ssid_len == scan_ssid->ssid_len &&
+                               memcmp(ssid, scan_ssid->ssid, ssid_len) == 0)
+                       return true;
+       }
+
+       return false;
+}
+
+static int add_scan_param(gchar *hex_ssid, char *raw_ssid, int ssid_len,
+                       int freq, GSupplicantScanParams *scan_data,
+                       int driver_max_scan_ssids, char *ssid_name)
+{
+       unsigned int i;
+       struct scan_ssid *scan_ssid;
+
+       if ((driver_max_scan_ssids == 0 ||
+                       driver_max_scan_ssids > scan_data->num_ssids) &&
+                       (hex_ssid || raw_ssid)) {
+               gchar *ssid;
+               unsigned int j = 0, hex;
+
+               if (hex_ssid) {
+                       size_t hex_ssid_len = strlen(hex_ssid);
+
+                       ssid = g_try_malloc0(hex_ssid_len / 2);
+                       if (!ssid)
+                               return -ENOMEM;
+
+                       for (i = 0; i < hex_ssid_len; i += 2) {
+                               sscanf(hex_ssid + i, "%02x", &hex);
+                               ssid[j++] = hex;
+                       }
+               } else {
+                       ssid = raw_ssid;
+                       j = ssid_len;
+               }
+
+               /*
+                * If we have already added hidden AP to the list,
+                * then do not do it again. This might happen if you have
+                * used or are using multiple wifi cards, so in that case
+                * you might have multiple service files for same AP.
+                */
+               if (is_duplicate(scan_data->ssids, ssid, j)) {
+                       if (hex_ssid)
+                               g_free(ssid);
+                       return 0;
+               }
+
+               scan_ssid = g_try_new(struct scan_ssid, 1);
+               if (!scan_ssid) {
+                       if (hex_ssid)
+                               g_free(ssid);
+                       return -ENOMEM;
+               }
+
+               memcpy(scan_ssid->ssid, ssid, j);
+               scan_ssid->ssid_len = j;
+               scan_data->ssids = g_slist_prepend(scan_data->ssids,
+                                                               scan_ssid);
+
+               scan_data->num_ssids++;
+
+               DBG("SSID %s added to scanned list of %d entries", ssid_name,
+                                                       scan_data->num_ssids);
+
+               if (hex_ssid)
+                       g_free(ssid);
+       } else
+               return -EINVAL;
+
+       scan_data->ssids = g_slist_reverse(scan_data->ssids);
+
+       if (!scan_data->freqs) {
+               scan_data->freqs = g_try_malloc0(sizeof(uint16_t));
+               if (!scan_data->freqs) {
+                       g_slist_free_full(scan_data->ssids, g_free);
+                       return -ENOMEM;
+               }
+
+               scan_data->num_freqs = 1;
+               scan_data->freqs[0] = freq;
+       } else {
+               bool duplicate = false;
+
+               /* Don't add duplicate entries */
+               for (i = 0; i < scan_data->num_freqs; i++) {
+                       if (scan_data->freqs[i] == freq) {
+                               duplicate = true;
+                               break;
+                       }
+               }
+
+               if (!duplicate) {
+                       scan_data->num_freqs++;
+                       scan_data->freqs = g_try_realloc(scan_data->freqs,
+                               sizeof(uint16_t) * scan_data->num_freqs);
+                       if (!scan_data->freqs) {
+                               g_slist_free_full(scan_data->ssids, g_free);
+                               return -ENOMEM;
+                       }
+                       scan_data->freqs[scan_data->num_freqs - 1] = freq;
+               }
+       }
+
+       return 1;
+}
+
+static int get_hidden_connections(GSupplicantScanParams *scan_data)
+{
+       struct connman_config_entry **entries;
+       GKeyFile *keyfile;
+       gchar **services;
+       char *ssid, *name;
+       int i, freq, ret;
+       bool value;
+       int num_ssids = 0, add_param_failed = 0;
+
+       services = connman_storage_get_services();
+       for (i = 0; services && services[i]; i++) {
+               if (strncmp(services[i], "wifi_", 5) != 0)
+                       continue;
+
+               keyfile = connman_storage_load_service(services[i]);
+               if (!keyfile)
+                       continue;
+
+               value = g_key_file_get_boolean(keyfile,
+                                       services[i], "Hidden", NULL);
+               if (!value) {
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+
+               value = g_key_file_get_boolean(keyfile,
+                                       services[i], "Favorite", NULL);
+               if (!value) {
+                       g_key_file_free(keyfile);
+                       continue;
+               }
+
+               ssid = g_key_file_get_string(keyfile,
+                                       services[i], "SSID", NULL);
+
+               freq = g_key_file_get_integer(keyfile, services[i],
+                                       "Frequency", NULL);
+
+               name = g_key_file_get_string(keyfile, services[i], "Name",
+                                                               NULL);
+
+               ret = add_scan_param(ssid, NULL, 0, freq, scan_data, 0, name);
+               if (ret < 0)
+                       add_param_failed++;
+               else if (ret > 0)
+                       num_ssids++;
+
+               g_free(ssid);
+               g_free(name);
+               g_key_file_free(keyfile);
+       }
+
+       /*
+        * Check if there are any hidden AP that needs to be provisioned.
+        */
+       entries = connman_config_get_entries("wifi");
+       for (i = 0; entries && entries[i]; i++) {
+               int len;
+
+               if (!entries[i]->hidden)
+                       continue;
+
+               if (!entries[i]->ssid) {
+                       ssid = entries[i]->name;
+                       len = strlen(ssid);
+               } else {
+                       ssid = entries[i]->ssid;
+                       len = entries[i]->ssid_len;
+               }
+
+               if (!ssid)
+                       continue;
+
+               ret = add_scan_param(NULL, ssid, len, 0, scan_data, 0, ssid);
+               if (ret < 0)
+                       add_param_failed++;
+               else if (ret > 0)
+                       num_ssids++;
+       }
+
+       connman_config_free_entries(entries);
+
+       if (add_param_failed > 0)
+               DBG("Unable to scan %d out of %d SSIDs",
+                                       add_param_failed, num_ssids);
+
+       g_strfreev(services);
+
+       return num_ssids;
+}
+
+static int get_hidden_connections_params(struct wifi_data *wifi,
+                                       GSupplicantScanParams *scan_params)
+{
+       int driver_max_ssids, i;
+       GSupplicantScanParams *orig_params;
+
+       /*
+        * Scan hidden networks so that we can autoconnect to them.
+        * We will assume 1 as a default number of ssid to scan.
+        */
+       driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
+                                                       wifi->interface);
+       if (driver_max_ssids == 0)
+               driver_max_ssids = 1;
+
+       DBG("max ssids %d", driver_max_ssids);
+
+       if (!wifi->scan_params) {
+               wifi->scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
+               if (!wifi->scan_params)
+                       return 0;
+
+               if (get_hidden_connections(wifi->scan_params) == 0) {
+                       g_supplicant_free_scan_params(wifi->scan_params);
+                       wifi->scan_params = NULL;
+
+                       return 0;
+               }
+       }
+
+       orig_params = wifi->scan_params;
+
+       /* Let's transfer driver_max_ssids params */
+       for (i = 0; i < driver_max_ssids; i++) {
+               struct scan_ssid *ssid;
+
+               if (!wifi->scan_params->ssids)
+                       break;
+
+               ssid = orig_params->ssids->data;
+               orig_params->ssids = g_slist_remove(orig_params->ssids, ssid);
+               scan_params->ssids = g_slist_prepend(scan_params->ssids, ssid);
+       }
+
+       if (i > 0) {
+               scan_params->num_ssids = i;
+               scan_params->ssids = g_slist_reverse(scan_params->ssids);
+
+               scan_params->freqs = g_memdup(orig_params->freqs,
+                               sizeof(uint16_t) * orig_params->num_freqs);
+               if (!scan_params->freqs)
+                       goto err;
+
+               scan_params->num_freqs = orig_params->num_freqs;
+
+       } else
+               goto err;
+
+       orig_params->num_ssids -= scan_params->num_ssids;
+
+       return scan_params->num_ssids;
+
+err:
+       g_slist_free_full(scan_params->ssids, g_free);
+       g_supplicant_free_scan_params(wifi->scan_params);
+       wifi->scan_params = NULL;
+
+       return 0;
+}
+
 static int throw_wifi_scan(struct connman_device *device,
                        GSupplicantInterfaceCallback callback)
 {
        struct wifi_data *wifi = connman_device_get_data(device);
        int ret;
 
+       if (!wifi)
+               return -ENODEV;
+
        DBG("device %p %p", device, wifi->interface);
 
-       if (wifi->tethering == TRUE)
-               return 0;
+       if (wifi->tethering)
+               return -EBUSY;
+
+       if (connman_device_get_scanning(device))
+               return -EALREADY;
 
        connman_device_ref(device);
 
        ret = g_supplicant_interface_scan(wifi->interface, NULL,
                                                callback, device);
-       if (ret == 0)
-               connman_device_set_scanning(device, TRUE);
-       else
+       if (ret == 0) {
+               connman_device_set_scanning(device,
+                               CONNMAN_SERVICE_TYPE_WIFI, true);
+       } else
                connman_device_unref(device);
 
        return ret;
 }
 
-static void autoscan_scan_callback(int result,
+static void hidden_free(struct hidden_params *hidden)
+{
+       if (!hidden)
+               return;
+
+       if (hidden->scan_params)
+               g_supplicant_free_scan_params(hidden->scan_params);
+       g_free(hidden->identity);
+       g_free(hidden->passphrase);
+       g_free(hidden->security);
+       g_free(hidden);
+}
+
+static void scan_callback(int result, GSupplicantInterface *interface,
+                                               void *user_data)
+{
+       struct connman_device *device = user_data;
+       struct wifi_data *wifi = connman_device_get_data(device);
+       bool scanning;
+
+       DBG("result %d wifi %p", result, wifi);
+
+       if (wifi) {
+               if (wifi->hidden && !wifi->postpone_hidden) {
+                       connman_network_clear_hidden(wifi->hidden->user_data);
+                       hidden_free(wifi->hidden);
+                       wifi->hidden = NULL;
+               }
+
+               if (wifi->scan_params) {
+                       g_supplicant_free_scan_params(wifi->scan_params);
+                       wifi->scan_params = NULL;
+               }
+       }
+
+       if (result < 0)
+               connman_device_reset_scanning(device);
+
+       /* User is connecting to a hidden AP, let's wait for finished event */
+       if (wifi && wifi->hidden && wifi->postpone_hidden) {
+               GSupplicantScanParams *scan_params;
+               int ret;
+
+               wifi->postpone_hidden = false;
+               scan_params = wifi->hidden->scan_params;
+               wifi->hidden->scan_params = NULL;
+
+               reset_autoscan(device);
+
+               ret = g_supplicant_interface_scan(wifi->interface, scan_params,
+                                                       scan_callback, device);
+               if (ret == 0)
+                       return;
+
+               /* On error, let's recall scan_callback, which will cleanup */
+               return scan_callback(ret, interface, user_data);
+       }
+
+       scanning = connman_device_get_scanning(device);
+
+       if (scanning) {
+               connman_device_set_scanning(device,
+                               CONNMAN_SERVICE_TYPE_WIFI, false);
+       }
+
+       if (result != -ENOLINK)
+               start_autoscan(device);
+
+       /*
+        * If we are here then we were scanning; however, if we are
+        * also mid-flight disabling the interface, then wifi_disable
+        * has already cleared the device scanning state and
+        * unreferenced the device, obviating the need to do it here.
+        */
+
+       if (scanning)
+               connman_device_unref(device);
+}
+
+static void scan_callback_hidden(int result,
                        GSupplicantInterface *interface, void *user_data)
 {
        struct connman_device *device = user_data;
+       struct wifi_data *wifi = connman_device_get_data(device);
+       GSupplicantScanParams *scan_params;
+       int ret;
 
-       DBG("");
+       DBG("result %d wifi %p", result, wifi);
 
-       connman_device_set_scanning(device, FALSE);
-       connman_device_unref(device);
+       if (!wifi)
+               goto out;
+
+       /* User is trying to connect to a hidden AP */
+       if (wifi->hidden && wifi->postpone_hidden)
+               goto out;
+
+       scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
+       if (!scan_params)
+               goto out;
+
+       if (get_hidden_connections_params(wifi, scan_params) > 0) {
+               ret = g_supplicant_interface_scan(wifi->interface,
+                                                       scan_params,
+                                                       scan_callback_hidden,
+                                                       device);
+               if (ret == 0)
+                       return;
+       }
+
+       g_supplicant_free_scan_params(scan_params);
+
+out:
+       scan_callback(result, interface, user_data);
 }
 
 static gboolean autoscan_timeout(gpointer data)
@@ -293,6 +750,9 @@ static gboolean autoscan_timeout(gpointer data)
        struct autoscan_params *autoscan;
        int interval;
 
+       if (!wifi)
+               return FALSE;
+
        autoscan = wifi->autoscan;
 
        if (autoscan->interval <= 0) {
@@ -304,7 +764,7 @@ static gboolean autoscan_timeout(gpointer data)
        if (autoscan->interval >= autoscan->limit)
                interval = autoscan->limit;
 
-       throw_wifi_scan(wifi->device, autoscan_scan_callback);
+       throw_wifi_scan(wifi->device, scan_callback_hidden);
 
 set_interval:
        DBG("interval %d", interval);
@@ -324,11 +784,11 @@ static void start_autoscan(struct connman_device *device)
 
        DBG("");
 
-       if (wifi == NULL)
+       if (!wifi)
                return;
 
        autoscan = wifi->autoscan;
-       if (autoscan == NULL)
+       if (!autoscan)
                return;
 
        if (autoscan->timeout > 0 || autoscan->interval > 0)
@@ -363,7 +823,7 @@ static struct autoscan_params *parse_autoscan_params(const char *params)
        g_strfreev(list_params);
 
        autoscan = g_try_malloc0(sizeof(struct autoscan_params));
-       if (autoscan == NULL) {
+       if (!autoscan) {
                DBG("Could not allocate memory for autoscan");
                return NULL;
        }
@@ -377,59 +837,90 @@ static struct autoscan_params *parse_autoscan_params(const char *params)
 
 static void setup_autoscan(struct wifi_data *wifi)
 {
-       if (wifi->autoscan == NULL)
+       if (!wifi->autoscan)
                wifi->autoscan = parse_autoscan_params(AUTOSCAN_DEFAULT);
 
        start_autoscan(wifi->device);
 }
 
-static void interface_create_callback(int result,
+static void interface_autoscan_callback(int result,
                                        GSupplicantInterface *interface,
                                                        void *user_data)
 {
        struct wifi_data *wifi = user_data;
 
-       DBG("result %d ifname %s, wifi %p", result,
-                               g_supplicant_interface_get_ifname(interface),
-                               wifi);
-
-       if (result < 0 || wifi == NULL)
-               return;
-
-       wifi->interface = interface;
-       g_supplicant_interface_set_data(interface, wifi);
+       if (result < 0) {
+               DBG("Could not enable Autoscan, falling back...");
+               setup_autoscan(wifi);
+       }
+}
 
-       if (g_supplicant_interface_get_ready(interface) == FALSE)
-               return;
+static void finalize_interface_creation(struct wifi_data *wifi)
+{
+       GSupplicantInterface *interface = wifi->interface;
 
        DBG("interface is ready wifi %p tethering %d", wifi, wifi->tethering);
 
-       if (wifi->device == NULL) {
+       if (!wifi->device) {
                connman_error("WiFi device not set");
                return;
        }
 
-       connman_device_set_powered(wifi->device, TRUE);
+       connman_device_set_powered(wifi->device, true);
 
-       if (connman_setting_get_bool("BackgroundScanning") == FALSE)
+       if (!connman_setting_get_bool("BackgroundScanning"))
                return;
 
        /* Setting up automatic scanning */
-       setup_autoscan(wifi);
+       if (g_supplicant_interface_autoscan(interface, AUTOSCAN_DEFAULT,
+                               interface_autoscan_callback, wifi) < 0) {
+               DBG("Could not enable Autoscan, falling back...");
+               setup_autoscan(wifi);
+       }
+}
+
+static void interface_create_callback(int result,
+                                       GSupplicantInterface *interface,
+                                                       void *user_data)
+{
+       struct wifi_data *wifi = user_data;
+
+       DBG("result %d ifname %s, wifi %p", result,
+                               g_supplicant_interface_get_ifname(interface),
+                               wifi);
+
+       if (result < 0 || !wifi)
+               return;
+
+       wifi->interface = interface;
+       g_supplicant_interface_set_data(interface, wifi);
+
+       if (g_supplicant_interface_get_ready(interface)) {
+               wifi->interface_ready = true;
+               finalize_interface_creation(wifi);
+       }
 }
 
 static int wifi_enable(struct connman_device *device)
 {
        struct wifi_data *wifi = connman_device_get_data(device);
-       const char *interface = connman_device_get_string(device, "Interface");
+       int index;
+       char *interface;
        const char *driver = connman_option_get_string("wifi");
        int ret;
 
        DBG("device %p %p", device, wifi);
 
+       index = connman_device_get_index(device);
+       if (!wifi || index < 0)
+               return -ENODEV;
+
+       interface = connman_inet_ifname(index);
        ret = g_supplicant_interface_create(interface, driver, NULL,
                                                interface_create_callback,
                                                        wifi);
+       g_free(interface);
+
        if (ret < 0)
                return ret;
 
@@ -441,14 +932,32 @@ static int wifi_disable(struct connman_device *device)
        struct wifi_data *wifi = connman_device_get_data(device);
        int ret;
 
-       DBG("device %p", device);
+       DBG("device %p wifi %p", device, wifi);
 
-       wifi->connected = FALSE;
-       wifi->disconnecting = FALSE;
+       if (!wifi)
+               return -ENODEV;
+
+       wifi->connected = false;
+       wifi->disconnecting = false;
 
-       if (wifi->pending_network != NULL)
+       if (wifi->pending_network)
                wifi->pending_network = NULL;
 
+       stop_autoscan(device);
+
+       if (wifi->p2p_find_timeout) {
+               g_source_remove(wifi->p2p_find_timeout);
+               wifi->p2p_find_timeout = 0;
+               connman_device_unref(wifi->device);
+       }
+
+       /* In case of a user scan, device is still referenced */
+       if (connman_device_get_scanning(device)) {
+               connman_device_set_scanning(device,
+                               CONNMAN_SERVICE_TYPE_WIFI, false);
+               connman_device_unref(wifi->device);
+       }
+
        remove_networks(device, wifi);
 
        ret = g_supplicant_interface_remove(wifi->interface, NULL, NULL);
@@ -458,77 +967,6 @@ static int wifi_disable(struct connman_device *device)
        return -EINPROGRESS;
 }
 
-static void hidden_free(struct hidden_params *hidden)
-{
-       if (hidden == NULL)
-               return;
-
-       g_free(hidden->identity);
-       g_free(hidden->passphrase);
-       g_free(hidden);
-}
-
-static void scan_callback(int result, GSupplicantInterface *interface,
-                                               void *user_data)
-{
-       struct connman_device *device = user_data;
-       struct wifi_data *wifi = connman_device_get_data(device);
-
-       DBG("result %d", result);
-
-       if (wifi != NULL && wifi->hidden != NULL) {
-               hidden_free(wifi->hidden);
-               wifi->hidden = NULL;
-       }
-
-       if (result < 0)
-               connman_device_reset_scanning(device);
-
-       connman_device_set_scanning(device, FALSE);
-       connman_device_unref(device);
-
-       start_autoscan(device);
-}
-
-static int add_scan_param(gchar *hex_ssid, int freq,
-                       GSupplicantScanParams *scan_data,
-                       int driver_max_scan_ssids)
-{
-       unsigned int i;
-
-       if (driver_max_scan_ssids > scan_data->num_ssids && hex_ssid != NULL) {
-               gchar *ssid;
-               unsigned int j = 0, hex;
-               size_t hex_ssid_len = strlen(hex_ssid);
-
-               ssid = g_try_malloc0(hex_ssid_len / 2);
-               if (ssid == NULL)
-                       return -ENOMEM;
-
-               for (i = 0; i < hex_ssid_len; i += 2) {
-                       sscanf(hex_ssid + i, "%02x", &hex);
-                       ssid[j++] = hex;
-               }
-
-               memcpy(scan_data->ssids[scan_data->num_ssids].ssid, ssid, j);
-               scan_data->ssids[scan_data->num_ssids].ssid_len = j;
-               scan_data->num_ssids++;
-
-               g_free(ssid);
-       }
-
-       /* Don't add duplicate entries */
-       for (i = 0; i < G_SUPPLICANT_MAX_FAST_SCAN; i++) {
-               if (scan_data->freqs[i] == 0) {
-                       scan_data->freqs[i] = freq;
-                       break;
-               } else if (scan_data->freqs[i] == freq)
-                       break;
-       }
-
-       return 0;
-}
-
 struct last_connected {
        GTimeVal modified;
        gchar *ssid;
@@ -573,7 +1011,7 @@ static int get_latest_connections(int max_ssids,
        int num_ssids = 0;
 
        latest_list = g_sequence_new(free_entry);
-       if (latest_list == NULL)
+       if (!latest_list)
                return -ENOMEM;
 
        services = connman_storage_get_services();
@@ -582,12 +1020,13 @@ static int get_latest_connections(int max_ssids,
                        continue;
 
                keyfile = connman_storage_load_service(services[i]);
+               if (!keyfile)
+                       continue;
 
                str = g_key_file_get_string(keyfile,
                                        services[i], "Favorite", NULL);
-               if (str == NULL || g_strcmp0(str, "true")) {
-                       if (str)
-                               g_free(str);
+               if (!str || g_strcmp0(str, "true")) {
+                       g_free(str);
                        g_key_file_free(keyfile);
                        continue;
                }
@@ -595,9 +1034,8 @@ static int get_latest_connections(int max_ssids,
 
                str = g_key_file_get_string(keyfile,
                                        services[i], "AutoConnect", NULL);
-               if (str == NULL || g_strcmp0(str, "true")) {
-                       if (str)
-                               g_free(str);
+               if (!str || g_strcmp0(str, "true")) {
+                       g_free(str);
                        g_key_file_free(keyfile);
                        continue;
                }
@@ -605,10 +1043,12 @@ static int get_latest_connections(int max_ssids,
 
                str = g_key_file_get_string(keyfile,
                                        services[i], "Modified", NULL);
-               if (str != NULL) {
-                       g_time_val_from_iso8601(str, &modified);
-                       g_free(str);
+               if (!str) {
+                       g_key_file_free(keyfile);
+                       continue;
                }
+               g_time_val_from_iso8601(str, &modified);
+               g_free(str);
 
                ssid = g_key_file_get_string(keyfile,
                                        services[i], "SSID", NULL);
@@ -617,7 +1057,7 @@ static int get_latest_connections(int max_ssids,
                                        "Frequency", NULL);
                if (freq) {
                        entry = g_try_new(struct last_connected, 1);
-                       if (entry == NULL) {
+                       if (!entry) {
                                g_sequence_free(latest_list);
                                g_key_file_free(keyfile);
                                g_free(ssid);
@@ -639,8 +1079,7 @@ static int get_latest_connections(int max_ssids,
 
        g_strfreev(services);
 
-       num_ssids = num_ssids > G_SUPPLICANT_MAX_FAST_SCAN ?
-               G_SUPPLICANT_MAX_FAST_SCAN : num_ssids;
+       num_ssids = num_ssids > max_ssids ? max_ssids : num_ssids;
 
        iter = g_sequence_get_begin_iter(latest_list);
 
@@ -650,7 +1089,8 @@ static int get_latest_connections(int max_ssids,
                DBG("ssid %s freq %d modified %lu", entry->ssid, entry->freq,
                                                entry->modified.tv_sec);
 
-               add_scan_param(entry->ssid, entry->freq, scan_data, max_ssids);
+               add_scan_param(entry->ssid, NULL, 0, entry->freq, scan_data,
+                                               max_ssids, entry->ssid);
 
                iter = g_sequence_iter_next(iter);
        }
@@ -659,108 +1099,238 @@ static int get_latest_connections(int max_ssids,
        return num_ssids;
 }
 
-static int wifi_scan(struct connman_device *device)
+static int wifi_scan_simple(struct connman_device *device)
 {
-       stop_autoscan(device);
+       reset_autoscan(device);
 
-       return throw_wifi_scan(device, scan_callback);
+       return throw_wifi_scan(device, scan_callback_hidden);
 }
 
-static int wifi_scan_fast(struct connman_device *device)
+static gboolean p2p_find_stop(gpointer data)
 {
+       struct connman_device *device = data;
        struct wifi_data *wifi = connman_device_get_data(device);
-       GSupplicantScanParams *scan_params = NULL;
-       int ret;
-       int driver_max_ssids = 0;
 
-       DBG("device %p %p", device, wifi->interface);
+       DBG("");
 
-       if (wifi->tethering == TRUE)
-               return 0;
+       wifi->p2p_find_timeout = 0;
 
-       driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
-                                                       wifi->interface);
-       DBG("max ssids %d", driver_max_ssids);
-       if (driver_max_ssids == 0)
-               return wifi_scan(device);
+       connman_device_set_scanning(device, CONNMAN_SERVICE_TYPE_P2P, false);
 
-       scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
-       if (scan_params == NULL)
-               return -ENOMEM;
+       g_supplicant_interface_p2p_stop_find(wifi->interface);
+
+       connman_device_unref(device);
+       reset_autoscan(device);
+
+       return FALSE;
+}
 
-       ret = get_latest_connections(driver_max_ssids, scan_params);
-       if (ret <= 0) {
-               g_free(scan_params);
-               return wifi_scan(device);
+static void p2p_find_callback(int result, GSupplicantInterface *interface,
+                                                       void *user_data)
+{
+       struct connman_device *device = user_data;
+       struct wifi_data *wifi = connman_device_get_data(device);
+
+       DBG("result %d wifi %p", result, wifi);
+
+       if (wifi->p2p_find_timeout) {
+               g_source_remove(wifi->p2p_find_timeout);
+               wifi->p2p_find_timeout = 0;
        }
 
-       stop_autoscan(device);
+       if (result)
+               goto error;
+
+       wifi->p2p_find_timeout = g_timeout_add_seconds(P2P_FIND_TIMEOUT,
+                                                       p2p_find_stop, device);
+       if (!wifi->p2p_find_timeout)
+               goto error;
+
+       return;
+error:
+       p2p_find_stop(device);
+}
+
+static int p2p_find(struct connman_device *device)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       int ret;
+
+       DBG("");
+
+       if (!p2p_technology)
+               return -ENOTSUP;
 
+       reset_autoscan(device);
        connman_device_ref(device);
-       ret = g_supplicant_interface_scan(wifi->interface, scan_params,
-                                               scan_callback, device);
-       if (ret == 0)
-               connman_device_set_scanning(device, TRUE);
-       else {
-               g_free(scan_params);
+
+       ret = g_supplicant_interface_p2p_find(wifi->interface,
+                                               p2p_find_callback, device);
+       if (ret) {
                connman_device_unref(device);
+               start_autoscan(device);
+       } else {
+               connman_device_set_scanning(device,
+                               CONNMAN_SERVICE_TYPE_P2P, true);
        }
 
        return ret;
 }
 
-static int wifi_scan_hidden(struct connman_device *device,
-               const char *ssid, unsigned int ssid_len,
-               const char *identity, const char* passphrase)
+/*
+ * Note that the hidden scan is only used when connecting to this specific
+ * hidden AP first time. It is not used when system autoconnects to hidden AP.
+ */
+static int wifi_scan(enum connman_service_type type,
+                       struct connman_device *device,
+                       const char *ssid, unsigned int ssid_len,
+                       const char *identity, const char* passphrase,
+                       const char *security, void *user_data)
 {
        struct wifi_data *wifi = connman_device_get_data(device);
        GSupplicantScanParams *scan_params = NULL;
+       struct scan_ssid *scan_ssid;
        struct hidden_params *hidden;
        int ret;
+       int driver_max_ssids = 0;
+       bool do_hidden;
+       bool scanning;
 
-       DBG("hidden SSID %s", ssid);
+       if (!wifi)
+               return -ENODEV;
 
-       if (wifi->tethering == TRUE || wifi->hidden != NULL)
-               return -EBUSY;
+       if (type == CONNMAN_SERVICE_TYPE_P2P)
+               return p2p_find(device);
 
-       if (ssid == NULL || ssid_len == 0 || ssid_len > 32)
-               return -EINVAL;
+       DBG("device %p wifi %p hidden ssid %s", device, wifi->interface, ssid);
+
+       if (wifi->tethering)
+               return 0;
+
+       scanning = connman_device_get_scanning(device);
+
+       if (!ssid || ssid_len == 0 || ssid_len > 32) {
+               if (scanning)
+                       return -EALREADY;
+
+               driver_max_ssids = g_supplicant_interface_get_max_scan_ssids(
+                                                       wifi->interface);
+               DBG("max ssids %d", driver_max_ssids);
+               if (driver_max_ssids == 0)
+                       return wifi_scan_simple(device);
+
+               do_hidden = false;
+       } else {
+               if (scanning && wifi->hidden && wifi->postpone_hidden)
+                       return -EALREADY;
+
+               do_hidden = true;
+       }
 
        scan_params = g_try_malloc0(sizeof(GSupplicantScanParams));
-       if (scan_params == NULL)
+       if (!scan_params)
                return -ENOMEM;
-       memcpy(scan_params->ssids[0].ssid, ssid, ssid_len);
-       scan_params->ssids[0].ssid_len = ssid_len;
-       scan_params->num_ssids = 1;
 
-       hidden = g_try_new0(struct hidden_params, 1);
-       if (hidden == NULL) {
-               g_free(scan_params);
-               return -ENOMEM;
-       }
-       memcpy(hidden->ssid, ssid, ssid_len);
-       hidden->ssid_len = ssid_len;
-       hidden->identity = g_strdup(identity);
-       hidden->passphrase = g_strdup(passphrase);
-       wifi->hidden = hidden;
+       if (do_hidden) {
+               scan_ssid = g_try_new(struct scan_ssid, 1);
+               if (!scan_ssid) {
+                       g_free(scan_params);
+                       return -ENOMEM;
+               }
 
-       stop_autoscan(device);
+               memcpy(scan_ssid->ssid, ssid, ssid_len);
+               scan_ssid->ssid_len = ssid_len;
+               scan_params->ssids = g_slist_prepend(scan_params->ssids,
+                                                               scan_ssid);
+               scan_params->num_ssids = 1;
+
+               hidden = g_try_new0(struct hidden_params, 1);
+               if (!hidden) {
+                       g_supplicant_free_scan_params(scan_params);
+                       return -ENOMEM;
+               }
+
+               if (wifi->hidden) {
+                       hidden_free(wifi->hidden);
+                       wifi->hidden = NULL;
+               }
+
+               memcpy(hidden->ssid, ssid, ssid_len);
+               hidden->ssid_len = ssid_len;
+               hidden->identity = g_strdup(identity);
+               hidden->passphrase = g_strdup(passphrase);
+               hidden->security = g_strdup(security);
+               hidden->user_data = user_data;
+               wifi->hidden = hidden;
+
+               if (scanning) {
+                       /* Let's keep this active scan for later,
+                        * when current scan will be over. */
+                       wifi->postpone_hidden = TRUE;
+                       hidden->scan_params = scan_params;
+
+                       return 0;
+               }
+       } else {
+               ret = get_latest_connections(driver_max_ssids, scan_params);
+               if (ret <= 0) {
+                       g_supplicant_free_scan_params(scan_params);
+                       return wifi_scan_simple(device);
+               }
+       }
 
        connman_device_ref(device);
+
+       reset_autoscan(device);
+
        ret = g_supplicant_interface_scan(wifi->interface, scan_params,
-                       scan_callback, device);
-       if (ret == 0)
-               connman_device_set_scanning(device, TRUE);
-       else {
+                                               scan_callback, device);
+       if (ret == 0) {
+               connman_device_set_scanning(device,
+                               CONNMAN_SERVICE_TYPE_WIFI, true);
+       } else {
+               g_supplicant_free_scan_params(scan_params);
                connman_device_unref(device);
-               g_free(scan_params);
-               hidden_free(wifi->hidden);
-               wifi->hidden = NULL;
+
+               if (do_hidden) {
+                       hidden_free(wifi->hidden);
+                       wifi->hidden = NULL;
+               }
        }
 
        return ret;
 }
 
+static void wifi_regdom_callback(int result,
+                                       const char *alpha2,
+                                               void *user_data)
+{
+       struct connman_device *device = user_data;
+
+       connman_device_regdom_notify(device, result, alpha2);
+
+       connman_device_unref(device);
+}
+
+static int wifi_set_regdom(struct connman_device *device, const char *alpha2)
+{
+       struct wifi_data *wifi = connman_device_get_data(device);
+       int ret;
+
+       if (!wifi)
+               return -EINVAL;
+
+       connman_device_ref(device);
+
+       ret = g_supplicant_interface_set_country(wifi->interface,
+                                               wifi_regdom_callback,
+                                                       alpha2, device);
+       if (ret != 0)
+               connman_device_unref(device);
+
+       return ret;
+}
+
 static struct connman_device_driver wifi_ng_driver = {
        .name           = "wifi",
        .type           = CONNMAN_DEVICE_TYPE_WIFI,
@@ -770,8 +1340,7 @@ static struct connman_device_driver wifi_ng_driver = {
        .enable         = wifi_enable,
        .disable        = wifi_disable,
        .scan           = wifi_scan,
-       .scan_fast      = wifi_scan_fast,
-       .scan_hidden    = wifi_scan_hidden,
+       .set_regdom     = wifi_set_regdom,
 };
 
 static void system_ready(void)
@@ -804,7 +1373,7 @@ static void network_remove(struct connman_network *network)
        DBG("network %p", network);
 
        wifi = connman_device_get_data(device);
-       if (wifi == NULL)
+       if (!wifi)
                return;
 
        if (wifi->network != network)
@@ -827,21 +1396,23 @@ static void connect_callback(int result, GSupplicantInterface *interface,
                connman_network_set_error(network,
                                        CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
        }
+
+       connman_network_unref(network);
 }
 
 static GSupplicantSecurity network_security(const char *security)
 {
-       if (g_str_equal(security, "none") == TRUE)
+       if (g_str_equal(security, "none"))
                return G_SUPPLICANT_SECURITY_NONE;
-       else if (g_str_equal(security, "wep") == TRUE)
+       else if (g_str_equal(security, "wep"))
                return G_SUPPLICANT_SECURITY_WEP;
-       else if (g_str_equal(security, "psk") == TRUE)
+       else if (g_str_equal(security, "psk"))
                return G_SUPPLICANT_SECURITY_PSK;
-       else if (g_str_equal(security, "wpa") == TRUE)
+       else if (g_str_equal(security, "wpa"))
                return G_SUPPLICANT_SECURITY_PSK;
-       else if (g_str_equal(security, "rsn") == TRUE)
+       else if (g_str_equal(security, "rsn"))
                return G_SUPPLICANT_SECURITY_PSK;
-       else if (g_str_equal(security, "ieee8021x") == TRUE)
+       else if (g_str_equal(security, "ieee8021x"))
                return G_SUPPLICANT_SECURITY_IEEE8021X;
 
        return G_SUPPLICANT_SECURITY_UNKNOWN;
@@ -849,7 +1420,7 @@ static GSupplicantSecurity network_security(const char *security)
 
 static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network)
 {
-       const char *security, *passphrase, *agent_passphrase;
+       const char *security;
 
        memset(ssid, 0, sizeof(*ssid));
        ssid->mode = G_SUPPLICANT_MODE_INFRA;
@@ -858,20 +1429,8 @@ static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network)
        ssid->scan_ssid = 1;
        security = connman_network_get_string(network, "WiFi.Security");
        ssid->security = network_security(security);
-       passphrase = connman_network_get_string(network,
+       ssid->passphrase = connman_network_get_string(network,
                                                "WiFi.Passphrase");
-       if (passphrase == NULL || strlen(passphrase) == 0) {
-
-               /* Use agent provided passphrase as a fallback */
-               agent_passphrase = connman_network_get_string(network,
-                                               "WiFi.AgentPassphrase");
-
-               if (agent_passphrase == NULL || strlen(agent_passphrase) == 0)
-                       ssid->passphrase = NULL;
-               else
-                       ssid->passphrase = agent_passphrase;
-       } else
-               ssid->passphrase = passphrase;
 
        ssid->eap = connman_network_get_string(network, "WiFi.EAP");
 
@@ -881,8 +1440,7 @@ static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network)
         * for PEAP where 2 passphrases (identity and client
         * cert may have to be provided.
         */
-       if (connman_network_get_string(network,
-                                       "WiFi.PrivateKeyPassphrase") == NULL)
+       if (!connman_network_get_string(network, "WiFi.PrivateKeyPassphrase"))
                connman_network_set_string(network,
                                                "WiFi.PrivateKeyPassphrase",
                                                ssid->passphrase);
@@ -890,7 +1448,7 @@ static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network)
        ssid->identity = connman_network_get_string(network, "WiFi.Identity");
 
        /* Use agent provided identity as a fallback */
-       if (ssid->identity == NULL || strlen(ssid->identity) == 0)
+       if (!ssid->identity || strlen(ssid->identity) == 0)
                ssid->identity = connman_network_get_string(network,
                                                        "WiFi.AgentIdentity");
 
@@ -907,7 +1465,7 @@ static void ssid_init(GSupplicantSSID *ssid, struct connman_network *network)
        ssid->use_wps = connman_network_get_bool(network, "WiFi.UseWPS");
        ssid->pin_wps = connman_network_get_string(network, "WiFi.PinWPS");
 
-       if (connman_setting_get_bool("BackgroundScanning") == TRUE)
+       if (connman_setting_get_bool("BackgroundScanning"))
                ssid->bgscan = BGSCAN_DEFAULT;
 }
 
@@ -920,25 +1478,26 @@ static int network_connect(struct connman_network *network)
 
        DBG("network %p", network);
 
-       if (device == NULL)
+       if (!device)
                return -ENODEV;
 
        wifi = connman_device_get_data(device);
-       if (wifi == NULL)
+       if (!wifi)
                return -ENODEV;
 
        ssid = g_try_malloc0(sizeof(GSupplicantSSID));
-       if (ssid == NULL)
+       if (!ssid)
                return -ENOMEM;
 
        interface = wifi->interface;
 
        ssid_init(ssid, network);
 
-       if (wifi->disconnecting == TRUE)
+       if (wifi->disconnecting) {
                wifi->pending_network = network;
-       else {
-               wifi->network = network;
+               g_free(ssid);
+       } else {
+               wifi->network = connman_network_ref(network);
                wifi->retries = 0;
 
                return g_supplicant_interface_connect(interface, ssid,
@@ -953,7 +1512,15 @@ static void disconnect_callback(int result, GSupplicantInterface *interface,
 {
        struct wifi_data *wifi = user_data;
 
-       if (wifi->network != NULL) {
+       DBG("result %d supplicant interface %p wifi %p",
+                       result, interface, wifi);
+
+       if (result == -ECONNABORTED) {
+               DBG("wifi interface no longer available");
+               return;
+       }
+
+       if (wifi->network) {
                /*
                 * if result < 0 supplican return an error because
                 * the network is not current.
@@ -962,14 +1529,14 @@ static void disconnect_callback(int result, GSupplicantInterface *interface,
                 * disconnect is completed.
                 */
                if (result < 0)
-                       connman_network_set_connected(wifi->network, FALSE);
+                       connman_network_set_connected(wifi->network, false);
        }
 
        wifi->network = NULL;
 
-       wifi->disconnecting = FALSE;
+       wifi->disconnecting = false;
 
-       if (wifi->pending_network != NULL) {
+       if (wifi->pending_network) {
                network_connect(wifi->pending_network);
                wifi->pending_network = NULL;
        }
@@ -986,20 +1553,20 @@ static int network_disconnect(struct connman_network *network)
        DBG("network %p", network);
 
        wifi = connman_device_get_data(device);
-       if (wifi == NULL || wifi->interface == NULL)
+       if (!wifi || !wifi->interface)
                return -ENODEV;
 
-       connman_network_set_associating(network, FALSE);
+       connman_network_set_associating(network, false);
 
-       if (wifi->disconnecting == TRUE)
+       if (wifi->disconnecting)
                return -EALREADY;
 
-       wifi->disconnecting = TRUE;
+       wifi->disconnecting = true;
 
        err = g_supplicant_interface_disconnect(wifi->interface,
                                                disconnect_callback, wifi);
        if (err < 0)
-               wifi->disconnecting = FALSE;
+               wifi->disconnecting = false;
 
        return err;
 }
@@ -1027,33 +1594,34 @@ static void interface_added(GSupplicantInterface *interface)
         * the interface added signal is sent before the
         * interface creation callback is called.
         */
-       if (wifi == NULL)
+       if (!wifi)
                return;
 
        DBG("ifname %s driver %s wifi %p tethering %d",
                        ifname, driver, wifi, wifi->tethering);
 
-       if (wifi->device == NULL) {
+       if (!wifi->device) {
                connman_error("WiFi device not set");
                return;
        }
 
-       connman_device_set_powered(wifi->device, TRUE);
+       connman_device_set_powered(wifi->device, true);
 
-       if (wifi->tethering == TRUE)
+       if (wifi->tethering)
                return;
 }
 
-static connman_bool_t is_idle(struct wifi_data *wifi)
+static bool is_idle(struct wifi_data *wifi)
 {
        DBG("state %d", wifi->state);
 
        switch (wifi->state) {
        case G_SUPPLICANT_STATE_UNKNOWN:
+       case G_SUPPLICANT_STATE_DISABLED:
        case G_SUPPLICANT_STATE_DISCONNECTED:
        case G_SUPPLICANT_STATE_INACTIVE:
        case G_SUPPLICANT_STATE_SCANNING:
-               return TRUE;
+               return true;
 
        case G_SUPPLICANT_STATE_AUTHENTICATING:
        case G_SUPPLICANT_STATE_ASSOCIATING:
@@ -1061,49 +1629,50 @@ static connman_bool_t is_idle(struct wifi_data *wifi)
        case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
        case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
        case G_SUPPLICANT_STATE_COMPLETED:
-               return FALSE;
+               return false;
        }
 
-       return FALSE;
+       return false;
 }
 
-static connman_bool_t is_idle_wps(GSupplicantInterface *interface,
+static bool is_idle_wps(GSupplicantInterface *interface,
                                                struct wifi_data *wifi)
 {
        /* First, let's check if WPS processing did not went wrong */
        if (g_supplicant_interface_get_wps_state(interface) ==
                G_SUPPLICANT_WPS_STATE_FAIL)
-               return FALSE;
+               return false;
 
        /* Unlike normal connection, being associated while processing wps
         * actually means that we are idling. */
        switch (wifi->state) {
        case G_SUPPLICANT_STATE_UNKNOWN:
+       case G_SUPPLICANT_STATE_DISABLED:
        case G_SUPPLICANT_STATE_DISCONNECTED:
        case G_SUPPLICANT_STATE_INACTIVE:
        case G_SUPPLICANT_STATE_SCANNING:
        case G_SUPPLICANT_STATE_ASSOCIATED:
-               return TRUE;
+               return true;
        case G_SUPPLICANT_STATE_AUTHENTICATING:
        case G_SUPPLICANT_STATE_ASSOCIATING:
        case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
        case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
        case G_SUPPLICANT_STATE_COMPLETED:
-               return FALSE;
+               return false;
        }
 
-       return FALSE;
+       return false;
 }
 
-static connman_bool_t handle_wps_completion(GSupplicantInterface *interface,
+static bool handle_wps_completion(GSupplicantInterface *interface,
                                        struct connman_network *network,
                                        struct connman_device *device,
                                        struct wifi_data *wifi)
 {
-       connman_bool_t wps;
+       bool wps;
 
        wps = connman_network_get_bool(network, "WiFi.UseWPS");
-       if (wps == TRUE) {
+       if (wps) {
                const unsigned char *ssid, *wps_ssid;
                unsigned int ssid_len, wps_ssid_len;
                const char *wps_key;
@@ -1116,12 +1685,12 @@ static connman_bool_t handle_wps_completion(GSupplicantInterface *interface,
                wps_ssid = g_supplicant_interface_get_wps_ssid(
                        interface, &wps_ssid_len);
 
-               if (wps_ssid == NULL || wps_ssid_len != ssid_len ||
+               if (!wps_ssid || wps_ssid_len != ssid_len ||
                                memcmp(ssid, wps_ssid, ssid_len) != 0) {
-                       connman_network_set_associating(network, FALSE);
+                       connman_network_set_associating(network, false);
                        g_supplicant_interface_disconnect(wifi->interface,
                                                disconnect_callback, wifi);
-                       return FALSE;
+                       return false;
                }
 
                wps_key = g_supplicant_interface_get_wps_key(interface);
@@ -1131,24 +1700,33 @@ static connman_bool_t handle_wps_completion(GSupplicantInterface *interface,
                connman_network_set_string(network, "WiFi.PinWPS", NULL);
        }
 
-       return TRUE;
+       return true;
 }
 
-static connman_bool_t handle_4way_handshake_failure(GSupplicantInterface *interface,
+static bool handle_4way_handshake_failure(GSupplicantInterface *interface,
                                        struct connman_network *network,
                                        struct wifi_data *wifi)
 {
+       struct connman_service *service;
+
        if (wifi->state != G_SUPPLICANT_STATE_4WAY_HANDSHAKE)
-               return FALSE;
+               return false;
+
+       service = connman_service_lookup_from_network(network);
+       if (!service)
+               return false;
 
        wifi->retries++;
 
-       if (wifi->retries < MAXIMUM_RETRIES)
-               return TRUE;
+       if (connman_service_get_favorite(service)) {
+               if (wifi->retries < FAVORITE_MAXIMUM_RETRIES)
+                       return true;
+       }
 
+       wifi->retries = 0;
        connman_network_set_error(network, CONNMAN_NETWORK_ERROR_INVALID_KEY);
 
-       return FALSE;
+       return false;
 }
 
 static void interface_state(GSupplicantInterface *interface)
@@ -1157,19 +1735,27 @@ static void interface_state(GSupplicantInterface *interface)
        struct connman_device *device;
        struct wifi_data *wifi;
        GSupplicantState state = g_supplicant_interface_get_state(interface);
-       connman_bool_t wps;
+       bool wps;
 
        wifi = g_supplicant_interface_get_data(interface);
 
        DBG("wifi %p interface state %d", wifi, state);
 
-       if (wifi == NULL)
+       if (!wifi)
                return;
 
-       network = wifi->network;
        device = wifi->device;
+       if (!device)
+               return;
+
+       if (g_supplicant_interface_get_ready(interface) &&
+                                       !wifi->interface_ready) {
+               wifi->interface_ready = true;
+               finalize_interface_creation(wifi);
+       }
 
-       if (device == NULL || network == NULL)
+       network = wifi->network;
+       if (!network)
                return;
 
        switch (state) {
@@ -1180,8 +1766,8 @@ static void interface_state(GSupplicantInterface *interface)
        case G_SUPPLICANT_STATE_ASSOCIATING:
                stop_autoscan(device);
 
-               if (wifi->connected == FALSE)
-                       connman_network_set_associating(network, TRUE);
+               if (!wifi->connected)
+                       connman_network_set_associating(network, true);
 
                break;
 
@@ -1189,11 +1775,10 @@ static void interface_state(GSupplicantInterface *interface)
                /* though it should be already stopped: */
                stop_autoscan(device);
 
-               if (handle_wps_completion(interface, network, device, wifi) ==
-                                                                       FALSE)
+               if (!handle_wps_completion(interface, network, device, wifi))
                        break;
 
-               connman_network_set_connected(network, TRUE);
+               connman_network_set_connected(network, true);
                break;
 
        case G_SUPPLICANT_STATE_DISCONNECTED:
@@ -1204,8 +1789,8 @@ static void interface_state(GSupplicantInterface *interface)
                 * in progress.
                 */
                wps = connman_network_get_bool(network, "WiFi.UseWPS");
-               if (wps == TRUE)
-                       if (is_idle_wps(interface, wifi) == TRUE)
+               if (wps)
+                       if (is_idle_wps(interface, wifi))
                                break;
 
                if (is_idle(wifi))
@@ -1216,7 +1801,7 @@ static void interface_state(GSupplicantInterface *interface)
                 * or if we reach the maximum retries we declare the
                 * psk as wrong */
                if (handle_4way_handshake_failure(interface,
-                                               network, wifi) == TRUE)
+                                               network, wifi))
                        break;
 
                /* We disable the selected network, if not then
@@ -1225,20 +1810,22 @@ static void interface_state(GSupplicantInterface *interface)
                                                FALSE) != 0)
                        DBG("Could not disables selected network");
 
-               connman_network_set_connected(network, FALSE);
-               connman_network_set_associating(network, FALSE);
+               connman_network_set_connected(network, false);
+               connman_network_set_associating(network, false);
+               wifi->disconnecting = false;
 
                start_autoscan(device);
 
                break;
 
        case G_SUPPLICANT_STATE_INACTIVE:
-               connman_network_set_associating(network, FALSE);
+               connman_network_set_associating(network, false);
                start_autoscan(device);
 
                break;
 
        case G_SUPPLICANT_STATE_UNKNOWN:
+       case G_SUPPLICANT_STATE_DISABLED:
        case G_SUPPLICANT_STATE_ASSOCIATED:
        case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
        case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
@@ -1261,17 +1848,17 @@ static void interface_state(GSupplicantInterface *interface)
        case G_SUPPLICANT_STATE_ASSOCIATED:
        case G_SUPPLICANT_STATE_4WAY_HANDSHAKE:
        case G_SUPPLICANT_STATE_GROUP_HANDSHAKE:
-               if (wifi->connected == TRUE)
+               if (wifi->connected)
                        connman_warn("Probably roaming right now!"
                                                " Staying connected...");
                else
-                       wifi->connected = FALSE;
+                       wifi->connected = false;
                break;
        case G_SUPPLICANT_STATE_COMPLETED:
-               wifi->connected = TRUE;
+               wifi->connected = true;
                break;
        default:
-               wifi->connected = FALSE;
+               wifi->connected = false;
                break;
        }
 
@@ -1287,16 +1874,29 @@ static void interface_removed(GSupplicantInterface *interface)
 
        wifi = g_supplicant_interface_get_data(interface);
 
-       if (wifi != NULL && wifi->tethering == TRUE)
+       if (wifi && wifi->tethering)
                return;
 
-       if (wifi == NULL || wifi->device == NULL) {
-               connman_error("Wrong wifi pointer");
+       if (!wifi || !wifi->device) {
+               DBG("wifi interface already removed");
                return;
        }
 
        wifi->interface = NULL;
-       connman_device_set_powered(wifi->device, FALSE);
+       connman_device_set_powered(wifi->device, false);
+
+       check_p2p_technology();
+}
+
+static void p2p_support(GSupplicantInterface *interface)
+{
+       DBG("");
+
+       if (!g_supplicant_interface_has_p2p(interface))
+               return;
+
+       if (connman_technology_driver_register(&p2p_tech_driver) == 0)
+               DBG("Could not register P2P technology driver");
 }
 
 static void scan_started(GSupplicantInterface *interface)
@@ -1328,17 +1928,22 @@ static void network_added(GSupplicantNetwork *supplicant_network)
        const char *name, *identifier, *security, *group, *mode;
        const unsigned char *ssid;
        unsigned int ssid_len;
-       connman_bool_t wps;
-       connman_bool_t wps_pbc;
-       connman_bool_t wps_ready;
-       connman_bool_t wps_advertizing;
+       bool wps;
+       bool wps_pbc;
+       bool wps_ready;
+       bool wps_advertizing;
 
-       DBG("");
+       mode = g_supplicant_network_get_mode(supplicant_network);
+       identifier = g_supplicant_network_get_identifier(supplicant_network);
+
+       DBG("%s", identifier);
+
+       if (!g_strcmp0(mode, "adhoc"))
+               return;
 
        interface = g_supplicant_network_get_interface(supplicant_network);
        wifi = g_supplicant_interface_get_data(interface);
        name = g_supplicant_network_get_name(supplicant_network);
-       identifier = g_supplicant_network_get_identifier(supplicant_network);
        security = g_supplicant_network_get_security(supplicant_network);
        group = g_supplicant_network_get_identifier(supplicant_network);
        wps = g_supplicant_network_get_wps(supplicant_network);
@@ -1346,19 +1951,18 @@ static void network_added(GSupplicantNetwork *supplicant_network)
        wps_ready = g_supplicant_network_is_wps_active(supplicant_network);
        wps_advertizing = g_supplicant_network_is_wps_advertizing(
                                                        supplicant_network);
-       mode = g_supplicant_network_get_mode(supplicant_network);
 
-       if (wifi == NULL)
+       if (!wifi)
                return;
 
        ssid = g_supplicant_network_get_ssid(supplicant_network, &ssid_len);
 
        network = connman_device_get_network(wifi->device, identifier);
 
-       if (network == NULL) {
+       if (!network) {
                network = connman_network_create(identifier,
                                                CONNMAN_NETWORK_TYPE_WIFI);
-               if (network == NULL)
+               if (!network)
                        return;
 
                connman_network_set_index(network, wifi->index);
@@ -1368,10 +1972,10 @@ static void network_added(GSupplicantNetwork *supplicant_network)
                        return;
                }
 
-               wifi->networks = g_slist_append(wifi->networks, network);
+               wifi->networks = g_slist_prepend(wifi->networks, network);
        }
 
-       if (name != NULL && name[0] != '\0')
+       if (name && name[0] != '\0')
                connman_network_set_name(network, name);
 
        connman_network_set_blob(network, "WiFi.SSID",
@@ -1381,30 +1985,32 @@ static void network_added(GSupplicantNetwork *supplicant_network)
                                calculate_strength(supplicant_network));
        connman_network_set_bool(network, "WiFi.WPS", wps);
 
-       if (wps == TRUE) {
+       if (wps) {
                /* Is AP advertizing for WPS association?
                 * If so, we decide to use WPS by default */
-               if (wps_ready == TRUE && wps_pbc == TRUE &&
-                                               wps_advertizing == TRUE)
-                       connman_network_set_bool(network, "WiFi.UseWPS", TRUE);
+               if (wps_ready && wps_pbc &&
+                                               wps_advertizing)
+                       connman_network_set_bool(network, "WiFi.UseWPS", true);
        }
 
        connman_network_set_frequency(network,
                        g_supplicant_network_get_frequency(supplicant_network));
 
-       connman_network_set_available(network, TRUE);
+       connman_network_set_available(network, true);
        connman_network_set_string(network, "WiFi.Mode", mode);
 
-       if (ssid != NULL)
+       if (ssid)
                connman_network_set_group(network, group);
 
-       if (wifi->hidden != NULL) {
-               if (wifi->hidden->ssid_len == ssid_len &&
-                               memcmp(wifi->hidden->ssid, ssid,
-                                               ssid_len) == 0) {
+       if (wifi->hidden && ssid) {
+               if (!g_strcmp0(wifi->hidden->security, security) &&
+                               wifi->hidden->ssid_len == ssid_len &&
+                               !memcmp(wifi->hidden->ssid, ssid, ssid_len)) {
                        connman_network_connect_hidden(network,
                                        wifi->hidden->identity,
-                                       wifi->hidden->passphrase);
+                                       wifi->hidden->passphrase,
+                                       wifi->hidden->user_data);
+                       wifi->hidden->user_data = NULL;
                        hidden_free(wifi->hidden);
                        wifi->hidden = NULL;
                }
@@ -1425,11 +2031,11 @@ static void network_removed(GSupplicantNetwork *network)
 
        DBG("name %s", name);
 
-       if (wifi == NULL)
+       if (!wifi)
                return;
 
        connman_network = connman_device_get_network(wifi->device, identifier);
-       if (connman_network == NULL)
+       if (!connman_network)
                return;
 
        wifi->networks = g_slist_remove(wifi->networks, connman_network);
@@ -1452,20 +2058,54 @@ static void network_changed(GSupplicantNetwork *network, const char *property)
 
        DBG("name %s", name);
 
-       if (wifi == NULL)
+       if (!wifi)
                return;
 
        connman_network = connman_device_get_network(wifi->device, identifier);
-       if (connman_network == NULL)
+       if (!connman_network)
                return;
 
-       if (g_str_equal(property, "Signal") == TRUE) {
+       if (g_str_equal(property, "Signal")) {
               connman_network_set_strength(connman_network,
                                        calculate_strength(network));
               connman_network_update(connman_network);
        }
 }
 
+static void peer_found(GSupplicantPeer *peer)
+{
+       struct connman_peer *connman_peer;
+       const char *identifier, *name;
+
+       identifier = g_supplicant_peer_get_identifier(peer);
+       name = g_supplicant_peer_get_name(peer);
+
+       DBG("ident: %s", identifier);
+
+       connman_peer = connman_peer_get(identifier);
+       if (connman_peer)
+               return;
+
+       connman_peer = connman_peer_create(identifier);
+       connman_peer_set_name(connman_peer, name);
+
+       connman_peer_register(connman_peer);
+}
+
+static void peer_lost(GSupplicantPeer *peer)
+{
+       struct connman_peer *connman_peer;
+       const char *identifier;
+
+       identifier = g_supplicant_peer_get_identifier(peer);
+
+       DBG("ident: %s", identifier);
+
+       connman_peer = connman_peer_get(identifier);
+       if (connman_peer)
+               connman_peer_unregister(connman_peer);
+}
+
 static void debug(const char *str)
 {
        if (getenv("CONNMAN_SUPPLICANT_DEBUG"))
@@ -1478,11 +2118,14 @@ static const GSupplicantCallbacks callbacks = {
        .interface_added        = interface_added,
        .interface_state        = interface_state,
        .interface_removed      = interface_removed,
+       .p2p_support            = p2p_support,
        .scan_started           = scan_started,
        .scan_finished          = scan_finished,
        .network_added          = network_added,
        .network_removed        = network_removed,
        .network_changed        = network_changed,
+       .peer_found             = peer_found,
+       .peer_lost              = peer_lost,
        .debug                  = debug,
 };
 
@@ -1511,7 +2154,7 @@ static GSupplicantSSID *ssid_ap_init(const char *ssid, const char *passphrase)
        GSupplicantSSID *ap;
 
        ap = g_try_malloc0(sizeof(GSupplicantSSID));
-       if (ap == NULL)
+       if (!ap)
                return NULL;
 
        ap->mode = G_SUPPLICANT_MODE_MASTER;
@@ -1520,7 +2163,7 @@ static GSupplicantSSID *ssid_ap_init(const char *ssid, const char *passphrase)
        ap->scan_ssid = 0;
        ap->freq = 2412;
 
-       if (passphrase == NULL || strlen(passphrase) == 0) {
+       if (!passphrase || strlen(passphrase) == 0) {
                ap->security = G_SUPPLICANT_SECURITY_NONE;
                ap->passphrase = NULL;
        } else {
@@ -1545,7 +2188,7 @@ static void ap_start_callback(int result, GSupplicantInterface *interface,
        if (result < 0) {
                connman_inet_remove_from_bridge(info->wifi->index,
                                                        info->wifi->bridge);
-               connman_technology_tethering_notify(info->technology, FALSE);
+               connman_technology_tethering_notify(info->technology, false);
        }
 
        g_free(info->ifname);
@@ -1564,9 +2207,10 @@ static void ap_create_callback(int result,
        if (result < 0) {
                connman_inet_remove_from_bridge(info->wifi->index,
                                                        info->wifi->bridge);
-               connman_technology_tethering_notify(info->technology, FALSE);
+               connman_technology_tethering_notify(info->technology, false);
 
                g_free(info->ifname);
+               g_free(info->ssid);
                g_free(info);
                return;
        }
@@ -1591,16 +2235,17 @@ static void sta_remove_callback(int result,
        DBG("ifname %s result %d ", info->ifname, result);
 
        if (result < 0) {
-               info->wifi->tethering = TRUE;
+               info->wifi->tethering = true;
 
                g_free(info->ifname);
+               g_free(info->ssid);
                g_free(info);
                return;
        }
 
        info->wifi->interface = NULL;
 
-       connman_technology_tethering_notify(info->technology, TRUE);
+       connman_technology_tethering_notify(info->technology, true);
 
        g_supplicant_interface_create(info->ifname, driver, info->wifi->bridge,
                                                ap_create_callback,
@@ -1609,7 +2254,7 @@ static void sta_remove_callback(int result,
 
 static int tech_set_tethering(struct connman_technology *technology,
                                const char *identifier, const char *passphrase,
-                               const char *bridge, connman_bool_t enabled)
+                               const char *bridge, bool enabled)
 {
        GList *list;
        GSupplicantInterface *interface;
@@ -1621,20 +2266,20 @@ static int tech_set_tethering(struct connman_technology *technology,
 
        DBG("");
 
-       if (enabled == FALSE) {
+       if (!enabled) {
                for (list = iface_list; list; list = list->next) {
                        wifi = list->data;
 
-                       if (wifi->tethering == TRUE) {
-                               wifi->tethering = FALSE;
+                       if (wifi->tethering) {
+                               wifi->tethering = false;
 
                                connman_inet_remove_from_bridge(wifi->index,
                                                                        bridge);
-                               wifi->bridged = FALSE;
+                               wifi->bridged = false;
                        }
                }
 
-               connman_technology_tethering_notify(technology, FALSE);
+               connman_technology_tethering_notify(technology, false);
 
                return 0;
        }
@@ -1644,7 +2289,7 @@ static int tech_set_tethering(struct connman_technology *technology,
 
                interface = wifi->interface;
 
-               if (interface == NULL)
+               if (!interface)
                        continue;
 
                ifname = g_supplicant_interface_get_ifname(wifi->interface);
@@ -1656,24 +2301,25 @@ static int tech_set_tethering(struct connman_technology *technology,
                }
 
                info = g_try_malloc0(sizeof(struct wifi_tethering_info));
-               if (info == NULL)
+               if (!info)
                        return -ENOMEM;
 
                info->wifi = wifi;
                info->technology = technology;
                info->wifi->bridge = bridge;
                info->ssid = ssid_ap_init(identifier, passphrase);
-               if (info->ssid == NULL) {
+               if (!info->ssid) {
                        g_free(info);
                        continue;
                }
                info->ifname = g_strdup(ifname);
-               if (info->ifname == NULL) {
+               if (!info->ifname) {
+                       g_free(info->ssid);
                        g_free(info);
                        continue;
                }
 
-               info->wifi->tethering = TRUE;
+               info->wifi->tethering = true;
 
                err = g_supplicant_interface_remove(interface,
                                                sta_remove_callback,
@@ -1685,21 +2331,22 @@ static int tech_set_tethering(struct connman_technology *technology,
        return -EOPNOTSUPP;
 }
 
-static void regdom_callback(void *user_data)
+static void regdom_callback(int result, const char *alpha2, void *user_data)
 {
-       char *alpha2 = user_data;
-
        DBG("");
 
-       if (wifi_technology == NULL)
+       if (!wifi_technology)
                return;
 
+       if (result != 0)
+               alpha2 = NULL;
+
        connman_technology_regdom_notify(wifi_technology, alpha2);
 }
 
 static int tech_set_regdom(struct connman_technology *technology, const char *alpha2)
 {
-       return g_supplicant_set_country(alpha2, regdom_callback, alpha2);
+       return g_supplicant_set_country(alpha2, regdom_callback, NULL);
 }
 
 static struct connman_technology_driver tech_driver = {