[PATCH 1/4] devices/wifi: Add NMDeviceIwd class to support IWD backend



This is very similar to NMDeviceWifi but simplified to remove the things
currently unsupported and with calls to nm_platform_wifi_* and
nm_supplicant_* replaced with IWD DBus API calls.  Only unsecured
infrastructure-mode networks are supported here.
---
 Makefile.am                      |    6 +
 configure.ac                     |   21 +
 src/devices/wifi/nm-device-iwd.c | 1891 ++++++++++++++++++++++++++++++++++++++
 src/devices/wifi/nm-device-iwd.h |   56 ++
 4 files changed, 1974 insertions(+)
 create mode 100644 src/devices/wifi/nm-device-iwd.c
 create mode 100644 src/devices/wifi/nm-device-iwd.h

diff --git a/Makefile.am b/Makefile.am
index 7bce597e9..7bde983a6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2641,6 +2641,12 @@ src_devices_wifi_libnm_device_plugin_wifi_la_SOURCES = \
        src/devices/wifi/nm-device-olpc-mesh.c \
        src/devices/wifi/nm-device-olpc-mesh.h
 
+if WITH_IWD
+src_devices_wifi_libnm_device_plugin_wifi_la_SOURCES += \
+       src/devices/wifi/nm-device-iwd.c \
+       src/devices/wifi/nm-device-iwd.h
+endif
+
 src_devices_wifi_libnm_device_plugin_wifi_la_CPPFLAGS = \
        -I$(srcdir)/src \
        -I$(builddir)/src \
diff --git a/configure.ac b/configure.ac
index 79791ab76..b99f05376 100644
--- a/configure.ac
+++ b/configure.ac
@@ -264,6 +264,26 @@ else
 fi
 
 dnl
+dnl Default to using wpa_supplicant but allow IWD as wifi backend
+dnl
+AC_ARG_WITH(iwd,
+            AS_HELP_STRING([--with-iwd=yes],
+                           [Support IWD as wifi-backend in addition to wpa_supplicant (experimental)]),
+            ac_with_iwd=$withval, ac_with_iwd="no")
+if test "$ac_with_iwd" != 'no'; then
+       ac_with_iwd='yes'
+fi
+if test x"$ac_with_iwd" = x"yes"; then
+       if test "$enable_wifi" != "yes"; then
+               AC_MSG_ERROR(Enabling IWD support and disabling Wi-Fi makes no sense)
+       fi
+       AC_DEFINE(WITH_IWD, 1, [Define to compile with the IWD wifi-backend])
+else
+       AC_DEFINE(WITH_IWD, 0, [Define to compile without the IWD wifi-backend])
+fi
+AM_CONDITIONAL(WITH_IWD, test x"${ac_with_iwd}" = x"yes")
+
+dnl
 dnl Check for newer VLAN flags
 dnl
 AC_MSG_CHECKING([Linux kernel VLAN_FLAG_LOOSE_BINDING enum value])
@@ -1326,6 +1346,7 @@ echo "  libteamdctl: $enable_teamdctl"
 echo "  libnm-glib: $with_libnm_glib"
 echo "  nmcli: $build_nmcli"
 echo "  nmtui: $build_nmtui"
+echo "  iwd: $ac_with_iwd"
 echo
 
 echo "Configuration plugins (main.plugins=${config_plugins_default})"
diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c
new file mode 100644
index 000000000..9a06604c2
--- /dev/null
+++ b/src/devices/wifi/nm-device-iwd.c
@@ -0,0 +1,1891 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2017 Intel Corporation
+ */
+
+#include "nm-default.h"
+
+#include "nm-device-iwd.h"
+
+#include <string.h>
+
+#include "nm-common-macros.h"
+#include "devices/nm-device.h"
+#include "devices/nm-device-private.h"
+#include "nm-utils.h"
+#include "nm-act-request.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-8021x.h"
+#include "settings/nm-settings-connection.h"
+#include "settings/nm-settings.h"
+#include "nm-wifi-utils.h"
+#include "nm-core-internal.h"
+#include "nm-config.h"
+#include "nm-iwd-manager.h"
+
+#include "introspection/org.freedesktop.NetworkManager.Device.Wireless.h"
+
+#include "devices/nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceIwd);
+
+static NM_CACHED_QUARK_FCN ("wireless-secrets-tries", wireless_secrets_tries_quark)
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceIwd,
+       PROP_MODE,
+       PROP_BITRATE,
+       PROP_ACCESS_POINTS,
+       PROP_ACTIVE_ACCESS_POINT,
+       PROP_CAPABILITIES,
+       PROP_SCANNING,
+);
+
+enum {
+       ACCESS_POINT_ADDED,
+       ACCESS_POINT_REMOVED,
+       SCANNING_PROHIBITED,
+
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct {
+       GDBusObject *   dbus_obj;
+       GDBusProxy *    dbus_proxy;
+       GHashTable *    aps;
+       GHashTable *    new_aps;
+       NMWifiAP *      current_ap;
+       GCancellable *  cancellable;
+       NMDeviceWifiCapabilities capabilities;
+       NMActRequestGetSecretsCallId wifi_secrets_id;
+       gboolean        enabled:1;
+       gboolean        can_scan:1;
+       gboolean        scanning:1;
+} NMDeviceIwdPrivate;
+
+struct _NMDeviceIwd
+{
+       NMDevice parent;
+       NMDeviceIwdPrivate _priv;
+};
+
+struct _NMDeviceIwdClass
+{
+       NMDeviceClass parent;
+
+       /* Signals */
+       gboolean (*scanning_prohibited) (NMDeviceIwd *device, gboolean periodic);
+};
+
+/*****************************************************************************/
+
+G_DEFINE_TYPE (NMDeviceIwd, nm_device_iwd, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_IWD_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDeviceIwd, NM_IS_DEVICE_IWD)
+
+/*****************************************************************************/
+
+static void
+_ap_dump (NMDeviceIwd *self,
+          NMLogLevel log_level,
+          const NMWifiAP *ap,
+          const char *prefix,
+          gint32 now_s)
+{
+       char buf[1024];
+
+       buf[0] = '\0';
+       _NMLOG (log_level, LOGD_WIFI_SCAN, "wifi-ap: %-7s %s",
+               prefix,
+               nm_wifi_ap_to_string (ap, buf, sizeof (buf), now_s));
+}
+
+/* Callers ensure we're not removing current_ap */
+static void
+ap_add_remove (NMDeviceIwd *self,
+               guint signum,
+               NMWifiAP *ap,
+               gboolean recheck_available_connections)
+{
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       nm_assert (NM_IN_SET (signum, ACCESS_POINT_ADDED, ACCESS_POINT_REMOVED));
+
+       if (signum == ACCESS_POINT_ADDED) {
+               g_hash_table_insert (priv->aps,
+                                    (gpointer) nm_exported_object_export ((NMExportedObject *) ap),
+                                    g_object_ref (ap));
+               _ap_dump (self, LOGL_DEBUG, ap, "added", 0);
+       } else
+               _ap_dump (self, LOGL_DEBUG, ap, "removed", 0);
+
+       g_signal_emit (self, signals[signum], 0, ap);
+
+       if (signum == ACCESS_POINT_REMOVED) {
+               g_hash_table_remove (priv->aps, nm_exported_object_get_path ((NMExportedObject *) ap));
+               nm_exported_object_unexport ((NMExportedObject *) ap);
+               g_object_unref (ap);
+       }
+
+       _notify (self, PROP_ACCESS_POINTS);
+
+       nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
+       if (recheck_available_connections)
+               nm_device_recheck_available_connections (NM_DEVICE (self));
+}
+
+static void
+set_current_ap (NMDeviceIwd *self, NMWifiAP *new_ap, gboolean recheck_available_connections)
+{
+       NMDeviceIwdPrivate *priv;
+       NMWifiAP *old_ap;
+
+       g_return_if_fail (NM_IS_DEVICE_IWD (self));
+
+       priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+       old_ap = priv->current_ap;
+
+       if (old_ap == new_ap)
+               return;
+
+       if (new_ap)
+               priv->current_ap = g_object_ref (new_ap);
+       else
+               priv->current_ap = NULL;
+
+       if (old_ap) {
+               if (nm_wifi_ap_get_fake (old_ap))
+                       ap_add_remove (self, ACCESS_POINT_REMOVED, old_ap, recheck_available_connections);
+               g_object_unref (old_ap);
+       }
+
+       _notify (self, PROP_ACTIVE_ACCESS_POINT);
+       _notify (self, PROP_MODE);
+}
+
+static gboolean
+update_ap_func (gpointer key, gpointer value, gpointer user_data)
+{
+       NMWifiAP *ap = value;
+       NMDeviceIwd *self = user_data;
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+       NMWifiAP *new_ap = NULL;
+
+       if (priv->new_aps)
+               new_ap = g_hash_table_lookup (priv->new_aps,
+                                             nm_wifi_ap_get_supplicant_path (ap));
+
+       if (new_ap) {
+               g_hash_table_steal (priv->new_aps,
+                                   nm_wifi_ap_get_supplicant_path (ap));
+
+               if (nm_wifi_ap_set_strength (ap, nm_wifi_ap_get_strength (new_ap)))
+                       _ap_dump (self, LOGL_TRACE, ap, "updated", 0);
+
+               g_object_unref (new_ap);
+               return FALSE;
+       }
+
+       if (ap == priv->current_ap)
+               /* Normally IWD will prevent the current AP from being
+                * removed from the list and set a low signal strength,
+                * but just making sure.
+                */
+               return FALSE;
+
+       _ap_dump (self, LOGL_DEBUG, ap, "removed", 0);
+
+       g_signal_emit (self, signals[ACCESS_POINT_REMOVED], 0, ap);
+
+       nm_exported_object_unexport ((NMExportedObject *) ap);
+       g_object_unref (ap);
+
+       return TRUE;
+}
+
+static void
+remove_all_aps (NMDeviceIwd *self)
+{
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       if (!g_hash_table_size (priv->aps))
+               return;
+
+       set_current_ap (self, NULL, FALSE);
+
+       g_hash_table_foreach_remove (priv->aps, update_ap_func, self);
+
+       _notify (self, PROP_ACCESS_POINTS);
+       nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
+       nm_device_recheck_available_connections (NM_DEVICE (self));
+}
+
+static GVariant *
+vardict_from_network_type (const gchar *type)
+{
+       GVariantBuilder builder;
+       const gchar *key_mgmt = "";
+       const gchar *pairwise = "ccmp";
+
+       if (!strcmp (type, "psk"))
+               key_mgmt = "wpa-psk";
+       else if (!strcmp (type, "8021x"))
+               key_mgmt = "wpa-eap";
+       else
+               return NULL;
+
+       g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+       g_variant_builder_add (&builder, "{sv}", "KeyMgmt",
+                              g_variant_new_strv (&key_mgmt, 1));
+       g_variant_builder_add (&builder, "{sv}", "Pairwise",
+                              g_variant_new_strv (&pairwise, 1));
+       g_variant_builder_add (&builder, "{sv}", "Group",
+                              g_variant_new_string ("ccmp"));
+       return g_variant_new ("a{sv}", &builder);
+}
+
+static void
+get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+       NMDeviceIwd *self = user_data;
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+       gs_free_error GError *error = NULL;
+       gs_unref_variant GVariant *variant = NULL;
+       GVariantIter *networks;
+       const gchar *path, *name, *type;
+       int16_t signal;
+       NMWifiAP *ap;
+       gboolean changed = FALSE;
+       GHashTableIter ap_iter;
+
+       variant = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
+                                             G_VARIANT_TYPE ("(a(osns))"),
+                                             &error);
+       if (!variant) {
+               _LOGE (LOGD_WIFI, "Device.GetOrderedNetworks failed: %s",
+                      error->message);
+               return;
+       }
+
+       priv->new_aps = g_hash_table_new (g_str_hash, g_str_equal);
+
+       g_variant_get (variant, "(a(osns))", &networks);
+
+       while (g_variant_iter_next (networks, "(&o&sn&s)", &path, &name, &signal, &type)) {
+               GVariantBuilder builder;
+               gs_unref_variant GVariant *props = NULL;
+               GVariant *rsn;
+               static uint32_t ap_id = 0;
+               uint8_t bssid[6];
+
+               /*
+                * What we get from IWD are networks, or ESSs, that may
+                * contain multiple APs, or BSSs, each.  We don't get
+                * information about any specific BSSs within an ESS but
+                * we can safely present each ESS as an individual BSS to
+                * NM, which will be seen as ESSs comprising a single BSS
+                * each.  NM won't be able to handle roaming but IWD already
+                * does that.  We fake the BSSIDs as they don't play any
+                * role either.
+                */
+               bssid[0] = 0x00;
+               bssid[1] = 0x01;
+               bssid[2] = 0x02;
+               bssid[3] = ap_id >> 16;
+               bssid[4] = ap_id >> 8;
+               bssid[5] = ap_id++;
+
+               /* WEP not supported */
+               if (!strcmp (type, "wep"))
+                       continue;
+
+               g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+               g_variant_builder_add (&builder, "{sv}", "BSSID",
+                                      g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, bssid, 6, 1));
+               g_variant_builder_add (&builder, "{sv}", "Mode",
+                                      g_variant_new_string ("infrastructure"));
+
+               rsn = vardict_from_network_type (type);
+               if (rsn)
+                       g_variant_builder_add (&builder, "{sv}", "RSN", rsn);
+
+               props = g_variant_new ("a{sv}", &builder);
+
+               ap = nm_wifi_ap_new_from_properties (path, props);
+               nm_wifi_ap_set_ssid (ap, (const guint8 *) name, strlen (name));
+               nm_wifi_ap_set_strength (ap, nm_wifi_utils_level_to_quality (signal / 100));
+               nm_wifi_ap_set_freq (ap, 2417);
+               nm_wifi_ap_set_max_bitrate (ap, 65000);
+               g_hash_table_insert (priv->new_aps,
+                                    (gpointer) nm_wifi_ap_get_supplicant_path (ap),
+                                    ap);
+       }
+
+       g_variant_iter_free (networks);
+
+       if (g_hash_table_foreach_remove (priv->aps, update_ap_func, self))
+               changed = TRUE;
+
+       g_hash_table_iter_init (&ap_iter, priv->new_aps);
+       while (g_hash_table_iter_next (&ap_iter, NULL, (gpointer) &ap)) {
+               ap_add_remove (self, ACCESS_POINT_ADDED, ap, FALSE);
+               changed = TRUE;
+       }
+
+       g_hash_table_destroy (priv->new_aps);
+       priv->new_aps = NULL;
+
+       if (changed) {
+               _notify (self, PROP_ACCESS_POINTS);
+               nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
+               nm_device_recheck_available_connections (NM_DEVICE (self));
+       }
+}
+
+static void
+update_aps (NMDeviceIwd *self)
+{
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       if (!priv->cancellable)
+               priv->cancellable = g_cancellable_new ();
+
+       g_dbus_proxy_call (priv->dbus_proxy, "GetOrderedNetworks",
+                          g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE,
+                          2000, priv->cancellable,
+                          get_ordered_networks_cb, self);
+}
+
+static void
+send_disconnect (NMDeviceIwd *self)
+{
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       g_dbus_proxy_call (priv->dbus_proxy, "Disconnect", g_variant_new ("()"),
+                          G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+}
+
+static void
+cleanup_association_attempt (NMDeviceIwd *self, gboolean disconnect)
+{
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       set_current_ap (self, NULL, TRUE);
+
+       if (disconnect && priv->dbus_obj)
+               send_disconnect (self);
+}
+
+static void
+deactivate (NMDevice *device)
+{
+       cleanup_association_attempt (NM_DEVICE_IWD (device), TRUE);
+}
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+       NMSettingConnection *s_con;
+       NMSettingWireless *s_wireless;
+       const char *mac;
+       const char * const *mac_blacklist;
+       int i;
+       const char *mode;
+       const char *perm_hw_addr;
+
+       if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->check_connection_compatible (device, connection))
+               return FALSE;
+
+       s_con = nm_connection_get_setting_connection (connection);
+       g_assert (s_con);
+
+       if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRELESS_SETTING_NAME))
+               return FALSE;
+
+       s_wireless = nm_connection_get_setting_wireless (connection);
+       if (!s_wireless)
+               return FALSE;
+
+       perm_hw_addr = nm_device_get_permanent_hw_address (device);
+       mac = nm_setting_wireless_get_mac_address (s_wireless);
+       if (perm_hw_addr) {
+               if (mac && !nm_utils_hwaddr_matches (mac, -1, perm_hw_addr, -1))
+                       return FALSE;
+
+               /* Check for MAC address blacklist */
+               mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
+               for (i = 0; mac_blacklist[i]; i++) {
+                       if (!nm_utils_hwaddr_valid (mac_blacklist[i], ETH_ALEN)) {
+                               g_warn_if_reached ();
+                               return FALSE;
+                       }
+
+                       if (nm_utils_hwaddr_matches (mac_blacklist[i], -1, perm_hw_addr, -1))
+                               return FALSE;
+               }
+       } else if (mac)
+               return FALSE;
+
+       mode = nm_setting_wireless_get_mode (s_wireless);
+       if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+static NMWifiAP *
+get_ap_by_path (NMDeviceIwd *self, const char *path)
+{
+       g_return_val_if_fail (path != NULL, NULL);
+       return g_hash_table_lookup (NM_DEVICE_IWD_GET_PRIVATE (self)->aps, path);
+
+}
+
+static NMWifiAP *
+find_first_compatible_ap (NMDeviceIwd *self,
+                          NMConnection *connection,
+                          gboolean allow_unstable_order)
+{
+       GHashTableIter iter;
+       NMWifiAP *ap;
+       NMWifiAP *cand_ap = NULL;
+
+       g_return_val_if_fail (connection != NULL, NULL);
+
+       g_hash_table_iter_init (&iter, NM_DEVICE_IWD_GET_PRIVATE (self)->aps);
+       while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) {
+               if (!nm_wifi_ap_check_compatible (ap, connection))
+                       continue;
+               if (allow_unstable_order)
+                       return ap;
+               if (!cand_ap || (nm_wifi_ap_get_id (cand_ap) < nm_wifi_ap_get_id (ap)))
+                       cand_ap = ap;
+       }
+       return cand_ap;
+}
+
+static gboolean
+check_connection_available (NMDevice *device,
+                            NMConnection *connection,
+                            NMDeviceCheckConAvailableFlags flags,
+                            const char *specific_object)
+{
+       NMSettingWireless *s_wifi;
+       const char *mode;
+
+       s_wifi = nm_connection_get_setting_wireless (connection);
+       g_return_val_if_fail (s_wifi, FALSE);
+
+       /* a connection that is available for a certain @specific_object, MUST
+        * also be available in general (without @specific_object). */
+
+       if (specific_object) {
+               NMWifiAP *ap;
+
+               ap = get_ap_by_path (NM_DEVICE_IWD (device), specific_object);
+               return ap ? nm_wifi_ap_check_compatible (ap, connection) : FALSE;
+       }
+
+       /* Only Infrastrusture mode at this time */
+       mode = nm_setting_wireless_get_mode (s_wifi);
+       if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
+               return FALSE;
+
+       /* Hidden SSIDs not supported yet */
+       if (nm_setting_wireless_get_hidden (s_wifi))
+               return FALSE;
+
+       if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP))
+               return TRUE;
+
+       /* Check at least one AP is compatible with this connection */
+       return !!find_first_compatible_ap (NM_DEVICE_IWD (device), connection, TRUE);
+}
+
+static gboolean
+is_manf_default_ssid (const GByteArray *ssid)
+{
+       int i;
+       /*
+        * List of manufacturer default SSIDs that are often unchanged by users.
+        *
+        * NOTE: this list should *not* contain networks that you would like to
+        * automatically roam to like "Starbucks" or "AT&T" or "T-Mobile HotSpot".
+        */
+       static const char *manf_defaults[] = {
+               "linksys",
+               "linksys-a",
+               "linksys-g",
+               "default",
+               "belkin54g",
+               "NETGEAR",
+               "o2DSL",
+               "WLAN",
+               "ALICE-WLAN",
+               "Speedport W 501V",
+               "TURBONETT",
+       };
+
+       for (i = 0; i < G_N_ELEMENTS (manf_defaults); i++) {
+               if (ssid->len == strlen (manf_defaults[i])) {
+                       if (memcmp (manf_defaults[i], ssid->data, ssid->len) == 0)
+                               return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+static gboolean
+complete_connection (NMDevice *device,
+                     NMConnection *connection,
+                     const char *specific_object,
+                     const GSList *existing_connections,
+                     GError **error)
+{
+       NMDeviceIwd *self = NM_DEVICE_IWD (device);
+       NMSettingWireless *s_wifi;
+       const char *setting_mac;
+       char *str_ssid = NULL;
+       NMWifiAP *ap;
+       const GByteArray *ssid = NULL;
+       GByteArray *tmp_ssid = NULL;
+       GBytes *setting_ssid = NULL;
+       const char *perm_hw_addr;
+       const char *mode;
+
+       s_wifi = nm_connection_get_setting_wireless (connection);
+
+       mode = s_wifi ? nm_setting_wireless_get_mode (s_wifi) : NULL;
+
+       if (s_wifi && !nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_INFRA)) {
+               g_set_error_literal (error,
+                                    NM_DEVICE_ERROR,
+                                    NM_DEVICE_ERROR_INVALID_CONNECTION,
+                                    "Only Infrastructure mode is supported.");
+               return FALSE;
+       }
+
+       if (!specific_object) {
+               /* If not given a specific object, we need at minimum an SSID */
+               if (!s_wifi) {
+                       g_set_error_literal (error,
+                                            NM_DEVICE_ERROR,
+                                            NM_DEVICE_ERROR_INVALID_CONNECTION,
+                                            "A 'wireless' setting is required if no AP path was given.");
+                       return FALSE;
+               }
+
+               setting_ssid = nm_setting_wireless_get_ssid (s_wifi);
+               if (!setting_ssid || g_bytes_get_size (setting_ssid) == 0) {
+                       g_set_error_literal (error,
+                                            NM_DEVICE_ERROR,
+                                            NM_DEVICE_ERROR_INVALID_CONNECTION,
+                                            "A 'wireless' setting with a valid SSID is required if no AP 
path was given.");
+                       return FALSE;
+               }
+
+               /* Find a compatible AP in the scan list */
+               ap = find_first_compatible_ap (self, connection, FALSE);
+               if (!ap) {
+                       g_set_error_literal (error,
+                                            NM_DEVICE_ERROR,
+                                            NM_DEVICE_ERROR_INVALID_CONNECTION,
+                                            "No compatible AP in the scan list and hidden SSIDs not 
supported.");
+                       return FALSE;
+               }
+       } else {
+               ap = get_ap_by_path (self, specific_object);
+               if (!ap) {
+                       g_set_error (error,
+                                    NM_DEVICE_ERROR,
+                                    NM_DEVICE_ERROR_SPECIFIC_OBJECT_NOT_FOUND,
+                                    "The access point %s was not in the scan list.",
+                                    specific_object);
+                       return FALSE;
+               }
+       }
+
+       /* Add a wifi setting if one doesn't exist yet */
+       if (!s_wifi) {
+               s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+               nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+       }
+
+       ssid = nm_wifi_ap_get_ssid (ap);
+
+       if (ssid == NULL) {
+               g_set_error_literal (error,
+                                    NM_DEVICE_ERROR,
+                                    NM_DEVICE_ERROR_INVALID_CONNECTION,
+                                    "A 'wireless' setting with a valid SSID is required.");
+               return FALSE;
+       }
+
+       if (!nm_wifi_ap_complete_connection (ap,
+                                            connection,
+                                            is_manf_default_ssid (ssid),
+                                            error)) {
+               if (tmp_ssid)
+                       g_byte_array_unref (tmp_ssid);
+               return FALSE;
+       }
+
+       str_ssid = nm_utils_ssid_to_utf8 (ssid->data, ssid->len);
+
+       nm_utils_complete_generic (nm_device_get_platform (device),
+                                  connection,
+                                  NM_SETTING_WIRELESS_SETTING_NAME,
+                                  existing_connections,
+                                  str_ssid,
+                                  str_ssid,
+                                  NULL,
+                                  TRUE);
+       g_free (str_ssid);
+       if (tmp_ssid)
+               g_byte_array_unref (tmp_ssid);
+
+       perm_hw_addr = nm_device_get_permanent_hw_address (device);
+       if (perm_hw_addr) {
+               setting_mac = nm_setting_wireless_get_mac_address (s_wifi);
+               if (setting_mac) {
+                       /* Make sure the setting MAC (if any) matches the device's permanent MAC */
+                       if (!nm_utils_hwaddr_matches (setting_mac, -1, perm_hw_addr, -1)) {
+                               g_set_error_literal (error,
+                                                    NM_CONNECTION_ERROR,
+                                                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                                                    _("connection does not match device"));
+                               g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, 
NM_SETTING_WIRELESS_MAC_ADDRESS);
+                               return FALSE;
+                       }
+               } else {
+                       guint8 tmp[ETH_ALEN];
+
+                       /* Lock the connection to this device by default if it uses a
+                        * permanent MAC address (ie not a 'locally administered' one)
+                        */
+                       nm_utils_hwaddr_aton (perm_hw_addr, tmp, ETH_ALEN);
+                       if (!(tmp[0] & 0x02)) {
+                               g_object_set (G_OBJECT (s_wifi),
+                                             NM_SETTING_WIRELESS_MAC_ADDRESS, perm_hw_addr,
+                                             NULL);
+                       }
+               }
+       }
+
+       return TRUE;
+}
+
+static gboolean
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
+{
+       NMDeviceIwd *self = NM_DEVICE_IWD (device);
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       return priv->enabled && priv->dbus_obj;
+}
+
+static gboolean
+can_auto_connect (NMDevice *device,
+                  NMConnection *connection,
+                  char **specific_object)
+{
+       NMDeviceIwd *self = NM_DEVICE_IWD (device);
+       NMSettingWireless *s_wifi;
+       NMWifiAP *ap;
+       const char *mode;
+       guint64 timestamp = 0;
+
+       nm_assert (!specific_object || !*specific_object);
+
+       if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->can_auto_connect (device, connection, NULL))
+               return FALSE;
+
+       s_wifi = nm_connection_get_setting_wireless (connection);
+       g_return_val_if_fail (s_wifi, FALSE);
+
+       /* Only Infrastrusture mode */
+       mode = nm_setting_wireless_get_mode (s_wifi);
+       if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
+               return FALSE;
+
+       /* Don't autoconnect to networks that have been tried at least once
+        * but haven't been successful, since these are often accidental choices
+        * from the menu and the user may not know the password.
+        */
+       if (nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), &timestamp)) {
+               if (timestamp == 0)
+                       return FALSE;
+       }
+
+       ap = find_first_compatible_ap (self, connection, FALSE);
+       if (ap) {
+               /* All good; connection is usable */
+               NM_SET_OUT (specific_object, g_strdup (nm_exported_object_get_path (NM_EXPORTED_OBJECT 
(ap))));
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static int
+ap_id_compare (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
+{
+       guint64 a_id = nm_wifi_ap_get_id (*((NMWifiAP **) p_a));
+       guint64 b_id = nm_wifi_ap_get_id (*((NMWifiAP **) p_b));
+
+       return a_id < b_id ? -1 : (a_id == b_id ? 0 : 1);
+}
+
+static NMWifiAP **
+ap_list_get_sorted (NMDeviceIwd *self, gboolean include_without_ssid)
+{
+       NMDeviceIwdPrivate *priv;
+       NMWifiAP **list;
+       GHashTableIter iter;
+       NMWifiAP *ap;
+       gsize i, n;
+
+       priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       n = g_hash_table_size (priv->aps);
+       list = g_new (NMWifiAP *, n + 1);
+
+       i = 0;
+       if (n > 0) {
+               g_hash_table_iter_init (&iter, priv->aps);
+               while (g_hash_table_iter_next (&iter, NULL, (gpointer) &ap)) {
+                       nm_assert (i < n);
+                       if (   include_without_ssid
+                           || nm_wifi_ap_get_ssid (ap))
+                               list[i++] = ap;
+               }
+               nm_assert (i <= n);
+               nm_assert (!include_without_ssid || i == n);
+
+               g_qsort_with_data (list,
+                                  i,
+                                  sizeof (gpointer),
+                                  ap_id_compare,
+                                  NULL);
+       }
+       list[i] = NULL;
+       return list;
+}
+
+static const char **
+ap_list_get_sorted_paths (NMDeviceIwd *self, gboolean include_without_ssid)
+{
+       gpointer *list;
+       gsize i, j;
+
+       list = (gpointer *) ap_list_get_sorted (self, include_without_ssid);
+       for (i = 0, j = 0; list[i]; i++) {
+               NMWifiAP *ap = list[i];
+               const char *path;
+
+               /* update @list inplace to hold instead the export-path. */
+               path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (ap));
+               nm_assert (path);
+               list[j++] = (gpointer) path;
+       }
+       return (const char **) list;
+}
+
+static void
+impl_device_iwd_get_access_points (NMDeviceIwd *self,
+                                    GDBusMethodInvocation *context)
+{
+       gs_free const char **list = NULL;
+       GVariant *v;
+
+       list = ap_list_get_sorted_paths (self, FALSE);
+       v = g_variant_new_objv (list, -1);
+       g_dbus_method_invocation_return_value (context, g_variant_new_tuple (&v, 1));
+}
+
+static void
+impl_device_iwd_get_all_access_points (NMDeviceIwd *self,
+                                        GDBusMethodInvocation *context)
+{
+       gs_free const char **list = NULL;
+       GVariant *v;
+
+       list = ap_list_get_sorted_paths (self, TRUE);
+       v = g_variant_new_objv (list, -1);
+       g_dbus_method_invocation_return_value (context, g_variant_new_tuple (&v, 1));
+}
+
+static gboolean
+check_scanning_prohibited (NMDeviceIwd *self, gboolean periodic)
+{
+       gboolean prohibited = FALSE;
+
+       g_signal_emit (self, signals[SCANNING_PROHIBITED], 0, periodic, &prohibited);
+       return prohibited;
+}
+
+static void
+dbus_request_scan_cb (NMDevice *device,
+                      GDBusMethodInvocation *context,
+                      NMAuthSubject *subject,
+                      GError *error,
+                      gpointer user_data)
+{
+       NMDeviceIwd *self = NM_DEVICE_IWD (device);
+       NMDeviceIwdPrivate *priv;
+       gs_unref_variant GVariant *scan_options = user_data;
+       gs_unref_ptrarray GPtrArray *ssids = NULL;
+
+       if (error) {
+               g_dbus_method_invocation_return_gerror (context, error);
+               return;
+       }
+
+       if (check_scanning_prohibited (self, FALSE)) {
+               g_dbus_method_invocation_return_error_literal (context,
+                                                              NM_DEVICE_ERROR,
+                                                              NM_DEVICE_ERROR_NOT_ALLOWED,
+                                                              "Scanning not allowed at this time");
+               return;
+       }
+
+       priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       if (   !priv->enabled
+           || !priv->dbus_obj
+           || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
+           || nm_device_is_activating (device)) {
+               g_dbus_method_invocation_return_error_literal (context,
+                                                              NM_DEVICE_ERROR,
+                                                              NM_DEVICE_ERROR_NOT_ALLOWED,
+                                                              "Scanning not allowed while unavailable");
+               return;
+       }
+
+       if (scan_options) {
+               gs_unref_variant GVariant *val = g_variant_lookup_value (scan_options, "ssids", NULL);
+
+               if (val) {
+                       g_dbus_method_invocation_return_error_literal (context,
+                                                                      NM_DEVICE_ERROR,
+                                                                      NM_DEVICE_ERROR_NOT_ALLOWED,
+                                                                      "'ssid' scan option not supported");
+                       return;
+               }
+       }
+
+       g_dbus_proxy_call (priv->dbus_proxy, "Scan", g_variant_new ("()"),
+                          G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+       g_dbus_method_invocation_return_value (context, NULL);
+}
+
+static void
+impl_device_iwd_request_scan (NMDeviceIwd *self,
+                               GDBusMethodInvocation *context,
+                               GVariant *options)
+{
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+       NMDevice *device = NM_DEVICE (self);
+
+       if (   !priv->enabled
+           || !priv->dbus_obj
+           || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
+           || nm_device_is_activating (device)) {
+               g_dbus_method_invocation_return_error_literal (context,
+                                                              NM_DEVICE_ERROR,
+                                                              NM_DEVICE_ERROR_NOT_ALLOWED,
+                                                              "Scanning not allowed while unavailable");
+               return;
+       }
+
+       /* Ask the manager to authenticate this request for us */
+       g_signal_emit_by_name (device,
+                              NM_DEVICE_AUTH_REQUEST,
+                              context,
+                              NULL,
+                              NM_AUTH_PERMISSION_NETWORK_CONTROL,
+                              TRUE,
+                              dbus_request_scan_cb,
+                              options ? g_variant_ref (options) : NULL);
+}
+
+static gboolean
+scanning_prohibited (NMDeviceIwd *self, gboolean periodic)
+{
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       g_return_val_if_fail (priv->dbus_obj != NULL, TRUE);
+
+       switch (nm_device_get_state (NM_DEVICE (self))) {
+       case NM_DEVICE_STATE_UNKNOWN:
+       case NM_DEVICE_STATE_UNMANAGED:
+       case NM_DEVICE_STATE_UNAVAILABLE:
+       case NM_DEVICE_STATE_PREPARE:
+       case NM_DEVICE_STATE_CONFIG:
+       case NM_DEVICE_STATE_NEED_AUTH:
+       case NM_DEVICE_STATE_IP_CONFIG:
+       case NM_DEVICE_STATE_IP_CHECK:
+       case NM_DEVICE_STATE_SECONDARIES:
+       case NM_DEVICE_STATE_DEACTIVATING:
+               /* Prohibit scans when unusable or activating */
+               return TRUE;
+       case NM_DEVICE_STATE_DISCONNECTED:
+       case NM_DEVICE_STATE_FAILED:
+               /* Can always scan when disconnected */
+               return FALSE;
+       case NM_DEVICE_STATE_ACTIVATED:
+               break;
+       }
+
+       /* Prohibit scans if IWD is busy */
+       return !priv->can_scan;
+}
+
+static void
+wifi_secrets_cb (NMActRequest *req,
+                 NMActRequestGetSecretsCallId call_id,
+                 NMSettingsConnection *connection,
+                 GError *error,
+                 gpointer user_data)
+{
+       NMDevice *device = user_data;
+       NMDeviceIwd *self = user_data;
+       NMDeviceIwdPrivate *priv;
+
+       g_return_if_fail (NM_IS_DEVICE_IWD (self));
+       g_return_if_fail (NM_IS_ACT_REQUEST (req));
+
+       priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       g_return_if_fail (priv->wifi_secrets_id == call_id);
+
+       priv->wifi_secrets_id = NULL;
+
+       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+               return;
+
+       g_return_if_fail (req == nm_device_get_act_request (device));
+       g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
+       g_return_if_fail (nm_act_request_get_settings_connection (req) == connection);
+
+       if (error) {
+               _LOGW (LOGD_WIFI, "%s", error->message);
+
+               nm_device_state_changed (device,
+                                        NM_DEVICE_STATE_FAILED,
+                                        NM_DEVICE_STATE_REASON_NO_SECRETS);
+       } else
+               nm_device_activate_schedule_stage1_device_prepare (device);
+}
+
+static void
+wifi_secrets_cancel (NMDeviceIwd *self)
+{
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       if (priv->wifi_secrets_id)
+               nm_act_request_cancel_secrets (NULL, priv->wifi_secrets_id);
+       nm_assert (!priv->wifi_secrets_id);
+}
+
+static void
+wifi_secrets_get_secrets (NMDeviceIwd *self,
+                          const char *setting_name,
+                          NMSecretAgentGetSecretsFlags flags)
+{
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+       NMActRequest *req;
+
+       wifi_secrets_cancel (self);
+
+       req = nm_device_get_act_request (NM_DEVICE (self));
+       g_return_if_fail (NM_IS_ACT_REQUEST (req));
+
+       priv->wifi_secrets_id = nm_act_request_get_secrets (req,
+                                                           TRUE,
+                                                           setting_name,
+                                                           flags,
+                                                           NULL,
+                                                           wifi_secrets_cb,
+                                                           self);
+       g_return_if_fail (priv->wifi_secrets_id);
+}
+
+static gboolean
+need_new_8021x_secrets (NMDeviceIwd *self,
+                        const char **setting_name)
+{
+       NMSetting8021x *s_8021x;
+       NMSettingWirelessSecurity *s_wsec;
+       NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+       NMConnection *connection;
+
+       g_assert (setting_name != NULL);
+
+       connection = nm_device_get_applied_connection (NM_DEVICE (self));
+       g_return_val_if_fail (connection != NULL, FALSE);
+
+       /* If it's an 802.1x or LEAP connection with "always ask"/unsaved secrets
+        * then we need to ask again because it might be an OTP token and the PIN
+        * may have changed.
+        */
+
+       s_8021x = nm_connection_get_setting_802_1x (connection);
+       if (s_8021x) {
+               if (!nm_setting_get_secret_flags (NM_SETTING (s_8021x),
+                                                 NM_SETTING_802_1X_PASSWORD,
+                                                 &secret_flags,
+                                                 NULL))
+                       g_assert_not_reached ();
+               if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
+                       *setting_name = NM_SETTING_802_1X_SETTING_NAME;
+               return *setting_name ? TRUE : FALSE;
+       }
+
+       s_wsec = nm_connection_get_setting_wireless_security (connection);
+       if (s_wsec) {
+               if (!nm_setting_get_secret_flags (NM_SETTING (s_wsec),
+                                                 NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
+                                                 &secret_flags,
+                                                 NULL))
+                       g_assert_not_reached ();
+               if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
+                       *setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
+               return *setting_name ? TRUE : FALSE;
+       }
+
+       /* Not a LEAP or 802.1x connection */
+       return FALSE;
+}
+
+static gboolean
+need_new_wpa_psk (NMDeviceIwd *self,
+                  const char **setting_name)
+{
+       NMSettingWirelessSecurity *s_wsec;
+       NMConnection *connection;
+       const char *key_mgmt = NULL;
+
+       g_assert (setting_name != NULL);
+
+       connection = nm_device_get_applied_connection (NM_DEVICE (self));
+       g_return_val_if_fail (connection != NULL, FALSE);
+
+       s_wsec = nm_connection_get_setting_wireless_security (connection);
+       if (s_wsec)
+               key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+
+       if (g_strcmp0 (key_mgmt, "wpa-psk") == 0) {
+               /* We don't have any data from IWD about the disconnect
+                * reason or association state when the disconnect happened
+                * so just assume it was a bad password.
+                */
+               *setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
+               return TRUE;
+       }
+
+       /* Not a WPA-PSK connection */
+       return FALSE;
+}
+
+static gboolean
+handle_8021x_or_psk_auth_fail (NMDeviceIwd *self)
+{
+       NMDevice *device = NM_DEVICE (self);
+       NMActRequest *req;
+       const char *setting_name = NULL;
+       gboolean handled = FALSE;
+
+       req = nm_device_get_act_request (NM_DEVICE (self));
+       g_return_val_if_fail (req != NULL, FALSE);
+
+       if (   need_new_8021x_secrets (self, &setting_name)
+           || need_new_wpa_psk (self, &setting_name)) {
+               nm_act_request_clear_secrets (req);
+
+               _LOGI (LOGD_DEVICE | LOGD_WIFI,
+                      "Activation: (wifi) disconnected during association, asking for new key");
+
+               cleanup_association_attempt (self, FALSE);
+               nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH,
+                                        NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
+               wifi_secrets_get_secrets (self,
+                                         setting_name,
+                                         NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
+                                           | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW);
+               handled = TRUE;
+       }
+
+       return handled;
+}
+
+static void
+network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+       NMDeviceIwd *self = user_data;
+       NMDevice *device = NM_DEVICE (self);
+       gs_free_error GError *error = NULL;
+       gs_unref_variant GVariant *variant = NULL;
+       NMConnection *connection;
+       NMSettingWireless *s_wifi;
+       GBytes *ssid;
+
+       if (!_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res,
+                                        G_VARIANT_TYPE ("()"),
+                                        &error)) {
+               gs_free gchar *dbus_error = NULL;
+
+               /* Connection failed; radio problems or if the network wasn't
+                * open, the passwords or certificates may be wrong.
+                */
+
+               _LOGE (LOGD_DEVICE | LOGD_WIFI,
+                      "Activation: (wifi) Network.Connect failed: %s",
+                      error->message);
+
+               connection = nm_device_get_applied_connection (NM_DEVICE (self));
+               if (!connection || nm_connection_get_setting_wireless_security (connection))
+                       goto failed;
+
+               if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR))
+                       dbus_error = g_dbus_error_get_remote_error (error);
+
+               /* If secrets were wrong, we'd be getting a net.connman.iwd.Failed */
+               if (nm_streq0 (dbus_error, "net.connman.iwd.Failed")) {
+                       if (handle_8021x_or_psk_auth_fail (self)) {
+                               _LOGW (LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) asking for new secrets");
+                       } else {
+                               cleanup_association_attempt (self, FALSE);
+                               nm_device_state_changed (device, NM_DEVICE_STATE_FAILED,
+                                                        NM_DEVICE_STATE_REASON_NO_SECRETS);
+                       }
+               } else if (   !nm_utils_error_is_cancelled (error, TRUE)
+                          && nm_device_is_activating (device))
+                       goto failed;
+
+               /* Call Disconnect to make sure IWD's autoconnect is disabled */
+               cleanup_association_attempt (self, TRUE);
+
+               return;
+       }
+
+       nm_assert (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG);
+
+       connection = nm_device_get_applied_connection (device);
+       if (!connection)
+               goto failed;
+
+       s_wifi = nm_connection_get_setting_wireless (connection);
+       if (!s_wifi)
+               goto failed;
+
+       ssid = nm_setting_wireless_get_ssid (s_wifi);
+       if (!ssid)
+               goto failed;
+
+       _LOGI (LOGD_DEVICE | LOGD_WIFI,
+              "Activation: (wifi) Stage 2 of 5 (Device Configure) successful.  Connected to '%s'.",
+              ssid ? nm_utils_escape_ssid (g_bytes_get_data (ssid, NULL),
+                                           g_bytes_get_size (ssid)) : "(none)");
+       nm_device_activate_schedule_stage3_ip_config_start (device);
+       return;
+
+failed:
+       cleanup_association_attempt (self, FALSE);
+       nm_device_queue_state (device, NM_DEVICE_STATE_FAILED,
+                              NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+}
+
+static gboolean
+handle_auth_or_fail (NMDeviceIwd *self,
+                     NMActRequest *req,
+                     gboolean new_secrets)
+{
+       const char *setting_name;
+       guint32 tries;
+       NMConnection *applied_connection;
+       NMSecretAgentGetSecretsFlags get_secret_flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+
+       g_return_val_if_fail (NM_IS_DEVICE_IWD (self), FALSE);
+
+       applied_connection = nm_act_request_get_applied_connection (req);
+
+       tries = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (applied_connection), 
wireless_secrets_tries_quark ()));
+       if (tries > 3)
+               return FALSE;
+
+       nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
+
+       nm_act_request_clear_secrets (req);
+       setting_name = nm_connection_need_secrets (applied_connection, NULL);
+       if (!setting_name) {
+               _LOGW (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
+               return FALSE;
+       }
+
+       if (new_secrets)
+               get_secret_flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
+       wifi_secrets_get_secrets (self, setting_name, get_secret_flags);
+       g_object_set_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark (), GUINT_TO_POINTER 
(++tries));
+       return TRUE;
+}
+
+/*****************************************************************************/
+
+static NMActStageReturn
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
+{
+       NMDeviceIwd *self = NM_DEVICE_IWD (device);
+       NMActStageReturn ret;
+       NMWifiAP *ap = NULL;
+       NMActRequest *req;
+       NMConnection *connection;
+       NMSettingWireless *s_wireless;
+       const char *ap_path;
+
+       ret = NM_DEVICE_CLASS (nm_device_iwd_parent_class)->act_stage1_prepare (device, out_failure_reason);
+       if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+               return ret;
+
+       req = nm_device_get_act_request (NM_DEVICE (self));
+       g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
+
+       connection = nm_act_request_get_applied_connection (req);
+       g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
+
+       s_wireless = nm_connection_get_setting_wireless (connection);
+       g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE);
+
+       ap_path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req));
+       ap = ap_path ? get_ap_by_path (self, ap_path) : NULL;
+       if (!ap) {
+               ap = find_first_compatible_ap (self, connection, FALSE);
+
+               /* TODO: assuming hidden networks aren't supported do we need
+                * to consider the case of APs that are not in the scan list
+                * yet, for which nm-device-wifi.c creates the temporary fake
+                * AP object?
+                */
+
+               nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
+                                                         nm_exported_object_get_path (NM_EXPORTED_OBJECT 
(ap)));
+       }
+
+       set_current_ap (self, ap, FALSE);
+       return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+static NMActStageReturn
+act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
+{
+       NMDeviceIwd *self = NM_DEVICE_IWD (device);
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+       NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+       NMActRequest *req;
+       NMWifiAP *ap;
+       NMConnection *connection;
+       const char *setting_name;
+       NMSettingWireless *s_wireless;
+       GError *error = NULL;
+       GDBusProxy *network_proxy;
+
+       req = nm_device_get_act_request (device);
+       g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
+
+       ap = priv->current_ap;
+       if (!ap) {
+               NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+               goto out;
+       }
+
+       connection = nm_act_request_get_applied_connection (req);
+       g_assert (connection);
+
+       s_wireless = nm_connection_get_setting_wireless (connection);
+       g_assert (s_wireless);
+
+       /* If we need secrets, get them */
+       setting_name = nm_connection_need_secrets (connection, NULL);
+       if (setting_name) {
+               _LOGI (LOGD_DEVICE | LOGD_WIFI,
+                      "Activation: (wifi) access point '%s' has security, but secrets are required.",
+                      nm_connection_get_id (connection));
+
+               if (handle_auth_or_fail (self, req, FALSE))
+                       ret = NM_ACT_STAGE_RETURN_POSTPONE;
+               else {
+                       NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS);
+                       ret = NM_ACT_STAGE_RETURN_FAILURE;
+               }
+               goto out;
+       }
+
+       /* Have secrets or no secrets required */
+       if (nm_connection_get_setting_wireless_security (connection)) {
+               _LOGI (LOGD_DEVICE | LOGD_WIFI,
+                      "Activation: (wifi) connection '%s' has security, and secrets exist.  No new secrets 
needed.",
+                      nm_connection_get_id (connection));
+       } else {
+               _LOGI (LOGD_DEVICE | LOGD_WIFI,
+                      "Activation: (wifi) connection '%s' requires no security.  No secrets needed.",
+                      nm_connection_get_id (connection));
+       }
+
+       /* Locate the IWD Network object */
+       network_proxy = g_dbus_proxy_new_for_bus_sync (IWD_BUS_TYPE,
+                                                      G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+                                                      G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+                                                      NULL,
+                                                      IWD_SERVICE,
+                                                      nm_wifi_ap_get_supplicant_path (ap),
+                                                      IWD_NETWORK_INTERFACE,
+                                                      NULL, &error);
+       if (!network_proxy) {
+               return FALSE;
+
+               _LOGE (LOGD_DEVICE | LOGD_WIFI,
+                      "Activation: (wifi) could not get Network interface proxy for %s: %s",
+                      nm_wifi_ap_get_supplicant_path (ap),
+                      error->message);
+               g_clear_error (&error);
+               NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+               goto out;
+       }
+
+       if (!priv->cancellable)
+               priv->cancellable = g_cancellable_new ();
+
+       /* Call Network.Connect.  No timeout because IWD already handles
+        * timeouts.
+        */
+       g_dbus_proxy_call (network_proxy, "Connect",
+                          g_variant_new ("()"),
+                          G_DBUS_CALL_FLAGS_NONE, -1,
+                          priv->cancellable, network_connect_cb, self);
+
+       g_object_unref (network_proxy);
+
+       /* We'll get stage3 started when the supplicant connects */
+       ret = NM_ACT_STAGE_RETURN_POSTPONE;
+
+out:
+       if (ret == NM_ACT_STAGE_RETURN_FAILURE)
+               cleanup_association_attempt (self, FALSE);
+
+       return ret;
+}
+
+static guint32
+get_configured_mtu (NMDevice *device, gboolean *out_is_user_config)
+{
+       NMSettingWireless *setting;
+       gint64 mtu_default;
+       guint32 mtu;
+
+       nm_assert (NM_IS_DEVICE (device));
+       nm_assert (out_is_user_config);
+
+       setting = NM_SETTING_WIRELESS (nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS));
+       if (!setting)
+               g_return_val_if_reached (0);
+
+       mtu = nm_setting_wireless_get_mtu (setting);
+       if (mtu == 0) {
+               mtu_default = nm_device_get_configured_mtu_from_connection_default (device, "wifi.mtu");
+               if (mtu_default >= 0) {
+                       *out_is_user_config = TRUE;
+                       return (guint32) mtu_default;
+               }
+       }
+       *out_is_user_config = (mtu != 0);
+       return mtu;
+}
+
+static void
+activation_success_handler (NMDevice *device)
+{
+       NMDeviceIwd *self = NM_DEVICE_IWD (device);
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+       NMActRequest *req;
+       NMConnection *applied_connection;
+
+       req = nm_device_get_act_request (device);
+       g_assert (req);
+
+       applied_connection = nm_act_request_get_applied_connection (req);
+
+       /* Clear wireless secrets tries on success */
+       g_object_set_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark (), NULL);
+
+       /* There should always be a current AP */
+       g_warn_if_fail (priv->current_ap);
+}
+
+static void
+activation_failure_handler (NMDevice *device)
+{
+       NMConnection *applied_connection;
+
+       applied_connection = nm_device_get_applied_connection (device);
+       g_assert (applied_connection);
+
+       /* Clear wireless secrets tries on failure */
+       g_object_set_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark (), NULL);
+}
+
+static void
+device_state_changed (NMDevice *device,
+                      NMDeviceState new_state,
+                      NMDeviceState old_state,
+                      NMDeviceStateReason reason)
+{
+       NMDeviceIwd *self = NM_DEVICE_IWD (device);
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       if (new_state <= NM_DEVICE_STATE_UNAVAILABLE)
+               remove_all_aps (self);
+       else if (old_state <= NM_DEVICE_STATE_UNAVAILABLE)
+               update_aps (self);
+
+       switch (new_state) {
+       case NM_DEVICE_STATE_UNMANAGED:
+               break;
+       case NM_DEVICE_STATE_UNAVAILABLE:
+               /*
+                * If the device is enabled and the IWD manager is ready,
+                * transition to DISCONNECTED because the device is now
+                * ready to use.
+                */
+               if (priv->enabled && priv->dbus_obj) {
+                       nm_device_queue_recheck_available (device,
+                                                          NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
+                                                          NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+               }
+               break;
+       case NM_DEVICE_STATE_NEED_AUTH:
+               send_disconnect (self);
+               break;
+       case NM_DEVICE_STATE_IP_CHECK:
+               break;
+       case NM_DEVICE_STATE_ACTIVATED:
+               activation_success_handler (device);
+               break;
+       case NM_DEVICE_STATE_FAILED:
+               activation_failure_handler (device);
+               break;
+       case NM_DEVICE_STATE_DISCONNECTED:
+               break;
+       default:
+               break;
+       }
+}
+
+static gboolean
+get_enabled (NMDevice *device)
+{
+       return NM_DEVICE_IWD_GET_PRIVATE ((NMDeviceIwd *) device)->enabled;
+}
+
+static void
+set_enabled (NMDevice *device, gboolean enabled)
+{
+       NMDeviceIwd *self = NM_DEVICE_IWD (device);
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+       NMDeviceState state;
+
+       enabled = !!enabled;
+
+       if (priv->enabled == enabled)
+               return;
+
+       priv->enabled = enabled;
+
+       _LOGD (LOGD_WIFI, "device now %s", enabled ? "enabled" : "disabled");
+
+       state = nm_device_get_state (NM_DEVICE (self));
+       if (state < NM_DEVICE_STATE_UNAVAILABLE) {
+               _LOGD (LOGD_WIFI, "(%s): device blocked by UNMANAGED state",
+                      enabled ? "enable" : "disable");
+               return;
+       }
+
+       if (enabled) {
+               if (state != NM_DEVICE_STATE_UNAVAILABLE)
+                       _LOGW (LOGD_CORE, "not in expected unavailable state!");
+
+               if (priv->dbus_obj)
+                       nm_device_queue_recheck_available (NM_DEVICE (self),
+                                                          NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE,
+                                                          NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+       } else {
+               nm_device_state_changed (NM_DEVICE (self),
+                                        NM_DEVICE_STATE_UNAVAILABLE,
+                                        NM_DEVICE_STATE_REASON_NONE);
+       }
+}
+
+static gboolean
+can_reapply_change (NMDevice *device,
+                    const char *setting_name,
+                    NMSetting *s_old,
+                    NMSetting *s_new,
+                    GHashTable *diffs,
+                    GError **error)
+{
+       NMDeviceClass *device_class;
+
+       /* Only handle wireless setting here, delegate other settings to parent class */
+       if (nm_streq (setting_name, NM_SETTING_WIRELESS_SETTING_NAME)) {
+               return nm_device_hash_check_invalid_keys (diffs,
+                                                         NM_SETTING_WIRELESS_SETTING_NAME,
+                                                         error,
+                                                         NM_SETTING_WIRELESS_MTU); /* reapplied with IP 
config */
+       }
+
+       device_class = NM_DEVICE_CLASS (nm_device_iwd_parent_class);
+       return device_class->can_reapply_change (device,
+                                                setting_name,
+                                                s_old,
+                                                s_new,
+                                                diffs,
+                                                error);
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+       NMDeviceIwd *self = NM_DEVICE_IWD (object);
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+       gsize i;
+       char **list;
+
+       switch (prop_id) {
+       case PROP_MODE:
+               if (priv->current_ap)
+                       g_value_set_uint (value, NM_802_11_MODE_INFRA);
+               else
+                       g_value_set_uint (value, NM_802_11_MODE_UNKNOWN);
+               break;
+       case PROP_BITRATE:
+               g_value_set_uint (value, 65000);
+               break;
+       case PROP_CAPABILITIES:
+               g_value_set_uint (value, priv->capabilities);
+               break;
+       case PROP_ACCESS_POINTS:
+               list = (char **) ap_list_get_sorted_paths (self, TRUE);
+               for (i = 0; list[i]; i++)
+                       list[i] = g_strdup (list[i]);
+               g_value_take_boxed (value, list);
+               break;
+       case PROP_ACTIVE_ACCESS_POINT:
+               nm_utils_g_value_set_object_path (value, priv->current_ap);
+               break;
+       case PROP_SCANNING:
+               g_value_set_boolean (value, priv->scanning);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+       NMDeviceIwd *device = NM_DEVICE_IWD (object);
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (device);
+
+       switch (prop_id) {
+       case PROP_CAPABILITIES:
+               /* construct-only */
+               priv->capabilities = g_value_get_uint (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+/*****************************************************************************/
+
+static void
+state_changed (NMDeviceIwd *self, const gchar *new_state)
+{
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+       NMDevice *device = NM_DEVICE (self);
+
+       _LOGI (LOGD_DEVICE | LOGD_WIFI, "new IWD device state is %s", new_state);
+
+       /* Don't allow scanning while connecting, disconnecting or roaming */
+       priv->can_scan = NM_IN_STRSET (new_state, "connected", "disconnected");
+
+       if (NM_IN_STRSET (new_state, "connecting", "connected", "roaming")) {
+               /* If we're activating, do nothing, the confirmation of
+                * a connection success is handled in the Device.Connect
+                * method return callback.  Otherwise IWD must have connected
+                * without Network Manager's will so for simplicity force a
+                * disconnect.
+                */
+               if (   nm_device_is_activating (device)
+                   || nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED)
+                       return;
+
+               _LOGW (LOGD_DEVICE | LOGD_WIFI,
+                      "Unsolicited connection success, asking IWD to disconnect");
+               send_disconnect (self);
+
+               return;
+       } else if (NM_IN_STRSET (new_state, "disconnecting", "disconnected")) {
+               if (   !nm_device_is_activating (device)
+                   && nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED)
+                       return;
+
+               /* Call Disconnect on the IWD device object to make sure it
+                * disables its own autoconnect.
+                *
+                * Note we could instead call net.connman.iwd.KnownNetworks.ForgetNetwork
+                * and leave the device in autoconnect.  This way if NetworkManager
+                * changes any settings for this connection, they'd be taken into
+                * account on the next connection attempt.  But both methods are
+                * a hack, we'll perhaps need an IWD API to "connect once" without
+                * storing anything.
+                */
+               send_disconnect (self);
+
+               nm_device_state_changed (device,
+                                        NM_DEVICE_STATE_FAILED,
+                                        NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
+
+               return;
+       }
+
+       _LOGE (LOGD_WIFI, "State %s unknown", new_state);
+}
+
+static void
+scanning_changed (NMDeviceIwd *self, gboolean new_scanning)
+{
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       if (new_scanning == priv->scanning)
+               return;
+
+       priv->scanning = new_scanning;
+
+       _notify (self, PROP_SCANNING);
+
+       if (!priv->scanning)
+               update_aps (self);
+}
+
+static void
+properties_changed (GDBusProxy *proxy, GVariant *changed_properties,
+                    GStrv invalidate_properties, gpointer user_data)
+{
+       NMDeviceIwd *self = user_data;
+       GVariantIter *iter;
+       const gchar *key;
+       GVariant *value;
+
+       g_variant_get (changed_properties, "a{sv}", &iter);
+       while (g_variant_iter_next (iter, "{&sv}", &key, &value)) {
+               if (!strcmp (key, "State"))
+                       state_changed (self, g_variant_get_string (value, NULL));
+
+               if (!strcmp (key, "Scanning"))
+                       scanning_changed (self, g_variant_get_boolean (value));
+
+               g_variant_unref (value);
+       }
+
+       g_variant_iter_free (iter);
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_iwd_init (NMDeviceIwd *self)
+{
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       priv->aps = g_hash_table_new (g_str_hash, g_str_equal);
+
+       /* Make sure the manager is running */
+       (void) nm_iwd_manager_get ();
+}
+
+NMDevice *
+nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities)
+{
+       return g_object_new (NM_TYPE_DEVICE_IWD,
+                            NM_DEVICE_IFACE, iface,
+                            NM_DEVICE_TYPE_DESC, "802.11 WiFi",
+                            NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI,
+                            NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI,
+                            NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN,
+                            NM_DEVICE_IWD_CAPABILITIES, (guint) capabilities,
+                            NULL);
+}
+
+static void
+dispose (GObject *object)
+{
+       NMDeviceIwd *self = NM_DEVICE_IWD (object);
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       nm_clear_g_cancellable (&priv->cancellable);
+
+       wifi_secrets_cancel (self);
+
+       cleanup_association_attempt (self, TRUE);
+
+       g_clear_object (&priv->dbus_proxy);
+       g_clear_object (&priv->dbus_obj);
+
+       remove_all_aps (self);
+
+       G_OBJECT_CLASS (nm_device_iwd_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+       NMDeviceIwd *self = NM_DEVICE_IWD (object);
+       NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+
+       nm_assert (g_hash_table_size (priv->aps) == 0);
+
+       g_hash_table_unref (priv->aps);
+
+       G_OBJECT_CLASS (nm_device_iwd_parent_class)->finalize (object);
+}
+
+static void
+nm_device_iwd_class_init (NMDeviceIwdClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
+
+       NM_DEVICE_CLASS_DECLARE_TYPES (klass, NM_SETTING_WIRELESS_SETTING_NAME, NM_LINK_TYPE_WIFI)
+
+       object_class->get_property = get_property;
+       object_class->set_property = set_property;
+       object_class->dispose = dispose;
+       object_class->finalize = finalize;
+
+       parent_class->can_auto_connect = can_auto_connect;
+       parent_class->is_available = is_available;
+       parent_class->check_connection_compatible = check_connection_compatible;
+       parent_class->check_connection_available = check_connection_available;
+       parent_class->complete_connection = complete_connection;
+       parent_class->get_enabled = get_enabled;
+       parent_class->set_enabled = set_enabled;
+
+       parent_class->act_stage1_prepare = act_stage1_prepare;
+       parent_class->act_stage2_config = act_stage2_config;
+       parent_class->get_configured_mtu = get_configured_mtu;
+       parent_class->deactivate = deactivate;
+       parent_class->can_reapply_change = can_reapply_change;
+
+       parent_class->state_changed = device_state_changed;
+
+       klass->scanning_prohibited = scanning_prohibited;
+
+       obj_properties[PROP_MODE] =
+           g_param_spec_uint (NM_DEVICE_IWD_MODE, "", "",
+                              NM_802_11_MODE_UNKNOWN,
+                              NM_802_11_MODE_AP,
+                              NM_802_11_MODE_INFRA,
+                              G_PARAM_READABLE |
+                              G_PARAM_STATIC_STRINGS);
+
+       obj_properties[PROP_BITRATE] =
+           g_param_spec_uint (NM_DEVICE_IWD_BITRATE, "", "",
+                              0, G_MAXUINT32, 0,
+                              G_PARAM_READABLE |
+                              G_PARAM_STATIC_STRINGS);
+
+       obj_properties[PROP_ACCESS_POINTS] =
+           g_param_spec_boxed (NM_DEVICE_IWD_ACCESS_POINTS, "", "",
+                               G_TYPE_STRV,
+                               G_PARAM_READABLE |
+                               G_PARAM_STATIC_STRINGS);
+
+       obj_properties[PROP_ACTIVE_ACCESS_POINT] =
+           g_param_spec_string (NM_DEVICE_IWD_ACTIVE_ACCESS_POINT, "", "",
+                                NULL,
+                                G_PARAM_READABLE |
+                                G_PARAM_STATIC_STRINGS);
+
+       obj_properties[PROP_CAPABILITIES] =
+           g_param_spec_uint (NM_DEVICE_IWD_CAPABILITIES, "", "",
+                              0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE,
+                              G_PARAM_READWRITE |
+                              G_PARAM_CONSTRUCT_ONLY |
+                              G_PARAM_STATIC_STRINGS);
+
+       obj_properties[PROP_SCANNING] =
+           g_param_spec_boolean (NM_DEVICE_IWD_SCANNING, "", "",
+                                 FALSE,
+                                 G_PARAM_READABLE |
+                                 G_PARAM_STATIC_STRINGS);
+
+       g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+       signals[ACCESS_POINT_ADDED] =
+           g_signal_new (NM_DEVICE_IWD_ACCESS_POINT_ADDED,
+                         G_OBJECT_CLASS_TYPE (object_class),
+                         G_SIGNAL_RUN_FIRST,
+                         0,
+                         NULL, NULL, NULL,
+                         G_TYPE_NONE, 1,
+                         NM_TYPE_WIFI_AP);
+
+       signals[ACCESS_POINT_REMOVED] =
+           g_signal_new (NM_DEVICE_IWD_ACCESS_POINT_REMOVED,
+                         G_OBJECT_CLASS_TYPE (object_class),
+                         G_SIGNAL_RUN_FIRST,
+                         0,
+                         NULL, NULL, NULL,
+                         G_TYPE_NONE, 1,
+                         NM_TYPE_WIFI_AP);
+
+       signals[SCANNING_PROHIBITED] =
+           g_signal_new (NM_DEVICE_IWD_SCANNING_PROHIBITED,
+                         G_OBJECT_CLASS_TYPE (object_class),
+                         G_SIGNAL_RUN_LAST,
+                         G_STRUCT_OFFSET (NMDeviceIwdClass, scanning_prohibited),
+                         NULL, NULL, NULL,
+                         G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN);
+
+       nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+                                               NMDBUS_TYPE_DEVICE_WIFI_SKELETON,
+                                               "GetAccessPoints", impl_device_iwd_get_access_points,
+                                               "GetAllAccessPoints", impl_device_iwd_get_all_access_points,
+                                               "RequestScan", impl_device_iwd_request_scan,
+                                               NULL);
+}
diff --git a/src/devices/wifi/nm-device-iwd.h b/src/devices/wifi/nm-device-iwd.h
new file mode 100644
index 000000000..0cfdcd6ee
--- /dev/null
+++ b/src/devices/wifi/nm-device-iwd.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2017 Intel Corporation
+ */
+
+#ifndef __NETWORKMANAGER_DEVICE_IWD_H__
+#define __NETWORKMANAGER_DEVICE_IWD_H__
+
+#include "devices/nm-device.h"
+#include "nm-wifi-ap.h"
+#include "nm-device-wifi.h"
+
+#define NM_TYPE_DEVICE_IWD              (nm_device_iwd_get_type ())
+#define NM_DEVICE_IWD(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_IWD, NMDeviceIwd))
+#define NM_DEVICE_IWD_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_IWD, 
NMDeviceIwdClass))
+#define NM_IS_DEVICE_IWD(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_IWD))
+#define NM_IS_DEVICE_IWD_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_IWD))
+#define NM_DEVICE_IWD_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_IWD, 
NMDeviceIwdClass))
+
+#define NM_DEVICE_IWD_MODE                NM_DEVICE_WIFI_MODE
+#define NM_DEVICE_IWD_BITRATE             NM_DEVICE_WIFI_BITRATE
+#define NM_DEVICE_IWD_ACCESS_POINTS       NM_DEVICE_WIFI_ACCESS_POINTS
+#define NM_DEVICE_IWD_ACTIVE_ACCESS_POINT NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT
+#define NM_DEVICE_IWD_CAPABILITIES        NM_DEVICE_WIFI_CAPABILITIES
+#define NM_DEVICE_IWD_SCANNING            NM_DEVICE_WIFI_SCANNING
+
+/* signals */
+#define NM_DEVICE_IWD_ACCESS_POINT_ADDED   NM_DEVICE_WIFI_ACCESS_POINT_ADDED
+#define NM_DEVICE_IWD_ACCESS_POINT_REMOVED NM_DEVICE_WIFI_ACCESS_POINT_REMOVED
+
+/* internal signals */
+#define NM_DEVICE_IWD_SCANNING_PROHIBITED  NM_DEVICE_WIFI_SCANNING_PROHIBITED
+
+typedef struct _NMDeviceIwd NMDeviceIwd;
+typedef struct _NMDeviceIwdClass NMDeviceIwdClass;
+
+GType nm_device_iwd_get_type (void);
+
+NMDevice *nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities);
+
+#endif /* __NETWORKMANAGER_DEVICE_IWD_H__ */
-- 
2.11.0



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]