summary refs log tree commit diff
path: root/src/devices/wifi
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2014-07-06 02:16:10 +0200
committerMichael Biebl <biebl@debian.org>2014-07-06 02:16:10 +0200
commit33491bc4279481db8ae47213e34a6d695a0e8830 (patch)
tree097d2b0fdff3fae6885381ae5e57a182cd8cbbba /src/devices/wifi
parent59c3714a494c3b3765657c0551ad82842d98a7d2 (diff)
Imported Upstream version 0.9.10.0 upstream/0.9.10.0
Diffstat (limited to 'src/devices/wifi')
-rw-r--r--src/devices/wifi/Makefile.am78
-rw-r--r--src/devices/wifi/Makefile.in983
-rw-r--r--src/devices/wifi/exports.ver7
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh-glue.h73
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c585
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.h82
-rw-r--r--src/devices/wifi/nm-device-wifi-glue.h167
-rw-r--r--src/devices/wifi/nm-device-wifi.c3502
-rw-r--r--src/devices/wifi/nm-device-wifi.h94
-rw-r--r--src/devices/wifi/nm-wifi-ap-utils.c721
-rw-r--r--src/devices/wifi/nm-wifi-ap-utils.h45
-rw-r--r--src/devices/wifi/nm-wifi-ap.c1293
-rw-r--r--src/devices/wifi/nm-wifi-ap.h121
-rw-r--r--src/devices/wifi/nm-wifi-enum-types.c58
-rw-r--r--src/devices/wifi/nm-wifi-enum-types.h21
-rw-r--r--src/devices/wifi/nm-wifi-factory.c89
-rw-r--r--src/devices/wifi/tests/Makefile.am28
-rw-r--r--src/devices/wifi/tests/Makefile.in891
-rw-r--r--src/devices/wifi/tests/test-wifi-ap-utils.c1545
19 files changed, 10383 insertions, 0 deletions
diff --git a/src/devices/wifi/Makefile.am b/src/devices/wifi/Makefile.am
new file mode 100644
index 00000000..5f3ce286
--- /dev/null
+++ b/src/devices/wifi/Makefile.am
@@ -0,0 +1,78 @@
+include $(GLIB_MAKEFILE)
+
+@GNOME_CODE_COVERAGE_RULES@
+
+SUBDIRS=. tests
+
+AM_CPPFLAGS = \
+	-I${top_srcdir}/src \
+	-I${top_builddir}/src \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/src/devices \
+	-I${top_srcdir}/src/settings \
+	-I${top_srcdir}/src/platform \
+	-I${top_srcdir}/src/supplicant-manager \
+	-I${top_builddir}/include \
+	-I${top_srcdir}/include \
+	-I${top_builddir}/libnm-util \
+	-I${top_srcdir}/libnm-util \
+	-DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+	$(DBUS_CFLAGS) \
+	$(POLKIT_CFLAGS) \
+	$(LIBNL_CFLAGS) \
+	$(GUDEV_CFLAGS)
+
+GLIB_GENERATED = nm-wifi-enum-types.h nm-wifi-enum-types.c
+GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
+GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
+nm_wifi_enum_types_sources = \
+	$(srcdir)/nm-device-wifi.h \
+	$(srcdir)/nm-wifi-ap.h \
+	$(srcdir)/nm-device-olpc-mesh.h
+
+glue_sources = \
+	nm-device-wifi-glue.h \
+	nm-device-olpc-mesh-glue.h
+
+%-glue.h: $(top_srcdir)/introspection/%.xml
+	$(AM_V_GEN) dbus-binding-tool --prefix=$(subst -,_,$(subst -glue.h,,$@)) --mode=glib-server --output=$@ $<
+
+BUILT_SOURCES = $(GLIB_GENERATED) $(glue_sources)
+
+pkglib_LTLIBRARIES = libnm-device-plugin-wifi.la
+
+libnm_device_plugin_wifi_la_SOURCES = \
+	nm-wifi-factory.c \
+	nm-device-wifi.c \
+	nm-device-wifi.h \
+	nm-wifi-ap.c \
+	nm-wifi-ap.h \
+	nm-wifi-ap-utils.c \
+	nm-wifi-ap-utils.h \
+	nm-device-olpc-mesh.c \
+	nm-device-olpc-mesh.h \
+	\
+	$(BUILT_SOURCES)
+
+SYMBOL_VIS_FILE=$(srcdir)/exports.ver
+
+libnm_device_plugin_wifi_la_LDFLAGS = \
+	-module -avoid-version \
+	 -Wl,--version-script=$(SYMBOL_VIS_FILE)
+
+libnm_device_plugin_wifi_la_LIBADD = \
+	$(DBUS_LIBS) \
+	$(GUDEV_LIBS)
+
+CLEANFILES = $(BUILT_SOURCES)
+
+EXTRA_DIST = $(SYMBOL_VIS_FILE)
+
+if ENABLE_TESTS
+
+check-local:
+	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-wifi.so $(SYMBOL_VIS_FILE)
+
+endif
+
diff --git a/src/devices/wifi/Makefile.in b/src/devices/wifi/Makefile.in
new file mode 100644
index 00000000..cbf6826b
--- /dev/null
+++ b/src/devices/wifi/Makefile.in
@@ -0,0 +1,983 @@
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/devices/wifi
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/build-aux/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gettext.m4 \
+	$(top_srcdir)/m4/gnome-code-coverage.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
+	$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \
+	$(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+	$(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(pkglibdir)"
+LTLIBRARIES = $(pkglib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libnm_device_plugin_wifi_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+	$(am__DEPENDENCIES_1)
+am__objects_1 = nm-wifi-enum-types.lo
+am__objects_2 =
+am__objects_3 = $(am__objects_1) $(am__objects_2)
+am_libnm_device_plugin_wifi_la_OBJECTS = nm-wifi-factory.lo \
+	nm-device-wifi.lo nm-wifi-ap.lo nm-wifi-ap-utils.lo \
+	nm-device-olpc-mesh.lo $(am__objects_3)
+libnm_device_plugin_wifi_la_OBJECTS =  \
+	$(am_libnm_device_plugin_wifi_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+libnm_device_plugin_wifi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+	$(AM_CFLAGS) $(CFLAGS) $(libnm_device_plugin_wifi_la_LDFLAGS) \
+	$(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libnm_device_plugin_wifi_la_SOURCES)
+DIST_SOURCES = $(libnm_device_plugin_wifi_la_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	distdir
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CKDB_PATH = @CKDB_PATH@
+CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
+DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
+DLLTOOL = @DLLTOOL@
+DNSMASQ_PATH = @DNSMASQ_PATH@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MAKEFILE = @GLIB_MAKEFILE@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
+INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
+INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@
+INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@
+INTROSPECTION_LIBS = @INTROSPECTION_LIBS@
+INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
+INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
+INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
+IPTABLES_PATH = @IPTABLES_PATH@
+IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@
+IWMX_SDK_LIBS = @IWMX_SDK_LIBS@
+KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBM = @LIBM@
+LIBNDP_CFLAGS = @LIBNDP_CFLAGS@
+LIBNDP_LIBS = @LIBNDP_LIBS@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
+LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
+LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@
+MM_GLIB_LIBS = @MM_GLIB_LIBS@
+MOC = @MOC@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NEWT_CFLAGS = @NEWT_CFLAGS@
+NEWT_LIBS = @NEWT_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
+NM_MICRO_VERSION = @NM_MICRO_VERSION@
+NM_MINOR_VERSION = @NM_MINOR_VERSION@
+NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@
+NM_VERSION = @NM_VERSION@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PPPD_PATH = @PPPD_PATH@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+PPPOE_PATH = @PPPOE_PATH@
+QT_CFLAGS = @QT_CFLAGS@
+QT_LIBS = @QT_LIBS@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+SED = @SED@
+SELINUX_CFLAGS = @SELINUX_CFLAGS@
+SELINUX_LIBS = @SELINUX_LIBS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@
+SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@
+SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@
+SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
+SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
+SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VALGRIND_RULES = @VALGRIND_RULES@
+VAPIGEN = @VAPIGEN@
+VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
+VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+nmbinary = @nmbinary@
+nmconfdir = @nmconfdir@
+nmdatadir = @nmdatadir@
+nmrundir = @nmrundir@
+nmstatedir = @nmstatedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+systemdsystemunitdir = @systemdsystemunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+with_dhclient = @with_dhclient@
+with_dhcpcd = @with_dhcpcd@
+with_netconfig = @with_netconfig@
+with_resolvconf = @with_resolvconf@
+with_valgrind = @with_valgrind@
+SUBDIRS = . tests
+AM_CPPFLAGS = \
+	-I${top_srcdir}/src \
+	-I${top_builddir}/src \
+	-I${top_srcdir}/src/logging \
+	-I${top_srcdir}/src/devices \
+	-I${top_srcdir}/src/settings \
+	-I${top_srcdir}/src/platform \
+	-I${top_srcdir}/src/supplicant-manager \
+	-I${top_builddir}/include \
+	-I${top_srcdir}/include \
+	-I${top_builddir}/libnm-util \
+	-I${top_srcdir}/libnm-util \
+	-DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+	$(DBUS_CFLAGS) \
+	$(POLKIT_CFLAGS) \
+	$(LIBNL_CFLAGS) \
+	$(GUDEV_CFLAGS)
+
+GLIB_GENERATED = nm-wifi-enum-types.h nm-wifi-enum-types.c
+GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
+GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
+nm_wifi_enum_types_sources = \
+	$(srcdir)/nm-device-wifi.h \
+	$(srcdir)/nm-wifi-ap.h \
+	$(srcdir)/nm-device-olpc-mesh.h
+
+glue_sources = \
+	nm-device-wifi-glue.h \
+	nm-device-olpc-mesh-glue.h
+
+BUILT_SOURCES = $(GLIB_GENERATED) $(glue_sources)
+pkglib_LTLIBRARIES = libnm-device-plugin-wifi.la
+libnm_device_plugin_wifi_la_SOURCES = \
+	nm-wifi-factory.c \
+	nm-device-wifi.c \
+	nm-device-wifi.h \
+	nm-wifi-ap.c \
+	nm-wifi-ap.h \
+	nm-wifi-ap-utils.c \
+	nm-wifi-ap-utils.h \
+	nm-device-olpc-mesh.c \
+	nm-device-olpc-mesh.h \
+	\
+	$(BUILT_SOURCES)
+
+SYMBOL_VIS_FILE = $(srcdir)/exports.ver
+libnm_device_plugin_wifi_la_LDFLAGS = \
+	-module -avoid-version \
+	 -Wl,--version-script=$(SYMBOL_VIS_FILE)
+
+libnm_device_plugin_wifi_la_LIBADD = \
+	$(DBUS_LIBS) \
+	$(GUDEV_LIBS)
+
+CLEANFILES = $(BUILT_SOURCES)
+EXTRA_DIST = $(SYMBOL_VIS_FILE)
+all: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/wifi/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/devices/wifi/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	@list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \
+	list2=; for p in $$list; do \
+	  if test -f $$p; then \
+	    list2="$$list2 $$p"; \
+	  else :; fi; \
+	done; \
+	test -z "$$list2" || { \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \
+	}
+
+uninstall-pkglibLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	@list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \
+	for p in $$list; do \
+	  $(am__strip_dir) \
+	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \
+	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \
+	done
+
+clean-pkglibLTLIBRARIES:
+	-test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES)
+	@list='$(pkglib_LTLIBRARIES)'; \
+	locs=`for p in $$list; do echo $$p; done | \
+	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+	      sort -u`; \
+	test -z "$$locs" || { \
+	  echo rm -f $${locs}; \
+	  rm -f $${locs}; \
+	}
+
+libnm-device-plugin-wifi.la: $(libnm_device_plugin_wifi_la_OBJECTS) $(libnm_device_plugin_wifi_la_DEPENDENCIES) $(EXTRA_libnm_device_plugin_wifi_la_DEPENDENCIES) 
+	$(AM_V_CCLD)$(libnm_device_plugin_wifi_la_LINK) -rpath $(pkglibdir) $(libnm_device_plugin_wifi_la_OBJECTS) $(libnm_device_plugin_wifi_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-device-olpc-mesh.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-device-wifi.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wifi-ap-utils.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wifi-ap.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wifi-enum-types.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wifi-factory.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+@ENABLE_TESTS_FALSE@check-local:
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) check-local
+check: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) check-recursive
+all-am: Makefile $(LTLIBRARIES)
+installdirs: installdirs-recursive
+installdirs-am:
+	for dir in "$(DESTDIR)$(pkglibdir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-pkglibLTLIBRARIES
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-pkglibLTLIBRARIES
+
+.MAKE: $(am__recursive_targets) all check check-am install install-am \
+	install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+	check-am check-local clean clean-generic clean-libtool \
+	clean-pkglibLTLIBRARIES cscopelist-am ctags ctags-am distclean \
+	distclean-compile distclean-generic distclean-libtool \
+	distclean-tags distdir dvi dvi-am html html-am info info-am \
+	install install-am install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-html \
+	install-html-am install-info install-info-am install-man \
+	install-pdf install-pdf-am install-pkglibLTLIBRARIES \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs installdirs-am maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am \
+	uninstall-pkglibLTLIBRARIES
+
+include $(GLIB_MAKEFILE)
+
+@GNOME_CODE_COVERAGE_RULES@
+
+%-glue.h: $(top_srcdir)/introspection/%.xml
+	$(AM_V_GEN) dbus-binding-tool --prefix=$(subst -,_,$(subst -glue.h,,$@)) --mode=glib-server --output=$@ $<
+
+@ENABLE_TESTS_TRUE@check-local:
+@ENABLE_TESTS_TRUE@	$(top_srcdir)/tools/check-exports.sh $(builddir)/.libs/libnm-device-plugin-wifi.so $(SYMBOL_VIS_FILE)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/devices/wifi/exports.ver b/src/devices/wifi/exports.ver
new file mode 100644
index 00000000..d2c45124
--- /dev/null
+++ b/src/devices/wifi/exports.ver
@@ -0,0 +1,7 @@
+{
+global:
+	nm_device_factory_create;
+	nm_device_factory_get_device_type;
+local:
+	*;
+};
diff --git a/src/devices/wifi/nm-device-olpc-mesh-glue.h b/src/devices/wifi/nm-device-olpc-mesh-glue.h
new file mode 100644
index 00000000..04b5e83f
--- /dev/null
+++ b/src/devices/wifi/nm-device-olpc-mesh-glue.h
@@ -0,0 +1,73 @@
+/* Generated by dbus-binding-tool; do not edit! */
+
+
+#ifndef __dbus_glib_marshal_nm_device_olpc_mesh_MARSHAL_H__
+#define __dbus_glib_marshal_nm_device_olpc_mesh_MARSHAL_H__
+
+#include	<glib-object.h>
+
+G_BEGIN_DECLS
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_schar (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#define g_marshal_value_peek_variant(v)  g_value_get_variant (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_variant(v)  (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+G_END_DECLS
+
+#endif /* __dbus_glib_marshal_nm_device_olpc_mesh_MARSHAL_H__ */
+
+#include <dbus/dbus-glib.h>
+static const DBusGMethodInfo dbus_glib_nm_device_olpc_mesh_methods[] = {
+};
+
+const DBusGObjectInfo dbus_glib_nm_device_olpc_mesh_object_info = {  1,
+  dbus_glib_nm_device_olpc_mesh_methods,
+  0,
+"\0",
+"org.freedesktop.NetworkManager.Device.OlpcMesh\0PropertiesChanged\0\0",
+"org.freedesktop.NetworkManager.Device.OlpcMesh\0HwAddress\0hw_address\0read\0org.freedesktop.NetworkManager.Device.OlpcMesh\0Companion\0companion\0read\0org.freedesktop.NetworkManager.Device.OlpcMesh\0ActiveChannel\0active_channel\0read\0\0"
+};
+
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
new file mode 100644
index 00000000..85214a70
--- /dev/null
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -0,0 +1,585 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * Dan Williams <dcbw@redhat.com>
+ * Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ * Daniel Drake <dsd@laptop.org>
+ *
+ * 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.
+ *
+ * (C) Copyright 2005 - 2014 Red Hat, Inc.
+ * (C) Copyright 2008 Collabora Ltd.
+ * (C) Copyright 2009 One Laptop per Child
+ */
+
+#include "config.h"
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <net/ethernet.h>
+#include <netinet/ether.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#include <linux/if.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include "nm-device.h"
+#include "nm-device-wifi.h"
+#include "nm-device-olpc-mesh.h"
+#include "nm-device-private.h"
+#include "nm-utils.h"
+#include "nm-logging.h"
+#include "NetworkManagerUtils.h"
+#include "nm-activation-request.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-olpc-mesh.h"
+#include "nm-manager.h"
+#include "nm-enum-types.h"
+#include "nm-dbus-manager.h"
+#include "nm-wifi-enum-types.h"
+
+/* This is a bug; but we can't really change API now... */
+#include "NetworkManagerVPN.h"
+
+
+#include "nm-device-olpc-mesh-glue.h"
+
+G_DEFINE_TYPE (NMDeviceOlpcMesh, nm_device_olpc_mesh, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshPrivate))
+
+enum {
+	PROP_0,
+	PROP_COMPANION,
+	PROP_ACTIVE_CHANNEL,
+
+	LAST_PROP
+};
+
+struct _NMDeviceOlpcMeshPrivate {
+	NMDevice *companion;
+	gboolean  stage1_waiting;
+};
+
+/*******************************************************************/
+
+static GQuark
+nm_olpc_mesh_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-mesh-error");
+	return quark;
+}
+
+#define NM_OLPC_MESH_ERROR (nm_olpc_mesh_error_quark ())
+
+/*******************************************************************/
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+	NMSettingConnection *s_con;
+	NMSettingOlpcMesh *s_mesh;
+
+	if (!NM_DEVICE_CLASS (nm_device_olpc_mesh_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_OLPC_MESH_SETTING_NAME))
+		return FALSE;
+
+	s_mesh = nm_connection_get_setting_olpc_mesh (connection);
+	if (!s_mesh)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+can_auto_connect (NMDevice *device,
+                  NMConnection *connection,
+                  char **specific_object)
+{
+	return FALSE;
+}
+
+#define DEFAULT_SSID "olpc-mesh"
+
+static gboolean
+complete_connection (NMDevice *device,
+                     NMConnection *connection,
+                     const char *specific_object,
+                     const GSList *existing_connections,
+                     GError **error)
+{
+	NMSettingOlpcMesh *s_mesh;
+	GByteArray *tmp;
+
+	s_mesh = nm_connection_get_setting_olpc_mesh (connection);
+	if (!s_mesh) {
+		s_mesh = (NMSettingOlpcMesh *) nm_setting_olpc_mesh_new ();
+		nm_connection_add_setting (connection, NM_SETTING (s_mesh));
+	}
+
+	if (!nm_setting_olpc_mesh_get_ssid (s_mesh)) {
+		tmp = g_byte_array_sized_new (strlen (DEFAULT_SSID));
+		g_byte_array_append (tmp, (const guint8 *) DEFAULT_SSID, strlen (DEFAULT_SSID));
+		g_object_set (G_OBJECT (s_mesh), NM_SETTING_OLPC_MESH_SSID, tmp, NULL);
+		g_byte_array_free (tmp, TRUE);
+	}
+
+	if (!nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh)) {
+		const guint8 anycast[ETH_ALEN] = { 0xC0, 0x27, 0xC0, 0x27, 0xC0, 0x27 };
+
+		tmp = g_byte_array_sized_new (ETH_ALEN);
+		g_byte_array_append (tmp, anycast, sizeof (anycast));
+		g_object_set (G_OBJECT (s_mesh), NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, tmp, NULL);
+		g_byte_array_free (tmp, TRUE);
+
+	}
+
+	nm_utils_complete_generic (connection,
+	                           NM_SETTING_OLPC_MESH_SETTING_NAME,
+	                           existing_connections,
+	                           _("Mesh %d"),
+	                           NULL,
+	                           FALSE); /* No IPv6 by default */
+
+	return TRUE;
+}
+
+/****************************************************************************/
+
+static NMActStageReturn
+act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+{
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (dev);
+	NMActStageReturn ret;
+	gboolean scanning;
+
+	ret = NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->act_stage1_prepare (dev, reason);
+	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+		return ret;
+
+	/* disconnect companion device, if it is connected */
+	if (nm_device_get_act_request (NM_DEVICE (priv->companion))) {
+		nm_log_info (LOGD_OLPC, "(%s): disconnecting companion device %s",
+		             nm_device_get_iface (dev),
+		             nm_device_get_iface (priv->companion));
+		/* FIXME: VPN stuff here is a bug; but we can't really change API now... */
+		nm_device_state_changed (NM_DEVICE (priv->companion),
+		                         NM_DEVICE_STATE_DISCONNECTED,
+		                         NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED);
+		nm_log_info (LOGD_OLPC, "(%s): companion %s disconnected",
+		             nm_device_get_iface (dev),
+		             nm_device_get_iface (priv->companion));
+	}
+
+
+	/* wait with continuing configuration untill the companion device is done scanning */
+	g_object_get (priv->companion, "scanning", &scanning, NULL);
+	if (scanning) {
+		priv->stage1_waiting = TRUE;
+		return NM_ACT_STAGE_RETURN_POSTPONE;
+	}
+
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+static void
+_mesh_set_channel (NMDeviceOlpcMesh *self, guint32 channel)
+{
+	int ifindex = nm_device_get_ifindex (NM_DEVICE (self));
+
+	if (nm_platform_mesh_get_channel (ifindex) != channel) {
+		if (nm_platform_mesh_set_channel (ifindex, channel))
+			g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL);
+	}
+}
+
+static NMActStageReturn
+act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev);
+	NMConnection *connection;
+	NMSettingOlpcMesh *s_mesh;
+	guint32 channel;
+	const GByteArray *anycast_addr_array;
+	guint8 *anycast_addr = NULL;
+
+	connection = nm_device_get_connection (dev);
+	g_assert (connection);
+
+	s_mesh = nm_connection_get_setting_olpc_mesh (connection);
+	g_assert (s_mesh);
+
+	channel = nm_setting_olpc_mesh_get_channel (s_mesh);
+	if (channel != 0)
+		_mesh_set_channel (self, channel);
+	nm_platform_mesh_set_ssid (nm_device_get_ifindex (dev),
+	                           nm_setting_olpc_mesh_get_ssid (s_mesh));
+
+	anycast_addr_array = nm_setting_olpc_mesh_get_dhcp_anycast_address (s_mesh);
+	if (anycast_addr_array)
+		anycast_addr = anycast_addr_array->data;
+
+	nm_device_set_dhcp_anycast_address (dev, anycast_addr);
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+static gboolean
+is_available (NMDevice *dev)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (dev);
+
+	if (!NM_DEVICE_OLPC_MESH_GET_PRIVATE (self)->companion) {
+		nm_log_dbg (LOGD_WIFI, "(%s): not available because companion not found",
+		            nm_device_get_iface (dev));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+/*******************************************************************/
+
+static void
+companion_cleanup (NMDeviceOlpcMesh *self)
+{
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+
+	if (priv->companion) {
+		g_signal_handlers_disconnect_by_data (priv->companion, self);
+		g_clear_object (&priv->companion);
+	}
+	g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_COMPANION);
+}
+
+static void
+companion_notify_cb (NMDeviceWifi *companion, GParamSpec *pspec, gpointer user_data)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+	gboolean scanning;
+
+	if (!priv->stage1_waiting)
+		return;
+
+	g_object_get (companion, "scanning", &scanning, NULL);
+
+	if (!scanning) {
+		priv->stage1_waiting = FALSE;
+		nm_device_activate_schedule_stage2_device_config (NM_DEVICE (self));
+	}
+}
+
+/* disconnect from mesh if someone starts using the companion */
+static void
+companion_state_changed_cb (NMDeviceWifi *companion,
+                            NMDeviceState state,
+                            NMDeviceState old_state,
+                            NMDeviceStateReason reason,
+                            gpointer user_data)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
+	NMDeviceState self_state = nm_device_get_state (NM_DEVICE (self));
+
+	if (   self_state < NM_DEVICE_STATE_PREPARE
+	    || self_state > NM_DEVICE_STATE_ACTIVATED
+	    || state < NM_DEVICE_STATE_PREPARE
+	    || state > NM_DEVICE_STATE_ACTIVATED)
+		return;
+
+	nm_log_dbg (LOGD_OLPC, "(%s): disconnecting mesh due to companion connectivity",
+	            nm_device_get_iface (NM_DEVICE (self)));
+	/* FIXME: VPN stuff here is a bug; but we can't really change API now... */
+	nm_device_state_changed (NM_DEVICE (self),
+	                         NM_DEVICE_STATE_DISCONNECTED,
+	                         NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED);
+}
+
+static gboolean
+companion_scan_allowed_cb (NMDeviceWifi *companion, gpointer user_data)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
+	NMDeviceState state = nm_device_get_state (NM_DEVICE (self));
+
+	/* Don't allow the companion to scan while configuring the mesh interface */
+	return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_IP_CONFIG);
+}
+
+static gboolean
+companion_autoconnect_allowed_cb (NMDeviceWifi *companion, gpointer user_data)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
+	NMDeviceState state = nm_device_get_state (NM_DEVICE (self));
+
+	/* Don't allow the companion to autoconnect while a mesh connection is
+	 * active */
+	return (state < NM_DEVICE_STATE_PREPARE) || (state > NM_DEVICE_STATE_ACTIVATED);
+}
+
+static gboolean
+check_companion (NMDeviceOlpcMesh *self, NMDevice *other)
+{
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+	const guint8 *my_addr, *their_addr;
+	guint their_addr_len;
+
+	if (!NM_IS_DEVICE_WIFI (other))
+		return FALSE;
+
+	my_addr = nm_device_get_hw_address (NM_DEVICE (self), NULL);
+	their_addr = nm_device_get_hw_address (other, &their_addr_len);
+	if (   (their_addr_len != ETH_ALEN)
+	    || (memcmp (my_addr, their_addr, ETH_ALEN) != 0))
+		return FALSE;
+
+	g_assert (priv->companion == NULL);
+	priv->companion = g_object_ref (other);
+
+	nm_log_info (LOGD_OLPC, "(%s): found companion WiFi device %s",
+	             nm_device_get_iface (NM_DEVICE (self)),
+	             nm_device_get_iface (other));
+
+	g_signal_connect (G_OBJECT (other), "state-changed",
+	                  G_CALLBACK (companion_state_changed_cb), self);
+
+	g_signal_connect (G_OBJECT (other), "notify::scanning",
+	                  G_CALLBACK (companion_notify_cb), self);
+
+	g_signal_connect (G_OBJECT (other), "scanning-allowed",
+	                  G_CALLBACK (companion_scan_allowed_cb), self);
+
+	g_signal_connect (G_OBJECT (other), "autoconnect-allowed",
+	                  G_CALLBACK (companion_autoconnect_allowed_cb), self);
+
+	g_object_notify (G_OBJECT (self), NM_DEVICE_OLPC_MESH_COMPANION);
+
+	return TRUE;
+}
+
+static void
+device_added_cb (NMManager *manager, NMDevice *other, gpointer user_data)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+
+	if (!priv->companion && check_companion (self, other)) {
+		nm_device_state_changed (NM_DEVICE (self),
+		                         NM_DEVICE_STATE_DISCONNECTED,
+		                         NM_DEVICE_STATE_REASON_NONE);
+		nm_device_remove_pending_action (NM_DEVICE (self), "waiting for companion", TRUE);
+	}
+}
+
+static void
+device_removed_cb (NMManager *manager, NMDevice *other, gpointer user_data)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (user_data);
+
+	if (other == NM_DEVICE_OLPC_MESH_GET_PRIVATE (self)->companion)
+		companion_cleanup (self);
+}
+
+static void
+find_companion (NMDeviceOlpcMesh *self)
+{
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
+	const GSList *list;
+
+	if (priv->companion)
+		return;
+
+	nm_device_add_pending_action (NM_DEVICE (self), "waiting for companion", TRUE);
+
+	/* Try to find the companion if it's already known to the NMManager */
+	for (list = nm_manager_get_devices (nm_manager_get ()); list ; list = g_slist_next (list)) {
+		if (check_companion (self, NM_DEVICE (list->data))) {
+			nm_device_queue_state (NM_DEVICE (self),
+			                       NM_DEVICE_STATE_DISCONNECTED,
+			                       NM_DEVICE_STATE_REASON_NONE);
+			nm_device_remove_pending_action (NM_DEVICE (self), "waiting for companion", TRUE);
+			break;
+		}
+	}
+}
+
+static void
+state_changed (NMDevice *device,
+               NMDeviceState new_state,
+               NMDeviceState old_state,
+               NMDeviceStateReason reason)
+{
+	if (new_state == NM_DEVICE_STATE_UNAVAILABLE)
+		find_companion (NM_DEVICE_OLPC_MESH (device));
+}
+
+/*******************************************************************/
+
+NMDevice *
+nm_device_olpc_mesh_new (NMPlatformLink *platform_device)
+{
+	g_return_val_if_fail (platform_device != NULL, NULL);
+
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_OLPC_MESH,
+	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
+	                                  NM_DEVICE_TYPE_DESC, "802.11 OLPC Mesh",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_OLPC_MESH,
+	                                  NULL);
+}
+
+static void
+nm_device_olpc_mesh_init (NMDeviceOlpcMesh * self)
+{
+}
+
+static GObject*
+constructor (GType type,
+             guint n_construct_params,
+             GObjectConstructParam *construct_params)
+{
+	GObject *object;
+	GObjectClass *klass;
+	NMDeviceOlpcMesh *self;
+	NMDeviceWifiCapabilities caps;
+
+	klass = G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class);
+	object = klass->constructor (type, n_construct_params, construct_params);
+	if (!object)
+		return NULL;
+
+	self = NM_DEVICE_OLPC_MESH (object);
+
+	nm_log_dbg (LOGD_HW | LOGD_OLPC, "(%s): kernel ifindex %d",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            nm_device_get_ifindex (NM_DEVICE (self)));
+
+	if (!nm_platform_wifi_get_capabilities (nm_device_get_ifindex (NM_DEVICE (self)), &caps)) {
+		nm_log_warn (LOGD_HW | LOGD_OLPC, "(%s): failed to initialize WiFi driver",
+		             nm_device_get_iface (NM_DEVICE (self)));
+		g_object_unref (object);
+		return NULL;
+	}
+
+	g_signal_connect (nm_manager_get (), "device-added", G_CALLBACK (device_added_cb), self);
+	g_signal_connect (nm_manager_get (), "device-removed", G_CALLBACK (device_removed_cb), self);
+
+	/* shorter timeout for mesh connectivity */
+	nm_device_set_dhcp_timeout (NM_DEVICE (self), 20);
+	return object;
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMDeviceOlpcMesh *device = NM_DEVICE_OLPC_MESH (object);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (device);
+
+	switch (prop_id) {
+	case PROP_COMPANION:
+		if (priv->companion)
+			g_value_set_boxed (value, nm_device_get_path (priv->companion));
+		else
+			g_value_set_boxed (value, "/");
+		break;
+	case PROP_ACTIVE_CHANNEL:
+		g_value_set_uint (value, nm_platform_mesh_get_channel (nm_device_get_ifindex (NM_DEVICE (device))));
+		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)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+dispose (GObject *object)
+{
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (object);
+
+	companion_cleanup (self);
+	g_signal_handlers_disconnect_by_func (nm_manager_get (), G_CALLBACK (device_added_cb), self);
+	g_signal_handlers_disconnect_by_func (nm_manager_get (), G_CALLBACK (device_removed_cb), self);
+
+	G_OBJECT_CLASS (nm_device_olpc_mesh_parent_class)->dispose (object);
+}
+
+static void
+nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMDeviceOlpcMeshPrivate));
+
+	object_class->constructor = constructor;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->dispose = dispose;
+
+	parent_class->check_connection_compatible = check_connection_compatible;
+	parent_class->can_auto_connect = can_auto_connect;
+	parent_class->complete_connection = complete_connection;
+
+	parent_class->is_available = is_available;
+	parent_class->act_stage1_prepare = act_stage1_prepare;
+	parent_class->act_stage2_config = act_stage2_config;
+
+	parent_class->state_changed = state_changed;
+
+	/* Properties */
+	g_object_class_install_property
+		(object_class, PROP_COMPANION,
+		 g_param_spec_boxed (NM_DEVICE_OLPC_MESH_COMPANION,
+		                     "Companion device",
+		                     "Companion device object path",
+		                     DBUS_TYPE_G_OBJECT_PATH,
+		                     G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE_CHANNEL,
+		 g_param_spec_uint (NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL,
+		                   "Active channel",
+		                   "Active channel",
+		                   0, G_MAXUINT32, 0,
+		                   G_PARAM_READABLE));
+
+	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
+	                                        G_TYPE_FROM_CLASS (klass),
+	                                        &dbus_glib_nm_device_olpc_mesh_object_info);
+
+	dbus_g_error_domain_register (NM_OLPC_MESH_ERROR, NULL, NM_TYPE_OLPC_MESH_ERROR);
+}
+
diff --git a/src/devices/wifi/nm-device-olpc-mesh.h b/src/devices/wifi/nm-device-olpc-mesh.h
new file mode 100644
index 00000000..c25dd8e5
--- /dev/null
+++ b/src/devices/wifi/nm-device-olpc-mesh.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+
+/* NetworkManager -- Network link manager
+ *
+ * Dan Williams <dcbw@redhat.com>
+ * Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ * Daniel Drake <dsd@laptop.org>
+ *
+ * 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.
+ *
+ * (C) Copyright 2005 Red Hat, Inc.
+ * (C) Copyright 2008 Collabora Ltd.
+ * (C) Copyright 2009 One Laptop per Child
+ */
+
+#ifndef NM_DEVICE_OLPC_MESH_H
+#define NM_DEVICE_OLPC_MESH_H
+
+#include <glib-object.h>
+#include <dbus/dbus.h>
+
+#include "nm-device.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_OLPC_MESH            (nm_device_olpc_mesh_get_type ())
+#define NM_DEVICE_OLPC_MESH(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMesh))
+#define NM_DEVICE_OLPC_MESH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass))
+#define NM_IS_DEVICE_OLPC_MESH(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_OLPC_MESH))
+#define NM_IS_DEVICE_OLPC_MESH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_OLPC_MESH))
+#define NM_DEVICE_OLPC_MESH_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_OLPC_MESH, NMDeviceOlpcMeshClass))
+
+typedef enum
+{
+	NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH = 0, /*< nick=ConnectionNotMesh >*/
+	NM_OLPC_MESH_ERROR_CONNECTION_INVALID,      /*< nick=ConnectionInvalid >*/
+	NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE, /*< nick=ConnectionIncompatible >*/
+} NMOlpcMeshError;
+
+#define NM_DEVICE_OLPC_MESH_COMPANION      "companion"
+#define NM_DEVICE_OLPC_MESH_BITRATE        "bitrate"
+#define NM_DEVICE_OLPC_MESH_ACTIVE_CHANNEL "active-channel"
+
+#ifndef NM_DEVICE_OLPC_MESH_DEFINED
+#define NM_DEVICE_OLPC_MESH_DEFINED
+typedef struct _NMDeviceOlpcMesh NMDeviceOlpcMesh;
+#endif
+
+typedef struct _NMDeviceOlpcMeshClass NMDeviceOlpcMeshClass;
+typedef struct _NMDeviceOlpcMeshPrivate NMDeviceOlpcMeshPrivate;
+
+struct _NMDeviceOlpcMesh
+{
+	NMDevice parent;
+};
+
+struct _NMDeviceOlpcMeshClass
+{
+	NMDeviceClass parent;
+
+};
+
+
+GType nm_device_olpc_mesh_get_type (void);
+
+NMDevice *nm_device_olpc_mesh_new (NMPlatformLink *platform_device);
+
+G_END_DECLS
+
+#endif  /* NM_DEVICE_OLPC_MESH_H */
diff --git a/src/devices/wifi/nm-device-wifi-glue.h b/src/devices/wifi/nm-device-wifi-glue.h
new file mode 100644
index 00000000..3d0f63db
--- /dev/null
+++ b/src/devices/wifi/nm-device-wifi-glue.h
@@ -0,0 +1,167 @@
+/* Generated by dbus-binding-tool; do not edit! */
+
+
+#ifndef __dbus_glib_marshal_nm_device_wifi_MARSHAL_H__
+#define __dbus_glib_marshal_nm_device_wifi_MARSHAL_H__
+
+#include	<glib-object.h>
+
+G_BEGIN_DECLS
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_schar (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#define g_marshal_value_peek_variant(v)  g_value_get_variant (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_variant(v)  (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* BOOLEAN:POINTER,POINTER */
+extern void dbus_glib_marshal_nm_device_wifi_BOOLEAN__POINTER_POINTER (GClosure     *closure,
+                                                                       GValue       *return_value,
+                                                                       guint         n_param_values,
+                                                                       const GValue *param_values,
+                                                                       gpointer      invocation_hint,
+                                                                       gpointer      marshal_data);
+void
+dbus_glib_marshal_nm_device_wifi_BOOLEAN__POINTER_POINTER (GClosure     *closure,
+                                                           GValue       *return_value G_GNUC_UNUSED,
+                                                           guint         n_param_values,
+                                                           const GValue *param_values,
+                                                           gpointer      invocation_hint G_GNUC_UNUSED,
+                                                           gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER_POINTER) (gpointer     data1,
+                                                             gpointer     arg_1,
+                                                             gpointer     arg_2,
+                                                             gpointer     data2);
+  register GMarshalFunc_BOOLEAN__POINTER_POINTER callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_BOOLEAN__POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_pointer (param_values + 1),
+                       g_marshal_value_peek_pointer (param_values + 2),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
+/* NONE:BOXED,POINTER */
+extern void dbus_glib_marshal_nm_device_wifi_VOID__BOXED_POINTER (GClosure     *closure,
+                                                                  GValue       *return_value,
+                                                                  guint         n_param_values,
+                                                                  const GValue *param_values,
+                                                                  gpointer      invocation_hint,
+                                                                  gpointer      marshal_data);
+void
+dbus_glib_marshal_nm_device_wifi_VOID__BOXED_POINTER (GClosure     *closure,
+                                                      GValue       *return_value G_GNUC_UNUSED,
+                                                      guint         n_param_values,
+                                                      const GValue *param_values,
+                                                      gpointer      invocation_hint G_GNUC_UNUSED,
+                                                      gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__BOXED_POINTER) (gpointer     data1,
+                                                    gpointer     arg_1,
+                                                    gpointer     arg_2,
+                                                    gpointer     data2);
+  register GMarshalFunc_VOID__BOXED_POINTER callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 3);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__BOXED_POINTER) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_boxed (param_values + 1),
+            g_marshal_value_peek_pointer (param_values + 2),
+            data2);
+}
+#define dbus_glib_marshal_nm_device_wifi_NONE__BOXED_POINTER	dbus_glib_marshal_nm_device_wifi_VOID__BOXED_POINTER
+
+G_END_DECLS
+
+#endif /* __dbus_glib_marshal_nm_device_wifi_MARSHAL_H__ */
+
+#include <dbus/dbus-glib.h>
+static const DBusGMethodInfo dbus_glib_nm_device_wifi_methods[] = {
+  { (GCallback) impl_device_get_access_points, dbus_glib_marshal_nm_device_wifi_BOOLEAN__POINTER_POINTER, 0 },
+  { (GCallback) impl_device_get_all_access_points, dbus_glib_marshal_nm_device_wifi_BOOLEAN__POINTER_POINTER, 89 },
+  { (GCallback) impl_device_request_scan, dbus_glib_marshal_nm_device_wifi_NONE__BOXED_POINTER, 181 },
+};
+
+const DBusGObjectInfo dbus_glib_nm_device_wifi_object_info = {  1,
+  dbus_glib_nm_device_wifi_methods,
+  3,
+"org.freedesktop.NetworkManager.Device.Wireless\0GetAccessPoints\0S\0access_points\0O\0F\0N\0ao\0\0org.freedesktop.NetworkManager.Device.Wireless\0GetAllAccessPoints\0S\0access_points\0O\0F\0N\0ao\0\0org.freedesktop.NetworkManager.Device.Wireless\0RequestScan\0A\0options\0I\0a{sv}\0\0\0",
+"org.freedesktop.NetworkManager.Device.Wireless\0PropertiesChanged\0org.freedesktop.NetworkManager.Device.Wireless\0AccessPointAdded\0org.freedesktop.NetworkManager.Device.Wireless\0AccessPointRemoved\0\0",
+"org.freedesktop.NetworkManager.Device.Wireless\0HwAddress\0hw_address\0read\0org.freedesktop.NetworkManager.Device.Wireless\0PermHwAddress\0perm_hw_address\0read\0org.freedesktop.NetworkManager.Device.Wireless\0Mode\0mode\0read\0org.freedesktop.NetworkManager.Device.Wireless\0Bitrate\0bitrate\0read\0org.freedesktop.NetworkManager.Device.Wireless\0AccessPoints\0access_points\0read\0org.freedesktop.NetworkManager.Device.Wireless\0ActiveAccessPoint\0active_access_point\0read\0org.freedesktop.NetworkManager.Device.Wireless\0WirelessCapabilities\0wireless_capabilities\0read\0\0"
+};
+
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
new file mode 100644
index 00000000..95173cf4
--- /dev/null
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -0,0 +1,3502 @@
+/* -*- 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) 2005 - 2012 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <net/ethernet.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#include <linux/sockios.h>
+#include <linux/ethtool.h>
+#include <sys/ioctl.h>
+#include <netinet/ether.h>
+#include <errno.h>
+
+#include "nm-glib-compat.h"
+#include "nm-dbus-manager.h"
+#include "nm-device.h"
+#include "nm-device-wifi.h"
+#include "nm-device-private.h"
+#include "nm-utils.h"
+#include "nm-logging.h"
+#include "NetworkManagerUtils.h"
+#include "nm-activation-request.h"
+#include "nm-supplicant-manager.h"
+#include "nm-supplicant-interface.h"
+#include "nm-supplicant-config.h"
+#include "nm-setting-connection.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-8021x.h"
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-ip6-config.h"
+#include "nm-platform.h"
+#include "nm-manager-auth.h"
+#include "nm-settings-connection.h"
+#include "nm-enum-types.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-wifi-enum-types.h"
+#include "nm-connection-provider.h"
+
+
+static gboolean impl_device_get_access_points (NMDeviceWifi *device,
+                                               GPtrArray **aps,
+                                               GError **err);
+
+static gboolean impl_device_get_all_access_points (NMDeviceWifi *device,
+                                                   GPtrArray **aps,
+                                                   GError **err);
+
+static void impl_device_request_scan (NMDeviceWifi *device,
+                                      GHashTable *options,
+                                      DBusGMethodInvocation *context);
+
+#include "nm-device-wifi-glue.h"
+
+
+/* All of these are in seconds */
+#define SCAN_INTERVAL_MIN 3
+#define SCAN_INTERVAL_STEP 20
+#define SCAN_INTERVAL_MAX 120
+
+#define WIRELESS_SECRETS_TRIES "wireless-secrets-tries"
+
+G_DEFINE_TYPE (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE)
+
+#define NM_DEVICE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_WIFI, NMDeviceWifiPrivate))
+
+
+enum {
+	PROP_0,
+	PROP_PERM_HW_ADDRESS,
+	PROP_MODE,
+	PROP_BITRATE,
+	PROP_ACCESS_POINTS,
+	PROP_ACTIVE_ACCESS_POINT,
+	PROP_CAPABILITIES,
+	PROP_SCANNING,
+
+	LAST_PROP
+};
+
+enum {
+	ACCESS_POINT_ADDED,
+	ACCESS_POINT_REMOVED,
+	SCANNING_ALLOWED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _NMDeviceWifiPrivate {
+	gboolean          disposed;
+
+	guint8            perm_hw_addr[ETH_ALEN];    /* Permanent MAC address */
+	guint8            initial_hw_addr[ETH_ALEN]; /* Initial MAC address (as seen when NM starts) */
+
+	gint8             invalid_strength_counter;
+
+	GSList *          ap_list;
+	NMAccessPoint *   current_ap;
+	guint32           rate;
+	gboolean          enabled; /* rfkilled or not */
+
+	gint32            scheduled_scan_time;
+	guint8            scan_interval; /* seconds */
+	guint             pending_scan_id;
+	guint             scanlist_cull_id;
+	gboolean          requested_scan;
+
+	NMSupplicantManager   *sup_mgr;
+	NMSupplicantInterface *sup_iface;
+	guint                  sup_timeout_id; /* supplicant association timeout */
+
+	gboolean          ssid_found;
+	NM80211Mode       mode;
+
+	guint32           failed_link_count;
+	guint             periodic_source_id;
+	guint             link_timeout_id;
+
+	NMDeviceWifiCapabilities capabilities;
+};
+
+static gboolean check_scanning_allowed (NMDeviceWifi *self);
+
+static void schedule_scan (NMDeviceWifi *self, gboolean backoff);
+
+static void cancel_pending_scan (NMDeviceWifi *self);
+
+static void cleanup_association_attempt (NMDeviceWifi * self,
+                                         gboolean disconnect);
+
+static void supplicant_iface_state_cb (NMSupplicantInterface *iface,
+                                       guint32 new_state,
+                                       guint32 old_state,
+                                       int disconnect_reason,
+                                       gpointer user_data);
+
+static void supplicant_iface_new_bss_cb (NMSupplicantInterface * iface,
+                                         const char *object_path,
+                                         GHashTable *properties,
+                                         NMDeviceWifi * self);
+
+static void supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
+                                             const char *object_path,
+                                             GHashTable *properties,
+                                             NMDeviceWifi *self);
+
+static void supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
+                                             const char *object_path,
+                                             NMDeviceWifi *self);
+
+static void supplicant_iface_scan_done_cb (NMSupplicantInterface * iface,
+                                           gboolean success,
+                                           NMDeviceWifi * self);
+
+static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface,
+                                                 GParamSpec * pspec,
+                                                 NMDeviceWifi * self);
+
+static void schedule_scanlist_cull (NMDeviceWifi *self);
+
+static gboolean request_wireless_scan (gpointer user_data);
+
+static void remove_access_point (NMDeviceWifi *device, NMAccessPoint *ap);
+
+static void remove_supplicant_interface_error_handler (NMDeviceWifi *self);
+
+/*****************************************************************/
+
+#define NM_WIFI_ERROR (nm_wifi_error_quark ())
+
+static GQuark
+nm_wifi_error_quark (void)
+{
+	static GQuark quark = 0;
+	if (!quark)
+		quark = g_quark_from_static_string ("nm-wifi-error");
+	return quark;
+}
+
+/*****************************************************************/
+
+static GObject*
+constructor (GType type,
+             guint n_construct_params,
+             GObjectConstructParam *construct_params)
+{
+	GObject *object;
+	GObjectClass *klass;
+	NMDeviceWifi *self;
+	NMDeviceWifiPrivate *priv;
+
+	klass = G_OBJECT_CLASS (nm_device_wifi_parent_class);
+	object = klass->constructor (type, n_construct_params, construct_params);
+	if (!object)
+		return NULL;
+
+	self = NM_DEVICE_WIFI (object);
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	nm_log_dbg (LOGD_HW | LOGD_WIFI, "(%s): kernel ifindex %d",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            nm_device_get_ifindex (NM_DEVICE (self)));
+
+	if (!nm_platform_wifi_get_capabilities (nm_device_get_ifindex (NM_DEVICE (self)),
+	                                        &priv->capabilities)) {
+		nm_log_warn (LOGD_HW | LOGD_WIFI, "(%s): failed to initialize WiFi driver",
+		             nm_device_get_iface (NM_DEVICE (self)));
+		g_object_unref (object);
+		return NULL;
+	}
+
+	if (priv->capabilities & NM_WIFI_DEVICE_CAP_AP) {
+		nm_log_info (LOGD_HW | LOGD_WIFI, "(%s): driver supports Access Point (AP) mode",
+		             nm_device_get_iface (NM_DEVICE (self)));
+	}
+
+	/* Connect to the supplicant manager */
+	priv->sup_mgr = nm_supplicant_manager_get ();
+	g_assert (priv->sup_mgr);
+
+	return object;
+}
+
+static gboolean
+supplicant_interface_acquire (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	g_return_val_if_fail (self != NULL, FALSE);
+	/* interface already acquired? */
+	g_return_val_if_fail (priv->sup_iface == NULL, TRUE);
+
+	priv->sup_iface = nm_supplicant_manager_iface_get (priv->sup_mgr,
+	                                                   nm_device_get_iface (NM_DEVICE (self)),
+	                                                   TRUE);
+	if (priv->sup_iface == NULL) {
+		nm_log_err (LOGD_WIFI, "Couldn't initialize supplicant interface for %s.",
+		            nm_device_get_iface (NM_DEVICE (self)));
+		return FALSE;
+	}
+
+	if (nm_supplicant_interface_get_state (priv->sup_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY)
+		nm_device_add_pending_action (NM_DEVICE (self), "waiting for supplicant", TRUE);
+
+	g_signal_connect (priv->sup_iface,
+	                  NM_SUPPLICANT_INTERFACE_STATE,
+	                  G_CALLBACK (supplicant_iface_state_cb),
+	                  self);
+	g_signal_connect (priv->sup_iface,
+	                  NM_SUPPLICANT_INTERFACE_NEW_BSS,
+	                  G_CALLBACK (supplicant_iface_new_bss_cb),
+	                  self);
+	g_signal_connect (priv->sup_iface,
+	                  NM_SUPPLICANT_INTERFACE_BSS_UPDATED,
+	                  G_CALLBACK (supplicant_iface_bss_updated_cb),
+	                  self);
+	g_signal_connect (priv->sup_iface,
+	                  NM_SUPPLICANT_INTERFACE_BSS_REMOVED,
+	                  G_CALLBACK (supplicant_iface_bss_removed_cb),
+	                  self);
+	g_signal_connect (priv->sup_iface,
+	                  NM_SUPPLICANT_INTERFACE_SCAN_DONE,
+	                  G_CALLBACK (supplicant_iface_scan_done_cb),
+	                  self);
+	g_signal_connect (priv->sup_iface,
+	                  "notify::scanning",
+	                  G_CALLBACK (supplicant_iface_notify_scanning_cb),
+	                  self);
+
+	return TRUE;
+}
+
+static void
+supplicant_interface_release (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv;
+
+	g_return_if_fail (self != NULL);
+
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	cancel_pending_scan (self);
+
+	/* Reset the scan interval to be pretty frequent when disconnected */
+	priv->scan_interval = SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP;
+	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): reset scanning interval to %d seconds",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            priv->scan_interval);
+
+	if (priv->scanlist_cull_id) {
+		g_source_remove (priv->scanlist_cull_id);
+		priv->scanlist_cull_id = 0;
+	}
+
+	if (priv->sup_iface) {
+		remove_supplicant_interface_error_handler (self);
+
+		/* Clear supplicant interface signal handlers */
+		g_signal_handlers_disconnect_by_data (priv->sup_iface, self);
+
+		/* Tell the supplicant to disconnect from the current AP */
+		nm_supplicant_interface_disconnect (priv->sup_iface);
+
+		nm_supplicant_manager_iface_release (priv->sup_mgr, priv->sup_iface);
+		priv->sup_iface = NULL;
+	}
+}
+
+static NMAccessPoint *
+get_ap_by_path (NMDeviceWifi *self, const char *path)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	GSList *iter;
+
+	if (!path)
+		return NULL;
+
+	for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
+		if (g_strcmp0 (path, nm_ap_get_dbus_path (NM_AP (iter->data))) == 0)
+			return NM_AP (iter->data);
+	}
+	return NULL;
+}
+
+static NMAccessPoint *
+get_ap_by_supplicant_path (NMDeviceWifi *self, const char *path)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	GSList *iter;
+
+	if (!path)
+		return NULL;
+
+	for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
+		if (g_strcmp0 (path, nm_ap_get_supplicant_path (NM_AP (iter->data))) == 0)
+			return NM_AP (iter->data);
+	}
+	return NULL;
+}
+
+static NMAccessPoint *
+find_active_ap (NMDeviceWifi *self,
+                NMAccessPoint *ignore_ap,
+                gboolean match_hidden)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	const char *iface = nm_device_get_iface (NM_DEVICE (self));
+	int ifindex = nm_device_get_ifindex (NM_DEVICE (self));
+	struct ether_addr bssid;
+	GByteArray *ssid;
+	GSList *iter;
+	int i = 0;
+	NMAccessPoint *match_nofreq = NULL, *active_ap = NULL;
+	gboolean found_a_band = FALSE;
+	gboolean found_bg_band = FALSE;
+	NM80211Mode devmode;
+	guint32 devfreq;
+
+	nm_platform_wifi_get_bssid (ifindex, &bssid);
+	nm_log_dbg (LOGD_WIFI, "(%s): active BSSID: %02x:%02x:%02x:%02x:%02x:%02x",
+	            iface,
+	            bssid.ether_addr_octet[0], bssid.ether_addr_octet[1],
+	            bssid.ether_addr_octet[2], bssid.ether_addr_octet[3],
+	            bssid.ether_addr_octet[4], bssid.ether_addr_octet[5]);
+
+	if (!nm_ethernet_address_is_valid (&bssid))
+		return NULL;
+
+	ssid = nm_platform_wifi_get_ssid (ifindex);
+	nm_log_dbg (LOGD_WIFI, "(%s): active SSID: %s%s%s",
+	            iface,
+	            ssid ? "'" : "",
+	            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
+	            ssid ? "'" : "");
+
+	devmode = nm_platform_wifi_get_mode (ifindex);
+	devfreq = nm_platform_wifi_get_frequency (ifindex);
+
+	/* When matching hidden APs, do a second pass that ignores the SSID check,
+	 * because NM might not yet know the SSID of the hidden AP in the scan list
+	 * and therefore it won't get matched the first time around.
+	 */
+	while (i++ < (match_hidden ? 2 : 1)) {
+		nm_log_dbg (LOGD_WIFI, "  Pass #%d %s", i, i > 1 ? "(ignoring SSID)" : "");
+
+		/* Find this SSID + BSSID in the device's AP list */
+		for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
+			NMAccessPoint *ap = NM_AP (iter->data);
+			const struct ether_addr	*ap_bssid = nm_ap_get_address (ap);
+			const GByteArray *ap_ssid = nm_ap_get_ssid (ap);
+			NM80211Mode apmode;
+			guint32 apfreq;
+
+			nm_log_dbg (LOGD_WIFI, "    AP: %s%s%s  %02x:%02x:%02x:%02x:%02x:%02x",
+			            ap_ssid ? "'" : "",
+			            ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)",
+			            ap_ssid ? "'" : "",
+			            ap_bssid->ether_addr_octet[0], ap_bssid->ether_addr_octet[1],
+			            ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3],
+			            ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]);
+
+			if (ap == ignore_ap) {
+				nm_log_dbg (LOGD_WIFI, "      ignored");
+				continue;
+			}
+
+			if (memcmp (bssid.ether_addr_octet, ap_bssid->ether_addr_octet, ETH_ALEN)) {
+				nm_log_dbg (LOGD_WIFI, "      BSSID mismatch");
+				continue;
+			}
+
+			if ((i == 0) && !nm_utils_same_ssid (ssid, ap_ssid, TRUE)) {
+				nm_log_dbg (LOGD_WIFI, "      SSID mismatch");
+				continue;
+			}
+
+			apmode = nm_ap_get_mode (ap);
+			if (devmode != apmode) {
+				nm_log_dbg (LOGD_WIFI, "      mode mismatch (device %d, ap %d)",
+				            devmode, apmode);
+				continue;
+			}
+
+			apfreq = nm_ap_get_freq (ap);
+			if (devfreq != apfreq) {
+				nm_log_dbg (LOGD_WIFI, "      frequency mismatch (device %u, ap %u)",
+				            devfreq, apfreq);
+
+				if (match_nofreq == NULL)
+					match_nofreq = ap;
+
+				if (apfreq > 4000)
+					found_a_band = TRUE;
+				else if (apfreq > 2000)
+					found_bg_band = TRUE;
+				continue;
+			}
+
+			// FIXME: handle security settings here too
+			nm_log_dbg (LOGD_WIFI, "      matched");
+			active_ap = ap;
+			goto done;
+		}
+	}
+
+	/* Some proprietary drivers (wl.o) report tuned frequency (like when
+	 * scanning) instead of the associated AP's frequency.  This is a great
+	 * example of how WEXT is underspecified.  We use frequency to find the
+	 * active AP in the scan list because some configurations use the same
+	 * SSID/BSSID on the 2GHz and 5GHz bands simultaneously, and we need to
+	 * make sure we get the right AP in the right band.  This configuration
+	 * is uncommon though, and the frequency check penalizes closed drivers we
+	 * can't fix.  Because we're not total dicks, ignore the frequency condition
+	 * if the associated BSSID/SSID exists only in one band since that's most
+	 * likely the AP we want.  Sometimes wl.o returns a frequency of 0, so if
+	 * we can't match the AP based on frequency at all, just give up.
+	 */
+	if (match_nofreq && ((found_a_band != found_bg_band) || (devfreq == 0))) {
+		const struct ether_addr	*ap_bssid = nm_ap_get_address (match_nofreq);
+		const GByteArray *ap_ssid = nm_ap_get_ssid (match_nofreq);
+
+		nm_log_dbg (LOGD_WIFI, "    matched %s%s%s  %02x:%02x:%02x:%02x:%02x:%02x",
+		            ap_ssid ? "'" : "",
+		            ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)",
+		            ap_ssid ? "'" : "",
+		            ap_bssid->ether_addr_octet[0], ap_bssid->ether_addr_octet[1],
+		            ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3],
+		            ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]);
+
+		active_ap = match_nofreq;
+		goto done;
+	}
+
+	nm_log_dbg (LOGD_WIFI, "  No matching AP found.");
+
+done:
+	if (ssid)
+		g_byte_array_free (ssid, TRUE);
+	return active_ap;
+}
+
+static void
+update_seen_bssids_cache (NMDeviceWifi *self, NMAccessPoint *ap)
+{
+	NMConnection *connection;
+
+	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
+
+	if (ap == NULL)
+		return;
+
+	/* Don't cache the BSSID for Ad-Hoc APs */
+	if (nm_ap_get_mode (ap) != NM_802_11_MODE_INFRA)
+		return;
+
+	if (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED) {
+		connection = nm_device_get_connection (NM_DEVICE (self));
+		if (connection) {
+			nm_settings_connection_add_seen_bssid (NM_SETTINGS_CONNECTION (connection),
+			                                       nm_ap_get_address (ap));
+		}
+	}
+}
+
+static void
+set_current_ap (NMDeviceWifi *self, NMAccessPoint *new_ap, gboolean recheck_available_connections, gboolean force_remove_old_ap)
+{
+	NMDeviceWifiPrivate *priv;
+	NMAccessPoint *old_ap;
+
+	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
+
+	priv = NM_DEVICE_WIFI_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);
+
+		/* Move the current AP to the front of the scan list.  Since we
+		 * do a lot of searches looking for the current AP, it saves
+		 * time to have it in front.
+		 */
+		priv->ap_list = g_slist_remove (priv->ap_list, new_ap);
+		priv->ap_list = g_slist_prepend (priv->ap_list, new_ap);
+
+		/* Update seen BSSIDs cache */
+		update_seen_bssids_cache (self, priv->current_ap);
+	} else
+		priv->current_ap = NULL;
+
+	if (old_ap) {
+		NM80211Mode mode = nm_ap_get_mode (old_ap);
+
+		if (force_remove_old_ap || mode == NM_802_11_MODE_ADHOC || mode == NM_802_11_MODE_AP || nm_ap_get_fake (old_ap)) {
+			remove_access_point (self, old_ap);
+			if (recheck_available_connections)
+				nm_device_recheck_available_connections (NM_DEVICE (self));
+		}
+		g_object_unref (old_ap);
+	}
+
+	g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT);
+}
+
+static void
+periodic_update (NMDeviceWifi *self, NMAccessPoint *ignore_ap)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	int ifindex = nm_device_get_ifindex (NM_DEVICE (self));
+	NMAccessPoint *new_ap;
+	guint32 new_rate;
+	int percent;
+	NMDeviceState state;
+	guint32 supplicant_state;
+
+	/* BSSID and signal strength have meaningful values only if the device
+	 * is activated and not scanning.
+	 */
+	state = nm_device_get_state (NM_DEVICE (self));
+	if (state != NM_DEVICE_STATE_ACTIVATED)
+		return;
+
+	/* Only update current AP if we're actually talking to something, otherwise
+	 * assume the old one (if any) is still valid until we're told otherwise or
+	 * the connection fails.
+	 */
+	supplicant_state = nm_supplicant_interface_get_state (priv->sup_iface);
+	if (   supplicant_state < NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING
+	    || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED
+	    || nm_supplicant_interface_get_scanning (priv->sup_iface))
+		return;
+
+	/* In AP mode we currently have nothing to do. */
+	if (priv->mode == NM_802_11_MODE_AP)
+		return;
+
+	/* In IBSS mode, most newer firmware/drivers do "BSS coalescing" where
+	 * multiple IBSS stations using the same SSID will eventually switch to
+	 * using the same BSSID to avoid network segmentation.  When this happens,
+	 * the card's reported BSSID will change, but the new BSS may not
+	 * be in the scan list, since scanning isn't done in ad-hoc mode for
+	 * various reasons.  So pull the BSSID from the card and update the
+	 * current AP with it, if the current AP is adhoc.
+	 */
+	if (priv->current_ap && (nm_ap_get_mode (priv->current_ap) == NM_802_11_MODE_ADHOC)) {
+		struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} };
+
+		nm_platform_wifi_get_bssid (ifindex, &bssid);
+		/* 0x02 means "locally administered" and should be OR-ed into
+		 * the first byte of IBSS BSSIDs.
+		 */
+		if (   (bssid.ether_addr_octet[0] & 0x02)
+		    && nm_ethernet_address_is_valid (&bssid))
+			nm_ap_set_address (priv->current_ap, &bssid);
+	}
+
+	new_ap = find_active_ap (self, ignore_ap, FALSE);
+	if (new_ap) {
+		/* Try to smooth out the strength.  Atmel cards, for example, will give no strength
+		 * one second and normal strength the next.
+		 */
+		percent = nm_platform_wifi_get_quality (ifindex);
+		if (percent >= 0 || ++priv->invalid_strength_counter > 3) {
+			nm_ap_set_strength (new_ap, (gint8) percent);
+			priv->invalid_strength_counter = 0;
+		}
+	}
+
+	if (new_ap != priv->current_ap) {
+		const struct ether_addr *new_bssid = NULL;
+		const GByteArray *new_ssid = NULL;
+		const struct ether_addr *old_bssid = NULL;
+		const GByteArray *old_ssid = NULL;
+		char *old_addr = NULL, *new_addr = NULL;
+
+		if (new_ap) {
+			new_bssid = nm_ap_get_address (new_ap);
+			new_addr = nm_utils_hwaddr_ntoa (new_bssid, ARPHRD_ETHER);
+			new_ssid = nm_ap_get_ssid (new_ap);
+		}
+
+		if (priv->current_ap) {
+			old_bssid = nm_ap_get_address (priv->current_ap);
+			old_addr = nm_utils_hwaddr_ntoa (old_bssid, ARPHRD_ETHER);
+			old_ssid = nm_ap_get_ssid (priv->current_ap);
+		}
+
+		nm_log_info (LOGD_WIFI, "(%s): roamed from BSSID %s (%s) to %s (%s)",
+		             nm_device_get_iface (NM_DEVICE (self)),
+		             old_addr ? old_addr : "(none)",
+		             old_ssid ? nm_utils_escape_ssid (old_ssid->data, old_ssid->len) : "(none)",
+		             new_addr ? new_addr : "(none)",
+		             new_ssid ? nm_utils_escape_ssid (new_ssid->data, new_ssid->len) : "(none)");
+		g_free (old_addr);
+		g_free (new_addr);
+
+		set_current_ap (self, new_ap, TRUE, FALSE);
+	}
+
+	new_rate = nm_platform_wifi_get_rate (ifindex);
+	if (new_rate != priv->rate) {
+		priv->rate = new_rate;
+		g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_BITRATE);
+	}
+}
+
+static gboolean
+periodic_update_cb (gpointer user_data)
+{
+	periodic_update (NM_DEVICE_WIFI (user_data), NULL);
+	return TRUE;
+}
+
+static gboolean
+bring_up (NMDevice *device, gboolean *no_firmware)
+{
+	if (!NM_DEVICE_WIFI_GET_PRIVATE (device)->enabled)
+		return FALSE;
+
+	return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->bring_up (device, no_firmware);
+}
+
+static void
+emit_ap_added_removed (NMDeviceWifi *self,
+                       guint signum,
+                       NMAccessPoint *ap,
+                       gboolean recheck_available_connections)
+{
+	g_signal_emit (self, signals[signum], 0, ap);
+	g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_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
+remove_access_point (NMDeviceWifi *device,
+                     NMAccessPoint *ap)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	g_return_if_fail (ap);
+	g_return_if_fail (ap != priv->current_ap);
+	g_return_if_fail (g_slist_find (priv->ap_list, ap));
+
+	priv->ap_list = g_slist_remove (priv->ap_list, ap);
+	emit_ap_added_removed (self, ACCESS_POINT_REMOVED, ap, FALSE);
+	g_object_unref (ap);
+}
+
+static void
+remove_all_aps (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	if (priv->ap_list) {
+		set_current_ap (self, NULL, FALSE, FALSE);
+
+		while (priv->ap_list)
+			remove_access_point (self, NM_AP (priv->ap_list->data));
+
+		nm_device_recheck_available_connections (NM_DEVICE (self));
+	}
+}
+
+static void
+deactivate (NMDevice *dev)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	int ifindex = nm_device_get_ifindex (dev);
+	NMConnection *connection;
+	NM80211Mode old_mode = priv->mode;
+
+	connection = nm_device_get_connection (dev);
+	if (connection) {
+		/* Clear wireless secrets tries when deactivating */
+		g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL);
+	}
+
+	if (priv->periodic_source_id) {
+		g_source_remove (priv->periodic_source_id);
+		priv->periodic_source_id = 0;
+	}
+
+	cleanup_association_attempt (self, TRUE);
+
+	priv->rate = 0;
+
+	/* If the AP is 'fake', i.e. it wasn't actually found from
+	 * a scan but the user tried to connect to it manually (maybe it
+	 * was non-broadcasting or something) get rid of it, because 'fake'
+	 * APs should only live for as long as we're connected to them.
+	 **/
+	set_current_ap (self, NULL, TRUE, FALSE);
+
+	/* Clear any critical protocol notification in the Wi-Fi stack */
+	nm_platform_wifi_indicate_addressing_running (ifindex, FALSE);
+
+	/* Reset MAC address back to initial address */
+	nm_device_set_hw_addr (dev, priv->initial_hw_addr, "reset", LOGD_WIFI);
+
+	/* Ensure we're in infrastructure mode after deactivation; some devices
+	 * (usually older ones) don't scan well in adhoc mode.
+	 */
+	if (nm_platform_wifi_get_mode (ifindex) != NM_802_11_MODE_INFRA) {
+		nm_device_take_down (NM_DEVICE (self), TRUE);
+		nm_platform_wifi_set_mode (ifindex, NM_802_11_MODE_INFRA);
+		nm_device_bring_up (NM_DEVICE (self), TRUE, NULL);
+	}
+
+	if (priv->mode != NM_802_11_MODE_INFRA) {
+		priv->mode = NM_802_11_MODE_INFRA;
+		g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_MODE);
+	}
+
+	/* Ensure we trigger a scan after deactivating a Hotspot */
+	if (old_mode == NM_802_11_MODE_AP) {
+		cancel_pending_scan (self);
+		request_wireless_scan (self);
+	}
+}
+
+static gboolean
+is_adhoc_wpa (NMConnection *connection)
+{
+	NMSettingWireless *s_wifi;
+	NMSettingWirelessSecurity *s_wsec;
+	const char *mode, *key_mgmt;
+
+	/* The kernel doesn't support Ad-Hoc WPA connections well at this time,
+	 * and turns them into open networks.  It's been this way since at least
+	 * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks.
+	 */
+
+	s_wifi = nm_connection_get_setting_wireless (connection);
+	g_return_val_if_fail (s_wifi != NULL, FALSE);
+
+	mode = nm_setting_wireless_get_mode (s_wifi);
+	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) != 0)
+		return FALSE;
+
+	s_wsec = nm_connection_get_setting_wireless_security (connection);
+	if (!s_wsec)
+		return FALSE;
+
+	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+	if (g_strcmp0 (key_mgmt, "wpa-none") != 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMSettingConnection *s_con;
+	NMSettingWireless *s_wireless;
+	const GByteArray *mac;
+	const GSList *mac_blacklist, *mac_blacklist_iter;
+	const char *mode;
+
+	if (!NM_DEVICE_CLASS (nm_device_wifi_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;
+
+	mac = nm_setting_wireless_get_mac_address (s_wireless);
+	if (mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN))
+		return FALSE;
+
+	/* Check for MAC address blacklist */
+	mac_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
+	for (mac_blacklist_iter = mac_blacklist; mac_blacklist_iter;
+	     mac_blacklist_iter = g_slist_next (mac_blacklist_iter)) {
+		struct ether_addr addr;
+
+		if (!ether_aton_r (mac_blacklist_iter->data, &addr)) {
+			g_warn_if_reached ();
+			continue;
+		}
+
+		if (memcmp (&addr, &priv->perm_hw_addr, ETH_ALEN) == 0)
+			return FALSE;
+	}
+
+	if (is_adhoc_wpa (connection))
+		return FALSE;
+
+	/* Early exit if supplicant or device doesn't support requested mode */
+	mode = nm_setting_wireless_get_mode (s_wireless);
+	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0) {
+		if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_ADHOC))
+			return FALSE;
+	} else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0) {
+		if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_AP))
+			return FALSE;
+
+		if (priv->sup_iface) {
+			if (nm_supplicant_interface_get_ap_support (priv->sup_iface) == AP_SUPPORT_NO)
+				return FALSE;
+		}
+	}
+
+	// FIXME: check channel/freq/band against bands the hardware supports
+	// FIXME: check encryption against device capabilities
+	// FIXME: check bitrate against device capabilities
+
+	return TRUE;
+}
+
+
+static gboolean
+_internal_check_connection_available (NMDevice *device,
+                                      NMConnection *connection,
+                                      const char *specific_object,
+                                      gboolean ignore_ap_list)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
+	NMSettingWireless *s_wifi;
+	const char *mode;
+	GSList *ap_iter = NULL;
+
+	s_wifi = nm_connection_get_setting_wireless (connection);
+	g_return_val_if_fail (s_wifi, FALSE);
+
+	if (specific_object) {
+		NMAccessPoint *ap;
+
+		ap = get_ap_by_path (NM_DEVICE_WIFI (device), specific_object);
+		return ap ? nm_ap_check_compatible (ap, connection) : FALSE;
+	}
+
+	/* Ad-Hoc and AP connections are always available because they may be
+	 * started at any time.
+	 */
+	mode = nm_setting_wireless_get_mode (s_wifi);
+	if (   g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0
+	    || g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0)
+		return TRUE;
+
+	/* Hidden SSIDs obviously don't always appear in the scan list either */
+	if (nm_setting_wireless_get_hidden (s_wifi) || ignore_ap_list)
+		return TRUE;
+
+	/* check if its visible */
+	for (ap_iter = priv->ap_list; ap_iter; ap_iter = g_slist_next (ap_iter)) {
+		if (nm_ap_check_compatible (NM_AP (ap_iter->data), connection))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+check_connection_available (NMDevice *device,
+                            NMConnection *connection,
+                            const char *specific_object)
+{
+	return _internal_check_connection_available (device, connection, specific_object, FALSE);
+}
+
+/* FIXME: remove this function when we require the 'hidden' property to be
+ * set before a hidden connection can be activated.
+ */
+static gboolean
+check_connection_available_wifi_hidden (NMDevice *device,
+                                        NMConnection *connection)
+{
+	return _internal_check_connection_available (device, connection, NULL, TRUE);
+}
+
+/*
+ * 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",
+};
+
+#define ARRAY_SIZE(a)  (sizeof (a) / sizeof (a[0]))
+
+static gboolean
+is_manf_default_ssid (const GByteArray *ssid)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE (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)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMSettingWireless *s_wifi;
+	NMSettingWirelessSecurity *s_wsec;
+	NMSetting8021x *s_8021x;
+	const GByteArray *setting_mac;
+	char *format, *str_ssid = NULL;
+	NMAccessPoint *ap = NULL;
+	const GByteArray *ssid = NULL;
+	GSList *iter;
+	gboolean hidden = FALSE;
+
+	s_wifi = nm_connection_get_setting_wireless (connection);
+	s_wsec = nm_connection_get_setting_wireless_security (connection);
+	s_8021x = nm_connection_get_setting_802_1x (connection);
+
+	if (!specific_object) {
+		/* If not given a specific object, we need at minimum an SSID */
+		if (!s_wifi) {
+			g_set_error_literal (error,
+			                     NM_WIFI_ERROR,
+			                     NM_WIFI_ERROR_CONNECTION_INVALID,
+			                     "A 'wireless' setting is required if no AP path was given.");
+			return FALSE;
+		}
+
+		ssid = nm_setting_wireless_get_ssid (s_wifi);
+		if (!ssid || !ssid->len) {
+			g_set_error_literal (error,
+			                     NM_WIFI_ERROR,
+			                     NM_WIFI_ERROR_CONNECTION_INVALID,
+			                     "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 */
+		for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
+			if (nm_ap_check_compatible (NM_AP (iter->data), connection)) {
+				ap = NM_AP (iter->data);
+				break;
+			}
+		}
+
+		/* If we still don't have an AP, then the WiFI settings needs to be
+		 * fully specified by the client.  Might not be able to find an AP
+		 * if the network isn't broadcasting the SSID for example.
+		 */
+		if (!ap) {
+			GSList *settings = NULL;
+			gboolean valid;
+
+			settings = g_slist_prepend (settings, s_wifi);
+			if (s_wsec)
+				settings = g_slist_prepend (settings, s_wsec);
+			if (s_8021x)
+				settings = g_slist_prepend (settings, s_8021x);
+			valid = nm_setting_verify (NM_SETTING (s_wifi), settings, error);
+			g_slist_free (settings);
+			if (!valid)
+				return FALSE;
+
+			hidden = TRUE;
+		}
+	} else {
+		ap = get_ap_by_path (self, specific_object);
+		if (!ap) {
+			g_set_error (error,
+			             NM_WIFI_ERROR,
+			             NM_WIFI_ERROR_ACCESS_POINT_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));
+	}
+
+	if (ap) {
+		ssid = nm_ap_get_ssid (ap);
+
+		if (ssid == NULL) {
+			/* The AP must be hidden.  Connecting to a WiFi AP requires the SSID
+			 * as part of the initial handshake, so check the connection details
+			 * for the SSID.  The AP object will still be used for encryption
+			 * settings and such.
+			 */
+			ssid = nm_setting_wireless_get_ssid (s_wifi);
+		}
+
+		if (ssid == NULL) {
+			/* If there's no SSID on the AP itself, and no SSID in the
+			 * connection data, then we cannot connect at all.  Return an error.
+			 */
+			g_set_error_literal (error,
+			                     NM_WIFI_ERROR,
+			                     NM_WIFI_ERROR_CONNECTION_INVALID,
+			                     "A 'wireless' setting with a valid SSID is required for hidden access points.");
+			return FALSE;
+		}
+
+		/* If the SSID is a well-known SSID, lock the connection to the AP's
+		 * specific BSSID so NM doesn't autoconnect to some random wifi net.
+		 */
+		if (!nm_ap_complete_connection (ap,
+		                                connection,
+		                                is_manf_default_ssid (ssid),
+		                                error))
+			return FALSE;
+	}
+
+	/* The kernel doesn't support Ad-Hoc WPA connections well at this time,
+	 * and turns them into open networks.  It's been this way since at least
+	 * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks.
+	 */
+	if (is_adhoc_wpa (connection)) {
+		g_set_error_literal (error,
+		                     NM_SETTING_WIRELESS_ERROR,
+		                     NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+		                     "WPA Ad-Hoc disabled due to kernel bugs");
+		return FALSE;
+	}
+
+	g_assert (ssid);
+	str_ssid = nm_utils_ssid_to_utf8 (ssid);
+	format = g_strdup_printf ("%s %%d", str_ssid);
+
+	nm_utils_complete_generic (connection,
+	                           NM_SETTING_WIRELESS_SETTING_NAME,
+	                           existing_connections,
+	                           format,
+	                           str_ssid,
+	                           TRUE);
+	g_free (str_ssid);
+	g_free (format);
+
+	if (hidden)
+		g_object_set (s_wifi, NM_SETTING_WIRELESS_HIDDEN, TRUE, NULL);
+
+	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 (memcmp (setting_mac->data, priv->perm_hw_addr, ETH_ALEN)) {
+			g_set_error (error,
+			             NM_SETTING_WIRELESS_ERROR,
+			             NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_WIRELESS_MAC_ADDRESS);
+			return FALSE;
+		}
+	} else {
+		GByteArray *mac;
+		const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+
+		/* Lock the connection to this device by default if it uses a
+		 * permanent MAC address (ie not a 'locally administered' one)
+		 */
+		if (   !(priv->perm_hw_addr[0] & 0x02)
+		    && memcmp (priv->perm_hw_addr, null_mac, ETH_ALEN)) {
+			mac = g_byte_array_sized_new (ETH_ALEN);
+			g_byte_array_append (mac, priv->perm_hw_addr, ETH_ALEN);
+			g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MAC_ADDRESS, mac, NULL);
+			g_byte_array_free (mac, TRUE);
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
+is_available (NMDevice *dev)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMSupplicantInterface *sup_iface;
+	guint32 state;
+
+	if (!priv->enabled) {
+		nm_log_dbg (LOGD_WIFI, "(%s): not available because not enabled",
+		            nm_device_get_iface (dev));
+		return FALSE;
+	}
+
+	sup_iface = priv->sup_iface;
+	if (!sup_iface) {
+		nm_log_dbg (LOGD_WIFI, "(%s): not available because supplicant not running",
+		            nm_device_get_iface (dev));
+		return FALSE;
+	}
+
+	state = nm_supplicant_interface_get_state (sup_iface);
+	if (   state < NM_SUPPLICANT_INTERFACE_STATE_READY
+	    || state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) {
+		nm_log_dbg (LOGD_WIFI, "(%s): not available because supplicant interface not ready",
+		            nm_device_get_iface (dev));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+can_auto_connect (NMDevice *dev,
+                  NMConnection *connection,
+                  char **specific_object)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	GSList *ap_iter;
+	const char *method = NULL;
+	guint64 timestamp = 0;
+
+	if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->can_auto_connect (dev, connection, specific_object))
+		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;
+	}
+
+	/* Use the connection if it's a shared connection */
+	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
+		return TRUE;
+
+	for (ap_iter = priv->ap_list; ap_iter; ap_iter = g_slist_next (ap_iter)) {
+		NMAccessPoint *ap = NM_AP (ap_iter->data);
+
+		if (nm_ap_check_compatible (ap, connection)) {
+			/* All good; connection is usable */
+			*specific_object = (char *) nm_ap_get_dbus_path (ap);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static void
+ap_list_dump (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	GSList * elt;
+	int i = 0;
+
+	g_return_if_fail (NM_IS_DEVICE_WIFI (self));
+
+	nm_log_dbg (LOGD_WIFI_SCAN, "Current AP list:");
+	for (elt = priv->ap_list; elt; elt = g_slist_next (elt), i++) {
+		NMAccessPoint * ap = NM_AP (elt->data);
+		nm_ap_dump (ap, "List AP: ");
+	}
+	nm_log_dbg (LOGD_WIFI_SCAN, "Current AP list: done");
+}
+
+static gboolean
+impl_device_get_access_points (NMDeviceWifi *self,
+                               GPtrArray **aps,
+                               GError **err)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	GSList *elt;
+
+	*aps = g_ptr_array_new ();
+	for (elt = priv->ap_list; elt; elt = g_slist_next (elt)) {
+		NMAccessPoint *ap = NM_AP (elt->data);
+
+		if (nm_ap_get_ssid (ap))
+			g_ptr_array_add (*aps, g_strdup (nm_ap_get_dbus_path (ap)));
+	}
+	return TRUE;
+}
+
+static gboolean
+impl_device_get_all_access_points (NMDeviceWifi *self,
+                                   GPtrArray **aps,
+                                   GError **err)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	GSList *elt;
+
+	*aps = g_ptr_array_new ();
+	for (elt = priv->ap_list; elt; elt = g_slist_next (elt))
+		g_ptr_array_add (*aps, g_strdup (nm_ap_get_dbus_path (NM_AP (elt->data))));
+	return TRUE;
+}
+
+static void
+request_scan_cb (NMDevice *device,
+                 DBusGMethodInvocation *context,
+                 GError *error,
+                 gpointer user_data)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	GError *local = NULL;
+
+	if (error) {
+		dbus_g_method_return_error (context, error);
+		return;
+	}
+
+	if (!check_scanning_allowed (self)) {
+		local = g_error_new_literal (NM_WIFI_ERROR,
+		                             NM_WIFI_ERROR_SCAN_NOT_ALLOWED,
+		                             "Scanning not allowed at this time");
+		dbus_g_method_return_error (context, local);
+		g_error_free (local);
+		return;
+	}
+
+	cancel_pending_scan (self);
+	request_wireless_scan (self);
+	dbus_g_method_return (context);
+}
+
+static void
+impl_device_request_scan (NMDeviceWifi *self,
+                          GHashTable *options,
+                          DBusGMethodInvocation *context)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMDevice *device = NM_DEVICE (self);
+	gint32 last_scan;
+	GError *error;
+
+	if (   !priv->enabled
+	    || !priv->sup_iface
+	    || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED
+	    || nm_device_is_activating (device)) {
+		error = g_error_new_literal (NM_WIFI_ERROR,
+		                             NM_WIFI_ERROR_SCAN_NOT_ALLOWED,
+		                             "Scanning not allowed while unavailable or activating");
+		goto error;
+	}
+
+	if (nm_supplicant_interface_get_scanning (priv->sup_iface)) {
+		error = g_error_new_literal (NM_WIFI_ERROR,
+		                             NM_WIFI_ERROR_SCAN_NOT_ALLOWED,
+		                             "Scanning not allowed while already scanning");
+		goto error;
+	}
+
+	last_scan = nm_supplicant_interface_get_last_scan_time (priv->sup_iface);
+	if (last_scan && (nm_utils_get_monotonic_timestamp_s () - last_scan) < 10) {
+		error = g_error_new_literal (NM_WIFI_ERROR,
+		                             NM_WIFI_ERROR_SCAN_NOT_ALLOWED,
+		                             "Scanning not allowed immediately following previous scan");
+		goto error;
+	}
+
+	/* 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,
+	                       request_scan_cb,
+	                       NULL);
+	return;
+
+error:
+	dbus_g_method_return_error (context, error);
+	g_error_free (error);
+}
+
+static gboolean
+scanning_allowed (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	guint32 sup_state;
+	NMConnection *connection;
+
+	g_return_val_if_fail (priv->sup_iface != NULL, FALSE);
+
+	/* Scanning not done in AP mode */
+	if (priv->mode == NM_802_11_MODE_AP)
+		return FALSE;
+
+	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:
+		/* Don't scan when unusable or activating */
+		return FALSE;
+	case NM_DEVICE_STATE_DISCONNECTED:
+	case NM_DEVICE_STATE_FAILED:
+		/* Can always scan when disconnected */
+		return TRUE;
+	case NM_DEVICE_STATE_ACTIVATED:
+		/* Need to do further checks when activated */
+		break;
+	}
+
+	/* Don't scan if the supplicant is busy */
+	sup_state = nm_supplicant_interface_get_state (priv->sup_iface);
+	if (   sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING
+	    || sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED
+	    || sup_state == NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE
+	    || sup_state == NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE
+	    || nm_supplicant_interface_get_scanning (priv->sup_iface))
+		return FALSE;
+
+	connection = nm_device_get_connection (NM_DEVICE (self));
+	if (connection) {
+		NMSettingWireless *s_wifi;
+		const char *ip4_method = NULL;
+		const GByteArray *bssid;
+
+		/* Don't scan when a shared connection is active; it makes drivers mad */
+		ip4_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+
+		if (!strcmp (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
+			return FALSE;
+
+		/* Don't scan when the connection is locked to a specifc AP, since
+		 * intra-ESS roaming (which requires periodic scanning) isn't being
+		 * used due to the specific AP lock. (bgo #513820)
+		 */
+		s_wifi = nm_connection_get_setting_wireless (connection);
+		g_assert (s_wifi);
+		bssid = nm_setting_wireless_get_bssid (s_wifi);
+		if (bssid && bssid->len == ETH_ALEN)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+scanning_allowed_accumulator (GSignalInvocationHint *ihint,
+                              GValue *return_accu,
+                              const GValue *handler_return,
+                              gpointer data)
+{
+	if (!g_value_get_boolean (handler_return))
+		g_value_set_boolean (return_accu, FALSE);
+	return TRUE;
+}
+
+static gboolean
+check_scanning_allowed (NMDeviceWifi *self)
+{
+	GValue instance = G_VALUE_INIT;
+	GValue retval = G_VALUE_INIT;
+
+	g_value_init (&instance, G_TYPE_OBJECT);
+	g_value_take_object (&instance, self);
+
+	g_value_init (&retval, G_TYPE_BOOLEAN);
+	g_value_set_boolean (&retval, TRUE);
+
+	/* Use g_signal_emitv() rather than g_signal_emit() to avoid the return
+	 * value being changed if no handlers are connected */
+	g_signal_emitv (&instance, signals[SCANNING_ALLOWED], 0, &retval);
+
+	return g_value_get_boolean (&retval);
+}
+
+static gboolean
+hidden_filter_func (NMConnectionProvider *provider,
+                    NMConnection *connection,
+                    gpointer user_data)
+{
+	NMSettingWireless *s_wifi;
+
+	s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (connection);
+	return s_wifi ? nm_setting_wireless_get_hidden (s_wifi) : FALSE;
+}
+
+static GPtrArray *
+build_hidden_probe_list (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	guint max_scan_ssids = nm_supplicant_interface_get_max_scan_ssids (priv->sup_iface);
+	GSList *connections, *iter;
+	GPtrArray *ssids = NULL;
+	static GByteArray *nullssid = NULL;
+
+	/* Need at least two: wildcard SSID and one or more hidden SSIDs */
+	if (max_scan_ssids < 2)
+		return NULL;
+
+	/* Static wildcard SSID used for every scan */
+	if (G_UNLIKELY (nullssid == NULL))
+		nullssid = g_byte_array_new ();
+
+	connections = nm_connection_provider_get_best_connections (nm_connection_provider_get (),
+	                                                           max_scan_ssids - 1,
+	                                                           NM_SETTING_WIRELESS_SETTING_NAME,
+	                                                           NULL,
+	                                                           hidden_filter_func,
+	                                                           NULL);
+	if (connections && connections->data) {
+		ssids = g_ptr_array_sized_new (max_scan_ssids - 1);
+		g_ptr_array_add (ssids, nullssid);  /* Add wildcard SSID */
+	}
+
+	for (iter = connections; iter; iter = g_slist_next (iter)) {
+		NMConnection *connection = iter->data;
+		NMSettingWireless *s_wifi;
+		const GByteArray *ssid;
+
+		s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (connection);
+		g_assert (s_wifi);
+		ssid = nm_setting_wireless_get_ssid (s_wifi);
+		g_assert (ssid);
+		g_ptr_array_add (ssids, (gpointer) ssid);
+	}
+	g_slist_free (connections);
+
+	return ssids;
+}
+
+static gboolean
+request_wireless_scan (gpointer user_data)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	gboolean backoff = FALSE;
+	GPtrArray *ssids = NULL;
+
+	if (priv->requested_scan) {
+		/* There's already a scan in progress */
+		return FALSE;
+	}
+
+	if (check_scanning_allowed (self)) {
+		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scanning requested",
+		            nm_device_get_iface (NM_DEVICE (self)));
+
+		ssids = build_hidden_probe_list (self);
+
+		if (nm_logging_enabled (LOGL_DEBUG, LOGD_WIFI_SCAN)) {
+			if (ssids) {
+				guint i;
+				char *foo;
+
+				for (i = 0; i < ssids->len; i++) {
+					foo = nm_utils_ssid_to_utf8 (g_ptr_array_index (ssids, i));
+					nm_log_dbg (LOGD_WIFI_SCAN, "(%s): (%d) probe scanning SSID '%s'",
+					            nm_device_get_iface (NM_DEVICE (self)),
+					            i, foo ? foo : "<hidden>");
+					g_free (foo);
+				}
+			} else {
+				nm_log_dbg (LOGD_WIFI_SCAN, "(%s): no SSIDs to probe scan",
+				            nm_device_get_iface (NM_DEVICE (self)));
+			}
+		}
+
+		if (nm_supplicant_interface_request_scan (priv->sup_iface, ssids)) {
+			/* success */
+			backoff = TRUE;
+			priv->requested_scan = TRUE;
+			nm_device_add_pending_action (NM_DEVICE (self), "scan", TRUE);
+		}
+
+		if (ssids) {
+			/* Elements owned by the connections, so we don't free them here */
+			g_ptr_array_free (ssids, TRUE);
+		}
+	} else {
+		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan requested but not allowed at this time",
+		            nm_device_get_iface (NM_DEVICE (self)));
+	}
+
+	priv->pending_scan_id = 0;
+	schedule_scan (self, backoff);
+	return FALSE;
+}
+
+
+/*
+ * schedule_scan
+ *
+ * Schedule a wireless scan.
+ *
+ */
+static void
+schedule_scan (NMDeviceWifi *self, gboolean backoff)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	gint32 now = nm_utils_get_monotonic_timestamp_s ();
+
+	/* Cancel the pending scan if it would happen later than (now + the scan_interval) */
+	if (priv->pending_scan_id) {
+		if (now + priv->scan_interval < priv->scheduled_scan_time)
+			cancel_pending_scan (self);
+	}
+
+	if (!priv->pending_scan_id) {
+		guint factor = 2, next_scan = priv->scan_interval;
+
+		if (    nm_device_is_activating (NM_DEVICE (self))
+		    || (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_ACTIVATED))
+			factor = 1;
+
+		priv->pending_scan_id = g_timeout_add_seconds (next_scan,
+		                                               request_wireless_scan,
+		                                               self);
+
+		priv->scheduled_scan_time = now + priv->scan_interval;
+		if (backoff && (priv->scan_interval < (SCAN_INTERVAL_MAX / factor))) {
+				priv->scan_interval += (SCAN_INTERVAL_STEP / factor);
+				/* Ensure the scan interval will never be less than 20s... */
+				priv->scan_interval = MAX(priv->scan_interval, SCAN_INTERVAL_MIN + SCAN_INTERVAL_STEP);
+				/* ... or more than 120s */
+				priv->scan_interval = MIN(priv->scan_interval, SCAN_INTERVAL_MAX);
+		} else if (!backoff && (priv->scan_interval == 0)) {
+			/* Invalid combination; would cause continual rescheduling of
+			 * the scan and hog CPU.  Reset to something minimally sane.
+			 */
+			priv->scan_interval = 5;
+		}
+
+		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scheduled scan in %d seconds (interval now %d seconds)",
+		            nm_device_get_iface (NM_DEVICE (self)),
+		            next_scan,
+		            priv->scan_interval);
+
+	}
+}
+
+
+static void
+cancel_pending_scan (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	if (priv->pending_scan_id) {
+		g_source_remove (priv->pending_scan_id);
+		priv->pending_scan_id = 0;
+	}
+}
+
+static void
+supplicant_iface_scan_done_cb (NMSupplicantInterface *iface,
+                               gboolean success,
+                               NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan %s",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            success ? "successful" : "failed");
+
+	schedule_scan (self, success);
+
+	/* Ensure that old APs get removed, which otherwise only
+	 * happens when there are new BSSes.
+	 */
+	schedule_scanlist_cull (self);
+
+	if (priv->requested_scan) {
+		priv->requested_scan = FALSE;
+		nm_device_remove_pending_action (NM_DEVICE (self), "scan", TRUE);
+	}
+}
+
+/****************************************************************************
+ * WPA Supplicant control stuff
+ *
+ */
+
+static void
+try_fill_ssid_for_hidden_ap (NMAccessPoint *ap)
+{
+	const struct ether_addr *bssid;
+	const GSList *connections, *iter;
+
+	g_return_if_fail (nm_ap_get_ssid (ap) == NULL);
+
+	bssid = nm_ap_get_address (ap);
+	g_assert (bssid);
+
+	/* Look for this AP's BSSID in the seen-bssids list of a connection,
+	 * and if a match is found, copy over the SSID */
+	connections = nm_connection_provider_get_connections (nm_connection_provider_get ());
+	for (iter = connections; iter; iter = g_slist_next (iter)) {
+		NMConnection *connection = NM_CONNECTION (iter->data);
+		NMSettingWireless *s_wifi;
+
+		s_wifi = nm_connection_get_setting_wireless (connection);
+		if (s_wifi) {
+			if (nm_settings_connection_has_seen_bssid (NM_SETTINGS_CONNECTION (connection), bssid)) {
+				nm_ap_set_ssid (ap, nm_setting_wireless_get_ssid (s_wifi));
+				break;
+			}
+		}
+	}
+}
+
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5]
+
+/*
+ * merge_scanned_ap
+ *
+ * If there is already an entry that matches the BSSID and ESSID of the
+ * AP to merge, replace that entry with the scanned AP.  Otherwise, add
+ * the scanned AP to the list.
+ *
+ * TODO: possibly need to differentiate entries based on security too; i.e. if
+ * there are two scan results with the same BSSID and SSID but different
+ * security options?
+ *
+ */
+static void
+merge_scanned_ap (NMDeviceWifi *self,
+                  NMAccessPoint *merge_ap)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMAccessPoint *found_ap = NULL;
+	const GByteArray *ssid;
+	const struct ether_addr *bssid;
+	gboolean strict_match = TRUE;
+
+	/* Let the manager try to fill in the SSID from seen-bssids lists */
+	bssid = nm_ap_get_address (merge_ap);
+	ssid = nm_ap_get_ssid (merge_ap);
+	if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) {
+		/* Try to fill the SSID from the AP database */
+		try_fill_ssid_for_hidden_ap (merge_ap);
+
+		ssid = nm_ap_get_ssid (merge_ap);
+		if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) {
+			/* Yay, matched it, no longer treat as hidden */
+			nm_log_dbg (LOGD_WIFI_SCAN, "(%s): matched hidden AP " MAC_FMT " => '%s'",
+			            nm_device_get_iface (NM_DEVICE (self)),
+			            MAC_ARG (bssid->ether_addr_octet),
+			            nm_utils_escape_ssid (ssid->data, ssid->len));
+			nm_ap_set_broadcast (merge_ap, FALSE);
+		} else {
+			/* Didn't have an entry for this AP in the database */
+			nm_log_dbg (LOGD_WIFI_SCAN, "(%s): failed to match hidden AP " MAC_FMT,
+			            nm_device_get_iface (NM_DEVICE (self)),
+			            MAC_ARG (bssid->ether_addr_octet));
+		}
+	}
+
+	/* If the incoming scan result matches the hidden AP that NM is currently
+	 * connected to but hasn't been seen in the scan list yet, don't use
+	 * strict matching.  Because the capabilities of the fake AP have to be
+	 * constructed from the NMConnection of the activation request, they won't
+	 * always be the same as the capabilities of the real AP from the scan.
+	 */
+	if (priv->current_ap && nm_ap_get_fake (priv->current_ap))
+		strict_match = FALSE;
+
+	found_ap = get_ap_by_supplicant_path (self, nm_ap_get_supplicant_path (merge_ap));
+	if (!found_ap)
+		found_ap = nm_ap_match_in_list (merge_ap, priv->ap_list, strict_match);
+	if (found_ap) {
+		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): merging AP '%s' " MAC_FMT " (%p) with existing (%p)",
+		            nm_device_get_iface (NM_DEVICE (self)),
+		            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
+		            MAC_ARG (bssid->ether_addr_octet),
+		            merge_ap,
+		            found_ap);
+
+		nm_ap_set_supplicant_path (found_ap, nm_ap_get_supplicant_path (merge_ap));
+		nm_ap_set_flags (found_ap, nm_ap_get_flags (merge_ap));
+		nm_ap_set_wpa_flags (found_ap, nm_ap_get_wpa_flags (merge_ap));
+		nm_ap_set_rsn_flags (found_ap, nm_ap_get_rsn_flags (merge_ap));
+		nm_ap_set_strength (found_ap, nm_ap_get_strength (merge_ap));
+		nm_ap_set_last_seen (found_ap, nm_ap_get_last_seen (merge_ap));
+		nm_ap_set_broadcast (found_ap, nm_ap_get_broadcast (merge_ap));
+		nm_ap_set_freq (found_ap, nm_ap_get_freq (merge_ap));
+		nm_ap_set_max_bitrate (found_ap, nm_ap_get_max_bitrate (merge_ap));
+
+		/* If the AP is noticed in a scan, it's automatically no longer
+		 * fake, since it clearly exists somewhere.
+		 */
+		nm_ap_set_fake (found_ap, FALSE);
+	} else {
+		/* New entry in the list */
+		nm_log_dbg (LOGD_WIFI_SCAN, "(%s): adding new AP '%s' " MAC_FMT " (%p)",
+		            nm_device_get_iface (NM_DEVICE (self)),
+		            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
+		            MAC_ARG (bssid->ether_addr_octet),
+		            merge_ap);
+
+		g_object_ref (merge_ap);
+		priv->ap_list = g_slist_prepend (priv->ap_list, merge_ap);
+		nm_ap_export_to_dbus (merge_ap);
+		emit_ap_added_removed (self, ACCESS_POINT_ADDED, merge_ap, TRUE);
+	}
+}
+
+#define WPAS_REMOVED_TAG "supplicant-removed"
+
+static gboolean
+cull_scan_list (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	gint32 now = nm_utils_get_monotonic_timestamp_s ();
+	GSList *outdated_list = NULL;
+	GSList *elt;
+	guint32 removed = 0, total = 0;
+
+	priv->scanlist_cull_id = 0;
+
+	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): checking scan list for outdated APs",
+	            nm_device_get_iface (NM_DEVICE (self)));
+
+	/* Walk the access point list and remove any access points older than
+	 * three times the inactive scan interval.
+	 */
+	for (elt = priv->ap_list; elt; elt = g_slist_next (elt), total++) {
+		NMAccessPoint *ap = elt->data;
+		const guint prune_interval_s = SCAN_INTERVAL_MAX * 3;
+		gint32 last_seen;
+
+		/* Don't cull the associated AP or manually created APs */
+		if (ap == priv->current_ap)
+			continue;
+		g_assert (!nm_ap_get_fake (ap)); /* only the current_ap can be fake */
+
+		/* Don't cull APs still known to the supplicant.  Since the supplicant
+		 * doesn't yet emit property updates for "last seen" we have to rely
+		 * on changing signal strength for updating "last seen".  But if the
+		 * AP's strength doesn't change we won't get any updates for the AP,
+		 * and we'll end up here even if the AP was still found by the
+		 * supplicant in the last scan.
+		 */
+		if (   nm_ap_get_supplicant_path (ap)
+		    && g_object_get_data (G_OBJECT (ap), WPAS_REMOVED_TAG) == NULL)
+			continue;
+
+		last_seen = nm_ap_get_last_seen (ap);
+		if (!last_seen || last_seen + prune_interval_s < now)
+			outdated_list = g_slist_prepend (outdated_list, ap);
+	}
+
+	/* Remove outdated APs */
+	for (elt = outdated_list; elt; elt = g_slist_next (elt)) {
+		NMAccessPoint *outdated_ap = NM_AP (elt->data);
+		const struct ether_addr *bssid;
+		const GByteArray *ssid;
+
+		bssid = nm_ap_get_address (outdated_ap);
+		ssid = nm_ap_get_ssid (outdated_ap);
+		nm_log_dbg (LOGD_WIFI_SCAN,
+		            "   removing %02x:%02x:%02x:%02x:%02x:%02x (%s%s%s)",
+		            bssid->ether_addr_octet[0], bssid->ether_addr_octet[1],
+		            bssid->ether_addr_octet[2], bssid->ether_addr_octet[3],
+		            bssid->ether_addr_octet[4], bssid->ether_addr_octet[5],
+		            ssid ? "'" : "",
+		            ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
+		            ssid ? "'" : "");
+
+		remove_access_point (self, outdated_ap);
+		removed++;
+	}
+	g_slist_free (outdated_list);
+
+	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): removed %d APs (of %d)",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            removed, total);
+
+	ap_list_dump (self);
+
+	if(removed > 0)
+	    nm_device_recheck_available_connections (NM_DEVICE (self));
+
+	return FALSE;
+}
+
+static void
+schedule_scanlist_cull (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	/* Cull the scan list after the last request for it has come in */
+	if (priv->scanlist_cull_id)
+		g_source_remove (priv->scanlist_cull_id);
+	priv->scanlist_cull_id = g_timeout_add_seconds (4, (GSourceFunc) cull_scan_list, self);
+}
+
+static void
+supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
+                             const char *object_path,
+                             GHashTable *properties,
+                             NMDeviceWifi *self)
+{
+	NMDeviceState state;
+	NMAccessPoint *ap;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (properties != NULL);
+	g_return_if_fail (iface != NULL);
+
+	/* Ignore new APs when unavailable, unmanaged, or in AP mode */
+	state = nm_device_get_state (NM_DEVICE (self));
+	if (state <= NM_DEVICE_STATE_UNAVAILABLE)
+		return;
+	if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP)
+		return;
+
+	ap = nm_ap_new_from_properties (object_path, properties);
+	if (ap) {
+		nm_ap_dump (ap, "New AP: ");
+
+		/* Add the AP to the device's AP list */
+		merge_scanned_ap (self, ap);
+		g_object_unref (ap);
+	} else {
+		nm_log_warn (LOGD_WIFI_SCAN, "(%s): invalid AP properties received",
+		             nm_device_get_iface (NM_DEVICE (self)));
+	}
+
+	/* Remove outdated access points */
+	schedule_scanlist_cull (self);
+}
+
+static void
+supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
+                                 const char *object_path,
+                                 GHashTable *properties,
+                                 NMDeviceWifi *self)
+{
+	NMDeviceState state;
+	NMAccessPoint *ap;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (object_path != NULL);
+	g_return_if_fail (properties != NULL);
+
+	/* Ignore new APs when unavailable or unamnaged */
+	state = nm_device_get_state (NM_DEVICE (self));
+	if (state <= NM_DEVICE_STATE_UNAVAILABLE)
+		return;
+
+	/* Update the AP's last-seen property */
+	ap = get_ap_by_supplicant_path (self, object_path);
+	if (ap)
+		nm_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ());
+
+	/* Remove outdated access points */
+	schedule_scanlist_cull (self);
+}
+
+static void
+supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
+                                 const char *object_path,
+                                 NMDeviceWifi *self)
+{
+	NMAccessPoint *ap;
+
+	g_return_if_fail (self != NULL);
+	g_return_if_fail (object_path != NULL);
+
+	ap = get_ap_by_supplicant_path (self, object_path);
+	if (ap)
+		g_object_set_data (G_OBJECT (ap), WPAS_REMOVED_TAG, GUINT_TO_POINTER (TRUE));
+}
+
+static void
+remove_supplicant_timeouts (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	if (priv->sup_timeout_id) {
+		g_source_remove (priv->sup_timeout_id);
+		priv->sup_timeout_id = 0;
+	}
+
+	if (priv->link_timeout_id) {
+		g_source_remove (priv->link_timeout_id);
+		priv->link_timeout_id = 0;
+	}
+}
+
+static void
+cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	remove_supplicant_interface_error_handler (self);
+	remove_supplicant_timeouts (self);
+	if (disconnect && priv->sup_iface)
+		nm_supplicant_interface_disconnect (priv->sup_iface);
+}
+
+static void
+wifi_secrets_cb (NMActRequest *req,
+                 guint32 call_id,
+                 NMConnection *connection,
+                 GError *error,
+                 gpointer user_data)
+{
+	NMDevice *dev = NM_DEVICE (user_data);
+
+	g_return_if_fail (req == nm_device_get_act_request (dev));
+	g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH);
+	g_return_if_fail (nm_act_request_get_connection (req) == connection);
+
+	if (error) {
+		nm_log_warn (LOGD_WIFI, "%s", error->message);
+		nm_device_state_changed (dev,
+		                         NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_NO_SECRETS);
+	} else
+		nm_device_activate_schedule_stage1_device_prepare (dev);
+}
+
+/*
+ * link_timeout_cb
+ *
+ * Called when the link to the access point has been down for a specified
+ * period of time.
+ */
+static gboolean
+link_timeout_cb (gpointer user_data)
+{
+	NMDevice *dev = NM_DEVICE (user_data);
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	nm_log_warn (LOGD_WIFI, "(%s): link timed out.", nm_device_get_iface (dev));
+
+	priv->link_timeout_id = 0;
+
+	/* Disconnect event while activated; the supplicant hasn't been able
+	 * to reassociate within the timeout period, so the connection must
+	 * fail.
+	 */
+	if (nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED)
+		return FALSE;
+
+	/* If the access point failed, and wasn't found by the supplicant when it
+	 * attempted to reconnect, then it's probably out of range or turned off.
+	 * Remove it from the list and if it's actually still present, it'll be
+	 * found in the next scan.
+	 */
+	if (priv->ssid_found == FALSE && priv->current_ap)
+		set_current_ap (self, NULL, TRUE, TRUE);
+
+	nm_device_state_changed (dev,
+	                         NM_DEVICE_STATE_FAILED,
+	                         priv->ssid_found ? NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT :
+	                                            NM_DEVICE_STATE_REASON_SSID_NOT_FOUND);
+	return FALSE;
+}
+
+static gboolean
+need_new_8021x_secrets (NMDeviceWifi *self,
+                        guint32 old_state,
+                        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_connection (NM_DEVICE (self));
+	g_return_val_if_fail (connection != NULL, FALSE);
+
+	/* 802.1x stuff only happens in the supplicant's ASSOCIATED state when it's
+	 * attempting to authenticate with the AP.
+	 */
+	if (old_state != NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED)
+		return 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) {
+		nm_setting_get_secret_flags (NM_SETTING (s_8021x),
+		                             NM_SETTING_802_1X_PASSWORD,
+		                             &secret_flags,
+		                             NULL);
+		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) {
+		nm_setting_get_secret_flags (NM_SETTING (s_wsec),
+		                             NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
+		                             &secret_flags,
+		                             NULL);
+		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 (NMDeviceWifi *self,
+                  guint32 old_state,
+                  const char **setting_name)
+{
+	NMSettingWirelessSecurity *s_wsec;
+	NMConnection *connection;
+	const char *key_mgmt = NULL;
+
+	g_assert (setting_name != NULL);
+
+	connection = nm_device_get_connection (NM_DEVICE (self));
+	g_return_val_if_fail (connection != NULL, FALSE);
+
+	/* A bad PSK will cause the supplicant to disconnect during the 4-way handshake */
+	if (old_state != NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE)
+		return 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) {
+		*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 (NMDeviceWifi *self,
+                               guint32 new_state,
+                               guint32 old_state,
+                               int disconnect_reason)
+{
+	NMDevice *device = NM_DEVICE (self);
+	NMActRequest *req;
+	NMConnection *connection;
+	const char *setting_name = NULL;
+	gboolean handled = FALSE;
+
+	g_return_val_if_fail (new_state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, FALSE);
+
+	req = nm_device_get_act_request (NM_DEVICE (self));
+	g_return_val_if_fail (req != NULL, FALSE);
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	if (   need_new_8021x_secrets (self, old_state, &setting_name)
+	    || need_new_wpa_psk (self, old_state, &setting_name)) {
+
+		nm_connection_clear_secrets (connection);
+
+		nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): disconnected during association,"
+		             " asking for new key.", nm_device_get_iface (device));
+
+		cleanup_association_attempt (self, TRUE);
+		nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
+		nm_act_request_get_secrets (req,
+		                            setting_name,
+		                            NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION
+		                              | NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW,
+		                            NULL,
+		                            wifi_secrets_cb,
+		                            self);
+		handled = TRUE;
+	}
+
+	return handled;
+}
+
+static void
+supplicant_iface_state_cb (NMSupplicantInterface *iface,
+                           guint32 new_state,
+                           guint32 old_state,
+                           int disconnect_reason,
+                           gpointer user_data)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMDevice *device = NM_DEVICE (self);
+	NMDeviceState devstate;
+	gboolean scanning;
+
+	if (new_state == old_state)
+		return;
+
+	nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+	             "(%s): supplicant interface state: %s -> %s",
+	             nm_device_get_iface (device),
+	             nm_supplicant_interface_state_to_string (old_state),
+	             nm_supplicant_interface_state_to_string (new_state));
+
+	devstate = nm_device_get_state (device);
+	scanning = nm_supplicant_interface_get_scanning (iface);
+
+	/* In these states we know the supplicant is actually talking to something */
+	if (   new_state >= NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING
+	    && new_state <= NM_SUPPLICANT_INTERFACE_STATE_COMPLETED)
+		priv->ssid_found = TRUE;
+
+	switch (new_state) {
+	case NM_SUPPLICANT_INTERFACE_STATE_READY:
+		priv->scan_interval = SCAN_INTERVAL_MIN;
+
+		/* If the interface can now be activated because the supplicant is now
+		 * available, transition to DISCONNECTED.
+		 */
+		if ((devstate == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device)) {
+			nm_device_state_changed (device,
+			                         NM_DEVICE_STATE_DISCONNECTED,
+			                         NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE);
+		}
+
+		nm_log_dbg (LOGD_WIFI_SCAN,
+		            "(%s): supplicant ready, requesting initial scan",
+		            nm_device_get_iface (device));
+
+		/* Request a scan to get latest results */
+		cancel_pending_scan (self);
+		request_wireless_scan (self);
+
+		if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY)
+			nm_device_remove_pending_action (device, "waiting for supplicant", TRUE);
+		break;
+	case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
+		remove_supplicant_interface_error_handler (self);
+		remove_supplicant_timeouts (self);
+
+		/* If this is the initial association during device activation,
+		 * schedule the next activation stage.
+		 */
+		if (devstate == NM_DEVICE_STATE_CONFIG) {
+			NMConnection *connection;
+			NMSettingWireless *s_wifi;
+			const GByteArray *ssid;
+
+			connection = nm_device_get_connection (NM_DEVICE (self));
+			g_return_if_fail (connection);
+
+			s_wifi = nm_connection_get_setting_wireless (connection);
+			g_return_if_fail (s_wifi);
+
+			ssid = nm_setting_wireless_get_ssid (s_wifi);
+			g_return_if_fail (ssid);
+
+			nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+			             "Activation (%s/wireless) Stage 2 of 5 (Device Configure) "
+			             "successful.  %s '%s'.",
+			             nm_device_get_iface (device),
+			             priv->mode == NM_802_11_MODE_AP ? "Started Wi-Fi Hotspot" :
+			                 "Connected to wireless network",
+			             ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)");
+			nm_device_activate_schedule_stage3_ip_config_start (device);
+		} else if (devstate == NM_DEVICE_STATE_ACTIVATED)
+			periodic_update (self, NULL);
+		break;
+	case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED:
+		if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) {
+			/* Disconnect of an 802.1x/LEAP connection during authentication,
+			 * or disconnect of a WPA-PSK connection during the 4-way handshake,
+			 * often means secrets are wrong. Not always the case, but until we
+			 * have more information from wpa_supplicant about why the
+			 * disconnect happened this is the best we can do.
+			 */
+			if (handle_8021x_or_psk_auth_fail (self, new_state, old_state, disconnect_reason))
+				break;
+		}
+
+		/* Otherwise it might be a stupid driver or some transient error, so
+		 * let the supplicant try to reconnect a few more times.  Give it more
+		 * time if a scan is in progress since the link might be dropped during
+		 * the scan but will be re-established when the scan is done.
+		 */
+		if (devstate == NM_DEVICE_STATE_ACTIVATED) {
+			if (priv->link_timeout_id == 0) {
+				priv->link_timeout_id = g_timeout_add_seconds (scanning ? 30 : 15, link_timeout_cb, self);
+				priv->ssid_found = FALSE;
+			}
+		}
+		break;
+	case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
+		cleanup_association_attempt (self, FALSE);
+
+		if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY)
+			nm_device_remove_pending_action (device, "waiting for supplicant", TRUE);
+
+		/* If the device is already in UNAVAILABLE state then the state change
+		 * is a NOP and the interface won't be re-acquired in the device state
+		 * change handler.  So ensure we have a new one here so that we're
+		 * ready if the supplicant comes back.
+		 */
+		supplicant_interface_release (self);
+		supplicant_interface_acquire (self);
+
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_UNAVAILABLE,
+		                         NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+		break;
+	default:
+		break;
+	}
+
+	/* Signal scanning state changes */
+	if (   new_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING
+	    || old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING)
+		g_object_notify (G_OBJECT (self), "scanning");
+}
+
+static void
+supplicant_iface_connection_error_cb (NMSupplicantInterface *iface,
+                                      const char *name,
+                                      const char *message,
+                                      NMDeviceWifi *self)
+{
+	NMDevice *device = NM_DEVICE (self);
+
+	if (nm_device_is_activating (device)) {
+		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): supplicant association failed: %s - %s",
+		             nm_device_get_iface (device), name, message);
+
+		cleanup_association_attempt (self, TRUE);
+		nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+	}
+}
+
+static void
+remove_supplicant_interface_error_handler (NMDeviceWifi *self)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	if (priv->sup_iface) {
+		g_signal_handlers_disconnect_by_func (priv->sup_iface,
+		                                      supplicant_iface_connection_error_cb,
+		                                      self);
+	}
+}
+
+static void
+supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface,
+                                     GParamSpec *pspec,
+                                     NMDeviceWifi *self)
+{
+	NMDeviceState state;
+	gboolean scanning;
+
+	scanning = nm_supplicant_interface_get_scanning (iface);
+	nm_log_dbg (LOGD_WIFI_SCAN, "(%s): now %s",
+	            nm_device_get_iface (NM_DEVICE (self)),
+	            scanning ? "scanning" : "idle");
+
+	g_object_notify (G_OBJECT (self), "scanning");
+
+	/* Run a quick update of current AP when coming out of a scan */
+	state = nm_device_get_state (NM_DEVICE (self));
+	if (!scanning && state == NM_DEVICE_STATE_ACTIVATED)
+		periodic_update (self, NULL);
+}
+
+static NMActStageReturn
+handle_auth_or_fail (NMDeviceWifi *self,
+                     NMActRequest *req,
+                     gboolean new_secrets)
+{
+	const char *setting_name;
+	guint32 tries;
+	NMConnection *connection;
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+
+	g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), NM_ACT_STAGE_RETURN_FAILURE);
+
+	if (!req) {
+		req = nm_device_get_act_request (NM_DEVICE (self));
+		g_assert (req);
+	}
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES));
+	if (tries > 3)
+		return NM_ACT_STAGE_RETURN_FAILURE;
+
+	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
+
+	nm_connection_clear_secrets (connection);
+	setting_name = nm_connection_need_secrets (connection, NULL);
+	if (setting_name) {
+		NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+
+		if (new_secrets)
+			flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW;
+		nm_act_request_get_secrets (req, setting_name, flags, NULL, wifi_secrets_cb, self);
+
+		g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, GUINT_TO_POINTER (++tries));
+		ret = NM_ACT_STAGE_RETURN_POSTPONE;
+	} else
+		nm_log_warn (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
+
+	return ret;
+}
+
+/*
+ * supplicant_connection_timeout_cb
+ *
+ * Called when the supplicant has been unable to connect to an access point
+ * within a specified period of time.
+ */
+static gboolean
+supplicant_connection_timeout_cb (gpointer user_data)
+{
+	NMDevice *dev = NM_DEVICE (user_data);
+	NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMActRequest *req;
+	NMConnection *connection;
+
+	cleanup_association_attempt (self, TRUE);
+
+	if (!nm_device_is_activating (dev))
+		return FALSE;
+
+	/* Timed out waiting for a successful connection to the AP; if the AP's
+	 * security requires network-side authentication (like WPA or 802.1x)
+	 * and the connection attempt timed out then it's likely the authentication
+	 * information (passwords, pin codes, etc) are wrong.
+	 */
+
+	req = nm_device_get_act_request (dev);
+	g_assert (req);
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	if (   priv->mode == NM_802_11_MODE_ADHOC
+	    || priv->mode == NM_802_11_MODE_AP) {
+		/* In Ad-Hoc and AP modes there's nothing to check the encryption key
+		 * (if any), so supplicant timeouts here are almost certainly the wifi
+		 * driver being really stupid.
+		 */
+		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): %s network creation took "
+		             "too long, failing activation.",
+		             nm_device_get_iface (dev),
+		             priv->mode == NM_802_11_MODE_ADHOC ? "Ad-Hoc" : "Hotspot");
+		nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED,
+		                         NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT);
+		return FALSE;
+	}
+
+	g_assert (priv->mode == NM_802_11_MODE_INFRA);
+
+	if (priv->ssid_found && nm_connection_get_setting_wireless_security (connection)) {
+		guint64 timestamp = 0;
+		gboolean new_secrets = TRUE;
+
+		/* Connection failed; either driver problems, the encryption key is
+		 * wrong, or the passwords or certificates were wrong.
+		 */
+		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): association took too long.",
+		             nm_device_get_iface (dev));
+
+		/* Ask for new secrets only if we've never activated this connection
+		 * before.  If we've connected before, don't bother the user with
+		 * dialogs, just retry or fail, and if we never connect the user can
+		 * fix the password somewhere else.
+		 */
+		if (nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (connection), &timestamp))
+			new_secrets = !timestamp;
+
+		if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_POSTPONE) {
+			nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+			             "Activation (%s/wireless): asking for new secrets",
+			             nm_device_get_iface (dev));
+		} else {
+			nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_NO_SECRETS);
+		}
+	} else {
+		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): association took too long, "
+		             "failing activation.",
+		             nm_device_get_iface (dev));
+		nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED,
+		                         priv->ssid_found ? NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT :
+		                                            NM_DEVICE_STATE_REASON_SSID_NOT_FOUND);
+	}
+
+	return FALSE;
+}
+
+static NMSupplicantConfig *
+build_supplicant_config (NMDeviceWifi *self,
+                         NMConnection *connection,
+                         guint32 fixed_freq)
+{
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMSupplicantConfig *config = NULL;
+	NMSettingWireless *s_wireless;
+	NMSettingWirelessSecurity *s_wireless_sec;
+
+	g_return_val_if_fail (self != NULL, NULL);
+
+	s_wireless = nm_connection_get_setting_wireless (connection);
+	g_return_val_if_fail (s_wireless != NULL, NULL);
+
+	config = nm_supplicant_config_new ();
+	if (!config)
+		return NULL;
+
+	/* Warn if AP mode may not be supported */
+	if (   g_strcmp0 (nm_setting_wireless_get_mode (s_wireless), NM_SETTING_WIRELESS_MODE_AP) == 0
+	    && nm_supplicant_interface_get_ap_support (priv->sup_iface) == AP_SUPPORT_UNKNOWN) {
+		nm_log_warn (LOGD_WIFI, "Supplicant may not support AP mode; connection may time out.");
+	}
+
+	if (!nm_supplicant_config_add_setting_wireless (config,
+	                                                s_wireless,
+	                                                fixed_freq)) {
+		nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless setting to supplicant config.");
+		goto error;
+	}
+
+	s_wireless_sec = nm_connection_get_setting_wireless_security (connection);
+	if (s_wireless_sec) {
+		NMSetting8021x *s_8021x;
+		const char *con_uuid = nm_connection_get_uuid (connection);
+
+		g_assert (con_uuid);
+		s_8021x = nm_connection_get_setting_802_1x (connection);
+		if (!nm_supplicant_config_add_setting_wireless_security (config,
+		                                                         s_wireless_sec,
+		                                                         s_8021x,
+		                                                         con_uuid)) {
+			nm_log_err (LOGD_WIFI, "Couldn't add 802-11-wireless-security setting to "
+			            "supplicant config.");
+			goto error;
+		}
+	} else {
+		if (!nm_supplicant_config_add_no_security (config)) {
+			nm_log_err (LOGD_WIFI, "Couldn't add unsecured option to supplicant config.");
+			goto error;
+		}
+	}
+
+	return config;
+
+error:
+	g_object_unref (config);
+	return NULL;
+}
+
+/****************************************************************************/
+
+static void
+update_permanent_hw_address (NMDevice *dev)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	struct ifreq req;
+	struct ethtool_perm_addr *epaddr = NULL;
+	int fd, ret;
+
+	fd = socket (PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		nm_log_err (LOGD_HW, "could not open control socket.");
+		return;
+	}
+
+	/* Get permanent MAC address */
+	memset (&req, 0, sizeof (struct ifreq));
+	strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ);
+
+	epaddr = g_malloc0 (sizeof (struct ethtool_perm_addr) + ETH_ALEN);
+	epaddr->cmd = ETHTOOL_GPERMADDR;
+	epaddr->size = ETH_ALEN;
+	req.ifr_data = (void *) epaddr;
+
+	errno = 0;
+	ret = ioctl (fd, SIOCETHTOOL, &req);
+	if ((ret < 0) || !nm_ethernet_address_is_valid ((struct ether_addr *) epaddr->data)) {
+		nm_log_dbg (LOGD_HW | LOGD_ETHER, "(%s): unable to read permanent MAC address (error %d)",
+		            nm_device_get_iface (dev), errno);
+		/* Fall back to current address */
+		memcpy (epaddr->data, nm_device_get_hw_address (dev, NULL), ETH_ALEN);
+	}
+
+	if (memcmp (&priv->perm_hw_addr, epaddr->data, ETH_ALEN)) {
+		memcpy (&priv->perm_hw_addr, epaddr->data, ETH_ALEN);
+		g_object_notify (G_OBJECT (dev), NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS);
+	}
+
+	g_free (epaddr);
+	close (fd);
+}
+
+static void
+update_initial_hw_address (NMDevice *dev)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	char *mac_str;
+
+	/* This sets initial MAC address from current MAC address. It should only
+	 * be called from NMDevice constructor() to really get the initial address.
+	 */
+	memcpy (priv->initial_hw_addr, nm_device_get_hw_address (dev, NULL), ETH_ALEN);
+
+	mac_str = nm_utils_hwaddr_ntoa (priv->initial_hw_addr, ARPHRD_ETHER);
+	nm_log_dbg (LOGD_DEVICE | LOGD_ETHER, "(%s): read initial MAC address %s",
+	            nm_device_get_iface (dev), mac_str);
+	g_free (mac_str);
+}
+
+static NMActStageReturn
+act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMActStageReturn ret;
+	NMAccessPoint *ap = NULL;
+	NMActRequest *req;
+	NMConnection *connection;
+	NMSettingWireless *s_wireless;
+	const GByteArray *cloned_mac;
+	GSList *iter;
+	const char *mode;
+	const char *ap_path;
+
+	ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage1_prepare (dev, 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 != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	connection = nm_act_request_get_connection (req);
+	g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	s_wireless = nm_connection_get_setting_wireless (connection);
+	g_assert (s_wireless);
+
+	mode = nm_setting_wireless_get_mode (s_wireless);
+	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) == 0)
+		priv->mode = NM_802_11_MODE_INFRA;
+	else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) == 0)
+		priv->mode = NM_802_11_MODE_ADHOC;
+	else if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_AP) == 0) {
+		priv->mode = NM_802_11_MODE_AP;
+
+		/* Scanning not done in AP mode; clear the scan list */
+		remove_all_aps (self);
+	}
+	g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_MODE);
+
+	/* The kernel doesn't support Ad-Hoc WPA connections well at this time,
+	 * and turns them into open networks.  It's been this way since at least
+	 * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks.
+	 */
+	if (is_adhoc_wpa (connection)) {
+		nm_log_warn (LOGD_WIFI, "Ad-Hoc WPA disabled due to kernel bugs");
+		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
+
+	/* Set spoof MAC to the interface */
+	cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless);
+	if (cloned_mac && (cloned_mac->len == ETH_ALEN))
+		nm_device_set_hw_addr (dev, (const guint8 *) cloned_mac->data, "set", LOGD_WIFI);
+
+	/* AP mode never uses a specific object or existing scanned AP */
+	if (priv->mode != NM_802_11_MODE_AP) {
+
+		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)
+			goto done;
+
+		/* Find a compatible AP in the scan list */
+		for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
+			NMAccessPoint *candidate = NM_AP (iter->data);
+
+			if (nm_ap_check_compatible (candidate, connection)) {
+				ap = candidate;
+				break;
+			}
+		}
+	}
+
+	if (ap) {
+		nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_ap_get_dbus_path (ap));
+		goto done;
+	}
+
+	/* If the user is trying to connect to an AP that NM doesn't yet know about
+	 * (hidden network or something) or starting a Hotspot, create an fake AP
+	 * from the security settings in the connection.  This "fake" AP gets used
+	 * until the real one is found in the scan list (Ad-Hoc or Hidden), or until
+	 * the device is deactivated (Hotspot).
+	 */
+	ap = nm_ap_new_fake_from_connection (connection);
+	g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	if (nm_ap_get_mode (ap) == NM_802_11_MODE_INFRA)
+		nm_ap_set_broadcast (ap, FALSE);
+	else if (nm_ap_is_hotspot (ap))
+		nm_ap_set_address (ap, (const struct ether_addr *) nm_device_get_hw_address (dev, NULL));
+
+	priv->ap_list = g_slist_prepend (priv->ap_list, ap);
+	nm_ap_export_to_dbus (ap);
+	g_object_freeze_notify (G_OBJECT (self));
+	set_current_ap (self, ap, FALSE, FALSE);
+	emit_ap_added_removed (self, ACCESS_POINT_ADDED, ap, TRUE);
+	g_object_thaw_notify (G_OBJECT (self));
+	nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_ap_get_dbus_path (ap));
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+
+done:
+	set_current_ap (self, ap, TRUE, FALSE);
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+static void
+ensure_hotspot_frequency (NMDeviceWifi *self,
+                          NMSettingWireless *s_wifi,
+                          NMAccessPoint *ap)
+{
+	const char *band = nm_setting_wireless_get_band (s_wifi);
+	const guint32 a_freqs[] = { 5180, 5200, 5220, 5745, 5765, 5785, 5805, 0 };
+	const guint32 bg_freqs[] = { 2412, 2437, 2462, 2472, 0 };
+	guint32 freq = 0;
+
+	g_assert (ap);
+
+	if (nm_ap_get_freq (ap))
+		return;
+
+	if (g_strcmp0 (band, "a") == 0)
+		freq = nm_platform_wifi_find_frequency (nm_device_get_ifindex (NM_DEVICE (self)), a_freqs);
+	else
+		freq = nm_platform_wifi_find_frequency (nm_device_get_ifindex (NM_DEVICE (self)), bg_freqs);
+
+	if (!freq)
+		freq = (g_strcmp0 (band, "a") == 0) ? 5180 : 2462;
+
+	nm_ap_set_freq (ap, freq);
+}
+
+static NMActStageReturn
+act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	const char *iface = nm_device_get_iface (dev);
+	NMSupplicantConfig *config = NULL;
+	NMActRequest *req;
+	NMAccessPoint *ap;
+	NMConnection *connection;
+	const char *setting_name;
+	NMSettingWireless *s_wireless;
+
+	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	remove_supplicant_timeouts (self);
+
+	req = nm_device_get_act_request (dev);
+	g_assert (req);
+
+	ap = priv->current_ap;
+	if (!ap) {
+		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED;
+		goto out;
+	}
+
+	connection = nm_act_request_get_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) {
+		nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): access point '%s' has security,"
+		             " but secrets are required.",
+		             iface, nm_connection_get_id (connection));
+
+		ret = handle_auth_or_fail (self, req, FALSE);
+		if (ret == NM_ACT_STAGE_RETURN_FAILURE)
+			*reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
+		goto out;
+	}
+
+	/* have secrets, or no secrets required */
+	if (nm_connection_get_setting_wireless_security (connection)) {
+		nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): connection '%s' has security"
+		             ", and secrets exist.  No new secrets needed.",
+		             iface, nm_connection_get_id (connection));
+	} else {
+		nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): connection '%s' requires no "
+		             "security.  No secrets needed.",
+		             iface, nm_connection_get_id (connection));
+	}
+
+	priv->ssid_found = FALSE;
+
+	/* Supplicant requires an initial frequency for Ad-Hoc and Hotspot; if the user
+	 * didn't specify one and we didn't find an AP that matched the connection,
+	 * just pick a frequency the device supports.
+	 */
+	if ((nm_ap_get_mode (ap) == NM_802_11_MODE_ADHOC) || nm_ap_is_hotspot (ap))
+		ensure_hotspot_frequency (self, s_wireless, ap);
+
+	/* Build up the supplicant configuration */
+	config = build_supplicant_config (self, connection, nm_ap_get_freq (ap));
+	if (config == NULL) {
+		nm_log_err (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): couldn't build wireless configuration.",
+		             iface);
+		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
+		goto out;
+	}
+
+	/* Hook up error signal handler to capture association errors */
+	g_signal_connect (priv->sup_iface,
+	                  NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR,
+	                  G_CALLBACK (supplicant_iface_connection_error_cb),
+	                  self);
+
+	if (!nm_supplicant_interface_set_config (priv->sup_iface, config)) {
+		nm_log_err (LOGD_DEVICE | LOGD_WIFI,
+		            "Activation (%s/wireless): couldn't send wireless "
+		            "configuration to the supplicant.", iface);
+		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
+		goto out;
+	}
+
+	/* Set up a timeout on the association attempt to fail after 25 seconds */
+	priv->sup_timeout_id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self);
+
+	if (!priv->periodic_source_id)
+		priv->periodic_source_id = g_timeout_add_seconds (6, periodic_update_cb, self);
+
+	/* 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, TRUE);
+
+	if (config) {
+		/* Supplicant interface object refs the config; we no longer care about
+		 * it after this function.
+		 */
+		g_object_unref (config);
+	}
+	return ret;
+}
+
+static NMActStageReturn
+act_stage3_ip4_config_start (NMDevice *device,
+                             NMIP4Config **out_config,
+                             NMDeviceStateReason *reason)
+{
+	NMConnection *connection;
+	NMSettingIP4Config *s_ip4;
+	const char *method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
+
+	connection = nm_device_get_connection (device);
+	g_assert (connection);
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	if (s_ip4)
+		method = nm_setting_ip4_config_get_method (s_ip4);
+
+	/* Indicate that a critical protocol is about to start */
+	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0)
+		nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), TRUE);
+
+	return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip4_config_start (device, out_config, reason);
+}
+
+static NMActStageReturn
+act_stage3_ip6_config_start (NMDevice *device,
+                             NMIP6Config **out_config,
+                             NMDeviceStateReason *reason)
+{
+	NMConnection *connection;
+	NMSettingIP6Config *s_ip6;
+	const char *method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
+
+	connection = nm_device_get_connection (device);
+	g_assert (connection);
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	if (s_ip6)
+		method = nm_setting_ip6_config_get_method (s_ip6);
+
+	/* Indicate that a critical protocol is about to start */
+	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 ||
+	    strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0)
+		nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), TRUE);
+
+	return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip6_config_start (device, out_config, reason);
+}
+
+static void
+ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
+{
+	NMConnection *connection;
+	NMSettingWireless *s_wifi;
+	guint32 mtu;
+
+	connection = nm_device_get_connection (device);
+	g_assert (connection);
+	s_wifi = nm_connection_get_setting_wireless (connection);
+	g_assert (s_wifi);
+
+	/* MTU override */
+	mtu = nm_setting_wireless_get_mtu (s_wifi);
+	if (mtu)
+		nm_ip4_config_set_mtu (config, mtu);
+}
+
+static gboolean
+is_static_wep (NMConnection *connection)
+{
+	NMSettingWirelessSecurity *s_wsec;
+	const char *str;
+
+	g_return_val_if_fail (connection != NULL, FALSE);
+
+	s_wsec = nm_connection_get_setting_wireless_security (connection);
+	if (!s_wsec)
+		return FALSE;
+
+	str = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+	if (g_strcmp0 (str, "none") != 0)
+		return FALSE;
+
+	str = nm_setting_wireless_security_get_auth_alg (s_wsec);
+	if (g_strcmp0 (str, "leap") == 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+static NMActStageReturn
+handle_ip_config_timeout (NMDeviceWifi *self,
+                          NMConnection *connection,
+                          gboolean may_fail,
+                          gboolean *chain_up,
+                          NMDeviceStateReason *reason)
+{
+	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+
+	g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+	if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP) {
+		*chain_up = TRUE;
+		return ret;
+	}
+
+	/* If IP configuration times out and it's a static WEP connection, that
+	 * usually means the WEP key is wrong.  WEP's Open System auth mode has
+	 * no provision for figuring out if the WEP key is wrong, so you just have
+	 * to wait for DHCP to fail to figure it out.  For all other WiFi security
+	 * types (open, WPA, 802.1x, etc) if the secrets/certs were wrong the
+	 * connection would have failed before IP configuration.
+	 */
+	if (!may_fail && is_static_wep (connection)) {
+		/* Activation failed, we must have bad encryption key */
+		nm_log_warn (LOGD_DEVICE | LOGD_WIFI,
+		             "Activation (%s/wireless): could not get IP configuration for "
+		             "connection '%s'.",
+		             nm_device_get_iface (NM_DEVICE (self)),
+		             nm_connection_get_id (connection));
+
+		ret = handle_auth_or_fail (self, NULL, TRUE);
+		if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
+			nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+			             "Activation (%s/wireless): asking for new secrets",
+			             nm_device_get_iface (NM_DEVICE (self)));
+		} else {
+			*reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
+		}
+	} else {
+		/* Not static WEP or failure allowed; let superclass handle it */
+		*chain_up = TRUE;
+	}
+
+	return ret;
+}
+
+
+static NMActStageReturn
+act_stage4_ip4_config_timeout (NMDevice *dev, NMDeviceStateReason *reason)
+{
+	NMConnection *connection;
+	NMSettingIP4Config *s_ip4;
+	gboolean may_fail = FALSE, chain_up = FALSE;
+	NMActStageReturn ret;
+
+	connection = nm_device_get_connection (dev);
+	g_assert (connection);
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	may_fail = nm_setting_ip4_config_get_may_fail (s_ip4);
+
+	ret = handle_ip_config_timeout (NM_DEVICE_WIFI (dev), connection, may_fail, &chain_up, reason);
+	if (chain_up)
+		ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip4_config_timeout (dev, reason);
+
+	return ret;
+}
+
+static NMActStageReturn
+act_stage4_ip6_config_timeout (NMDevice *dev, NMDeviceStateReason *reason)
+{
+	NMConnection *connection;
+	NMSettingIP6Config *s_ip6;
+	gboolean may_fail = FALSE, chain_up = FALSE;
+	NMActStageReturn ret;
+
+	connection = nm_device_get_connection (dev);
+	g_assert (connection);
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	may_fail = nm_setting_ip6_config_get_may_fail (s_ip6);
+
+	ret = handle_ip_config_timeout (NM_DEVICE_WIFI (dev), connection, may_fail, &chain_up, reason);
+	if (chain_up)
+		ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip6_config_timeout (dev, reason);
+
+	return ret;
+}
+
+static void
+activation_success_handler (NMDevice *dev)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	int ifindex = nm_device_get_ifindex (dev);
+	NMAccessPoint *ap;
+	struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} };
+	NMAccessPoint *tmp_ap = NULL;
+	NMActRequest *req;
+	NMConnection *connection;
+
+	req = nm_device_get_act_request (dev);
+	g_assert (req);
+
+	connection = nm_act_request_get_connection (req);
+	g_assert (connection);
+
+	/* Clear any critical protocol notification in the wifi stack */
+	nm_platform_wifi_indicate_addressing_running (ifindex, FALSE);
+
+	/* Clear wireless secrets tries on success */
+	g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL);
+
+	ap = priv->current_ap;
+
+	/* If the AP isn't fake, it was found in the scan list and all its
+	 * details are known.
+	 */
+	if (!ap || !nm_ap_get_fake (ap)){
+		ap = NULL;
+		goto done;
+	}
+
+	/* If the activate AP was fake, it probably won't have a BSSID at all.
+	 * But if activation was successful, the card will know the BSSID.  Grab
+	 * the BSSID off the card and fill in the BSSID of the activation AP.
+	 */
+	nm_platform_wifi_get_bssid (ifindex, &bssid);
+	if (!nm_ethernet_address_is_valid (nm_ap_get_address (ap)))
+		nm_ap_set_address (ap, &bssid);
+	if (!nm_ap_get_freq (ap))
+		nm_ap_set_freq (ap, nm_platform_wifi_get_frequency (ifindex));
+	if (!nm_ap_get_max_bitrate (ap))
+		nm_ap_set_max_bitrate (ap, nm_platform_wifi_get_rate (ifindex));
+
+	tmp_ap = find_active_ap (self, ap, TRUE);
+	if (tmp_ap) {
+		const GByteArray *ssid = nm_ap_get_ssid (tmp_ap);
+
+		/* Found a better match in the scan list than the fake AP.  Use it
+		 * instead.
+		 */
+
+		/* If the better match was a hidden AP, update it's SSID */
+		if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len))
+			nm_ap_set_ssid (tmp_ap, nm_ap_get_ssid (ap));
+
+		nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
+		                                          nm_ap_get_dbus_path (tmp_ap));
+	}
+
+done:
+	periodic_update (self, ap);
+
+	/* ap might be already unrefed, because it was a fake_ap. But we don't touch it... */
+	if (tmp_ap && ap == priv->current_ap) {
+		/* Strange, we would expect periodic_update() to find a better AP
+		 * then the fake one and reset it. Reset the fake current_ap to NULL
+		 * now, which will remove the fake ap.
+		 **/
+		set_current_ap (self, NULL, TRUE, FALSE);
+	}
+
+	/* No need to update seen BSSIDs cache, that is done by set_current_ap() already */
+
+	/* Reset scan interval to something reasonable */
+	priv->scan_interval = SCAN_INTERVAL_MIN + (SCAN_INTERVAL_STEP * 2);
+}
+
+static void
+activation_failure_handler (NMDevice *dev)
+{
+	NMConnection *connection;
+
+	connection = nm_device_get_connection (dev);
+	g_assert (connection);
+
+	/* Clear wireless secrets tries on failure */
+	g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, NULL);
+
+	/* Clear any critical protocol notification in the wifi stack */
+	nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (dev), FALSE);
+}
+
+static void
+device_state_changed (NMDevice *device,
+                      NMDeviceState new_state,
+                      NMDeviceState old_state,
+                      NMDeviceStateReason reason)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	gboolean clear_aps = FALSE;
+
+	if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) {
+		/* Clean up the supplicant interface because in these states the
+		 * device cannot be used.
+		 */
+		if (priv->sup_iface)
+			supplicant_interface_release (self);
+
+		if (priv->periodic_source_id) {
+			g_source_remove (priv->periodic_source_id);
+			priv->periodic_source_id = 0;
+		}
+
+		cleanup_association_attempt (self, TRUE);
+		remove_all_aps (self);
+	}
+
+	switch (new_state) {
+	case NM_DEVICE_STATE_UNMANAGED:
+		clear_aps = TRUE;
+		break;
+	case NM_DEVICE_STATE_UNAVAILABLE:
+		/* If the device is enabled and the supplicant manager is ready,
+		 * acquire a supplicant interface and transition to DISCONNECTED because
+		 * the device is now ready to use.
+		 */
+		if (priv->enabled && (nm_device_get_firmware_missing (device) == FALSE)) {
+			if (!priv->sup_iface)
+				supplicant_interface_acquire (self);
+		}
+		clear_aps = TRUE;
+		break;
+	case NM_DEVICE_STATE_NEED_AUTH:
+		if (priv->sup_iface)
+			nm_supplicant_interface_disconnect (priv->sup_iface);
+		break;
+	case NM_DEVICE_STATE_IP_CHECK:
+		/* Clear any critical protocol notification in the wifi stack */
+		nm_platform_wifi_indicate_addressing_running (nm_device_get_ifindex (device), FALSE);
+		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:
+		/* Kick off a scan to get latest results */
+		priv->scan_interval = SCAN_INTERVAL_MIN;
+		cancel_pending_scan (self);
+		request_wireless_scan (self);
+		break;
+	default:
+		break;
+	}
+
+	if (clear_aps)
+		remove_all_aps (self);
+}
+
+static void
+set_enabled (NMDevice *device, gboolean enabled)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+	NMDeviceState state;
+
+	if (priv->enabled == enabled)
+		return;
+
+	priv->enabled = enabled;
+
+	nm_log_dbg (LOGD_WIFI, "(%s): device now %s",
+	            nm_device_get_iface (NM_DEVICE (device)),
+	            enabled ? "enabled" : "disabled");
+
+	state = nm_device_get_state (NM_DEVICE (self));
+	if (state < NM_DEVICE_STATE_UNAVAILABLE) {
+		nm_log_dbg (LOGD_WIFI, "(%s): %s blocked by UNMANAGED state",
+		            enabled ? "enable" : "disable",
+		            nm_device_get_iface (NM_DEVICE (device)));
+		return;
+	}
+
+	if (enabled) {
+		gboolean no_firmware = FALSE;
+
+		if (state != NM_DEVICE_STATE_UNAVAILABLE)
+			nm_log_warn (LOGD_CORE, "not in expected unavailable state!");
+
+		if (!nm_device_bring_up (NM_DEVICE (self), TRUE, &no_firmware)) {
+			nm_log_dbg (LOGD_WIFI, "(%s): enable blocked by failure to bring device up",
+			            nm_device_get_iface (NM_DEVICE (device)));
+
+			if (no_firmware)
+				nm_device_set_firmware_missing (NM_DEVICE (device), TRUE);
+			else {
+				/* The device sucks, or the kernel was lying to us about the killswitch state */
+				priv->enabled = FALSE;
+			}
+			return;
+		}
+
+		/* Re-initialize the supplicant interface and wait for it to be ready */
+		if (priv->sup_iface)
+			supplicant_interface_release (self);
+		supplicant_interface_acquire (self);
+
+		nm_log_dbg (LOGD_WIFI, "(%s): enable waiting on supplicant state",
+		            nm_device_get_iface (NM_DEVICE (device)));
+	} else {
+		nm_device_state_changed (NM_DEVICE (self),
+		                         NM_DEVICE_STATE_UNAVAILABLE,
+		                         NM_DEVICE_STATE_REASON_NONE);
+		nm_device_take_down (NM_DEVICE (self), TRUE);
+	}
+}
+
+/********************************************************************/
+
+NMDevice *
+nm_device_wifi_new (NMPlatformLink *platform_device)
+{
+	g_return_val_if_fail (platform_device != NULL, NULL);
+
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_WIFI,
+	                                  NM_DEVICE_PLATFORM_DEVICE, platform_device,
+	                                  NM_DEVICE_TYPE_DESC, "802.11 WiFi",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI,
+	                                  NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN,
+	                                  NULL);
+}
+
+static void
+nm_device_wifi_init (NMDeviceWifi *self)
+{
+	NM_DEVICE_WIFI_GET_PRIVATE (self)->mode = NM_802_11_MODE_INFRA;
+}
+
+static void
+dispose (GObject *object)
+{
+	NMDeviceWifi *self = NM_DEVICE_WIFI (object);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+	if (priv->disposed) {
+		G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object);
+		return;
+	}
+
+	priv->disposed = TRUE;
+
+	if (priv->periodic_source_id) {
+		g_source_remove (priv->periodic_source_id);
+		priv->periodic_source_id = 0;
+	}
+
+	cleanup_association_attempt (self, TRUE);
+	supplicant_interface_release (self);
+
+	g_clear_object (&priv->sup_mgr);
+
+	remove_all_aps (self);
+
+	G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object);
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMDeviceWifi *device = NM_DEVICE_WIFI (object);
+	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
+	GPtrArray *array;
+	GSList *iter;
+
+	switch (prop_id) {
+	case PROP_PERM_HW_ADDRESS:
+		g_value_take_string (value, nm_utils_hwaddr_ntoa (&priv->perm_hw_addr, ARPHRD_ETHER));
+		break;
+	case PROP_MODE:
+		g_value_set_uint (value, priv->mode);
+		break;
+	case PROP_BITRATE:
+		g_value_set_uint (value, priv->rate);
+		break;
+	case PROP_CAPABILITIES:
+		g_value_set_uint (value, priv->capabilities);
+		break;
+	case PROP_ACCESS_POINTS:
+		array = g_ptr_array_sized_new (4);
+		for (iter = priv->ap_list; iter; iter = g_slist_next (iter))
+			g_ptr_array_add (array, g_strdup (nm_ap_get_dbus_path (NM_AP (iter->data))));
+		g_value_take_boxed (value, array);
+		break;
+	case PROP_ACTIVE_ACCESS_POINT:
+		if (priv->current_ap)
+			g_value_set_boxed (value, nm_ap_get_dbus_path (priv->current_ap));
+		else
+			g_value_set_boxed (value, "/");
+		break;
+	case PROP_SCANNING:
+		g_value_set_boolean (value, nm_supplicant_interface_get_scanning (priv->sup_iface));
+		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)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+
+static void
+nm_device_wifi_class_init (NMDeviceWifiClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
+
+	g_type_class_add_private (object_class, sizeof (NMDeviceWifiPrivate));
+
+	object_class->constructor = constructor;
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->dispose = dispose;
+
+	parent_class->bring_up = bring_up;
+	parent_class->update_permanent_hw_address = update_permanent_hw_address;
+	parent_class->update_initial_hw_address = update_initial_hw_address;
+	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->check_connection_available_wifi_hidden = check_connection_available_wifi_hidden;
+	parent_class->complete_connection = complete_connection;
+	parent_class->set_enabled = set_enabled;
+
+	parent_class->act_stage1_prepare = act_stage1_prepare;
+	parent_class->act_stage2_config = act_stage2_config;
+	parent_class->ip4_config_pre_commit = ip4_config_pre_commit;
+	parent_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start;
+	parent_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start;
+	parent_class->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout;
+	parent_class->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout;
+	parent_class->deactivate = deactivate;
+
+	parent_class->state_changed = device_state_changed;
+
+	klass->scanning_allowed = scanning_allowed;
+
+	/* Properties */
+	g_object_class_install_property (object_class, PROP_PERM_HW_ADDRESS,
+		g_param_spec_string (NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS,
+		                     "Permanent MAC Address",
+		                     "Permanent hardware MAC address",
+		                     NULL,
+		                     G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_MODE,
+		g_param_spec_uint (NM_DEVICE_WIFI_MODE,
+		                   "Mode",
+		                   "Mode",
+		                   NM_802_11_MODE_UNKNOWN,
+		                   NM_802_11_MODE_AP,
+		                   NM_802_11_MODE_INFRA,
+		                   G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_BITRATE,
+		g_param_spec_uint (NM_DEVICE_WIFI_BITRATE,
+		                   "Bitrate",
+		                   "Bitrate",
+		                   0, G_MAXUINT32, 0,
+		                   G_PARAM_READABLE));
+
+	g_object_class_install_property
+		(object_class, PROP_ACCESS_POINTS,
+		 g_param_spec_boxed (NM_DEVICE_WIFI_ACCESS_POINTS,
+		                     "Access points",
+		                     "Access points",
+		                     DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH,
+		                     G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_ACTIVE_ACCESS_POINT,
+		g_param_spec_boxed (NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT,
+		                    "Active access point",
+		                    "Currently active access point",
+		                    DBUS_TYPE_G_OBJECT_PATH,
+		                    G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_CAPABILITIES,
+		g_param_spec_uint (NM_DEVICE_WIFI_CAPABILITIES,
+		                   "Wireless Capabilities",
+		                   "Wireless Capabilities",
+		                   0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE,
+		                   G_PARAM_READABLE));
+
+	g_object_class_install_property (object_class, PROP_SCANNING,
+		g_param_spec_boolean (NM_DEVICE_WIFI_SCANNING,
+		                   "Scanning",
+		                   "Scanning",
+		                   FALSE,
+		                   G_PARAM_READABLE));
+
+	/* Signals */
+	signals[ACCESS_POINT_ADDED] =
+		g_signal_new ("access-point-added",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NMDeviceWifiClass, access_point_added),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 1,
+		              G_TYPE_OBJECT);
+
+	signals[ACCESS_POINT_REMOVED] =
+		g_signal_new ("access-point-removed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              0,
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 1,
+		              G_TYPE_OBJECT);
+
+	signals[SCANNING_ALLOWED] =
+		g_signal_new ("scanning-allowed",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_LAST,
+		              G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_allowed),
+		              scanning_allowed_accumulator, NULL, NULL,
+		              G_TYPE_BOOLEAN, 0);
+
+	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
+	                                        G_TYPE_FROM_CLASS (klass),
+	                                        &dbus_glib_nm_device_wifi_object_info);
+
+	dbus_g_error_domain_register (NM_WIFI_ERROR, NULL, NM_TYPE_WIFI_ERROR);
+}
+
+
diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h
new file mode 100644
index 00000000..f0a1beac
--- /dev/null
+++ b/src/devices/wifi/nm-device-wifi.h
@@ -0,0 +1,94 @@
+/* -*- 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) 2005 - 2010 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_DEVICE_WIFI_H
+#define NM_DEVICE_WIFI_H
+
+#include <glib-object.h>
+#include <dbus/dbus.h>
+#include <net/ethernet.h>
+
+#include "nm-device.h"
+#include "nm-wifi-ap.h"
+
+struct NMAccessPointList;
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DEVICE_WIFI			(nm_device_wifi_get_type ())
+#define NM_DEVICE_WIFI(obj)			(G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIFI, NMDeviceWifi))
+#define NM_DEVICE_WIFI_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass))
+#define NM_IS_DEVICE_WIFI(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIFI))
+#define NM_IS_DEVICE_WIFI_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_WIFI))
+#define NM_DEVICE_WIFI_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_WIFI, NMDeviceWifiClass))
+
+typedef enum {
+	NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS = 0, /*< nick=ConnectionNotWireless >*/
+	NM_WIFI_ERROR_CONNECTION_INVALID,          /*< nick=ConnectionInvalid >*/
+	NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE,     /*< nick=ConnectionIncompatible >*/
+	NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND,      /*< nick=AccessPointNotFound >*/
+	NM_WIFI_ERROR_SCAN_NOT_ALLOWED,            /*< nick=ScanNotAllowed >*/
+	NM_WIFI_ERROR_AP_MODE_UNSUPPORTED,         /*< nick=ApModeUnsupported >*/
+	NM_WIFI_ERROR_ADHOC_MODE_UNSUPPORTED,      /*< nick=AdhocModeUnsupported >*/
+} NMWifiError;
+
+#define NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS "perm-hw-address"
+#define NM_DEVICE_WIFI_MODE                "mode"
+#define NM_DEVICE_WIFI_BITRATE             "bitrate"
+#define NM_DEVICE_WIFI_ACCESS_POINTS       "access-points"
+#define NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT "active-access-point"
+#define NM_DEVICE_WIFI_CAPABILITIES        "wireless-capabilities"
+#define NM_DEVICE_WIFI_SCANNING            "scanning"
+
+#ifndef NM_DEVICE_WIFI_DEFINED
+#define NM_DEVICE_WIFI_DEFINED
+typedef struct _NMDeviceWifi NMDeviceWifi;
+#endif
+
+typedef struct _NMDeviceWifiClass NMDeviceWifiClass;
+typedef struct _NMDeviceWifiPrivate NMDeviceWifiPrivate;
+
+struct _NMDeviceWifi
+{
+	NMDevice parent;
+
+	/*< private >*/
+	NMDeviceWifiPrivate *priv;
+};
+
+struct _NMDeviceWifiClass
+{
+	NMDeviceClass parent;
+
+	/* Signals */
+	void (*access_point_added)   (NMDeviceWifi *device, NMAccessPoint *ap);
+	void (*access_point_removed) (NMDeviceWifi *device, NMAccessPoint *ap);
+	gboolean (*scanning_allowed) (NMDeviceWifi *device);
+};
+
+
+GType nm_device_wifi_get_type (void);
+
+NMDevice *nm_device_wifi_new (NMPlatformLink *platform_device);
+
+G_END_DECLS
+
+#endif	/* NM_DEVICE_WIFI_H */
diff --git a/src/devices/wifi/nm-wifi-ap-utils.c b/src/devices/wifi/nm-wifi-ap-utils.c
new file mode 100644
index 00000000..9b03cbd4
--- /dev/null
+++ b/src/devices/wifi/nm-wifi-ap-utils.c
@@ -0,0 +1,721 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2011 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "nm-wifi-ap-utils.h"
+
+static gboolean
+verify_no_wep (NMSettingWirelessSecurity *s_wsec, const char *tag, GError **error)
+{
+	if (   nm_setting_wireless_security_get_wep_key (s_wsec, 0)
+	    || nm_setting_wireless_security_get_wep_key (s_wsec, 1)
+	    || nm_setting_wireless_security_get_wep_key (s_wsec, 2)
+	    || nm_setting_wireless_security_get_wep_key (s_wsec, 3)
+	    || nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec)
+	    || nm_setting_wireless_security_get_wep_key_type (s_wsec)) {
+		/* Dynamic WEP cannot have any WEP keys set */
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             "%s is incompatible with static WEP keys", tag);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+verify_leap (NMSettingWirelessSecurity *s_wsec,
+             NMSetting8021x *s_8021x,
+             gboolean adhoc,
+             GError **error)
+{
+	const char *key_mgmt, *auth_alg, *leap_username;
+
+	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+	auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
+	leap_username = nm_setting_wireless_security_get_leap_username (s_wsec);
+
+	/* One (or both) of two things indicates we want LEAP:
+	 * 1) auth_alg == 'leap'
+	 * 2) valid leap_username
+     *
+     * LEAP always requires a LEAP username.
+	 */
+
+	if (auth_alg) {
+		if (!strcmp (auth_alg, "leap")) {
+			/* LEAP authentication requires at least a LEAP username */
+			if (!leap_username) {
+				g_set_error_literal (error,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME,
+				                     "LEAP requires a LEAP username");
+				return FALSE;
+			}
+		} else if (leap_username) {
+			/* Leap username requires 'leap' auth */
+			g_set_error_literal (error,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			                     "LEAP requires 'leap' authentication");
+			return FALSE;
+		}
+	}
+
+	if (leap_username) {
+		if (key_mgmt && strcmp (key_mgmt, "ieee8021x")) {
+			/* LEAP requires ieee8021x key management */
+			g_set_error_literal (error,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_802_1X,
+			                     "LEAP requires IEEE 802.1x key management");
+			return FALSE;
+		}
+	}
+
+	/* At this point if auth_alg is set it must be 'leap', and if key_mgmt
+	 * is set it must be 'ieee8021x'.
+	 */
+	if (leap_username) {
+		if (auth_alg)
+			g_assert (strcmp (auth_alg, "leap") == 0);
+		if (key_mgmt)
+			g_assert (strcmp (key_mgmt, "ieee8021x") == 0);
+
+		if (adhoc) {
+			g_set_error_literal (error,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			                     "LEAP incompatible with Ad-Hoc mode");
+			return FALSE;
+		}
+
+		if (!verify_no_wep (s_wsec, "LEAP", error))
+			return FALSE;
+
+		if (s_8021x) {
+			g_set_error_literal (error,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME,
+			                     "LEAP incompatible with 802.1x setting");
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
+verify_no_wpa (NMSettingWirelessSecurity *s_wsec,
+               const char *tag,
+               GError **error)
+{
+	const char *key_mgmt;
+	int n, i;
+
+	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+	if (key_mgmt && !strncmp (key_mgmt, "wpa", 3)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             "%s incompatible with any WPA key management", tag);
+		return FALSE;
+	}
+
+	if (nm_setting_wireless_security_get_num_protos (s_wsec)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             "%s incompatible with any 'proto' setting", tag);
+		return FALSE;
+	}
+
+	n = nm_setting_wireless_security_get_num_pairwise (s_wsec);
+	for (i = 0; i < n; i++) {
+		const char *pw;
+
+		pw = nm_setting_wireless_security_get_pairwise (s_wsec, i);
+		if (!strcmp (pw, "tkip") || !strcmp (pw, "ccmp")) {
+			g_set_error (error,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			             "%s is incompatible with WPA pairwise ciphers", tag);
+			return FALSE;
+		}
+	}
+
+	n = nm_setting_wireless_security_get_num_groups (s_wsec);
+	for (i = 0; i < n; i++) {
+		const char *gr;
+
+		gr = nm_setting_wireless_security_get_group (s_wsec, i);
+		if (strcmp (gr, "wep40") && strcmp (gr, "wep104")) {
+			g_set_error (error,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR,
+			             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			             "%s is incompatible with WPA group ciphers", tag);
+			return FALSE;
+		}
+	}
+
+	if (nm_setting_wireless_security_get_psk (s_wsec)) {
+		g_set_error (error,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR,
+		             NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		             "%s is incompatible with a WPA Pre-Shared Key", tag);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+verify_dynamic_wep (NMSettingWirelessSecurity *s_wsec,
+                    NMSetting8021x *s_8021x,
+                    gboolean adhoc,
+                    GError **error)
+{
+	const char *key_mgmt, *auth_alg, *leap_username;
+
+	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+	auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
+	leap_username = nm_setting_wireless_security_get_leap_username (s_wsec);
+
+	g_return_val_if_fail (leap_username == NULL, TRUE);
+
+	if (key_mgmt) {
+		if (!strcmp (key_mgmt, "ieee8021x")) {
+			if (!s_8021x) {
+				/* 802.1x key management requires an 802.1x setting */
+				g_set_error_literal (error,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+				                     "Dynamic WEP requires an 802.1x setting");
+				return FALSE;
+			}
+
+			if (auth_alg && strcmp (auth_alg, "open")) {
+				/* 802.1x key management must use "open" authentication */
+				g_set_error_literal (error,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+				                     "Dynamic WEP requires 'open' authentication");
+				return FALSE;
+			}
+
+			/* Dynamic WEP incompatible with anything static WEP related */
+			if (!verify_no_wep (s_wsec, "Dynamic WEP", error))
+				return FALSE;
+		} else if (!strcmp (key_mgmt, "none")) {
+			if (s_8021x) {
+				/* 802.1x setting requires 802.1x key management */
+				g_set_error_literal (error,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+				                     "Dynamic WEP requires 'ieee8021x' key management");
+				return FALSE;
+			}
+		}
+	} else if (s_8021x) {
+		/* 802.1x setting incompatible with anything but 'open' auth */
+		if (auth_alg && strcmp (auth_alg, "open")) {
+			/* 802.1x key management must use "open" authentication */
+			g_set_error_literal (error,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			                     "Dynamic WEP requires 'open' authentication");
+			return FALSE;
+		}
+
+		/* Dynamic WEP incompatible with anything static WEP related */
+		if (!verify_no_wep (s_wsec, "Dynamic WEP", error))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+verify_wpa_psk (NMSettingWirelessSecurity *s_wsec,
+                NMSetting8021x *s_8021x,
+                gboolean adhoc,
+                guint32 wpa_flags,
+                guint32 rsn_flags,
+                GError **error)
+{
+	const char *key_mgmt, *auth_alg, *tmp;
+	int n;
+
+	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+	auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
+
+	if (key_mgmt) {
+		if (!strcmp (key_mgmt, "wpa-psk") || !strcmp (key_mgmt, "wpa-none")) {
+			if (s_8021x) {
+				g_set_error_literal (error,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+				                     "WPA-PSK incompatible with 802.1x");
+				return FALSE;
+			}
+
+			if (auth_alg && strcmp (auth_alg, "open")) {
+				/* WPA must use "open" authentication */
+				g_set_error_literal (error,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+				                     "WPA-PSK requires 'open' authentication");
+				return FALSE;
+			}
+		}
+
+		if (!strcmp (key_mgmt, "wpa-none")) {
+			if (!adhoc) {
+				g_set_error_literal (error,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+				                     "WPA Ad-Hoc requires an Ad-Hoc mode AP");
+				return FALSE;
+			}
+
+			/* Ad-Hoc WPA requires 'wpa' proto, 'none' pairwise, and 'tkip' group */
+			n = nm_setting_wireless_security_get_num_protos (s_wsec);
+			tmp = (n > 0) ? nm_setting_wireless_security_get_proto (s_wsec, 0) : NULL;
+			if (n > 1 || !tmp || strcmp (tmp, "wpa")) {
+				g_set_error_literal (error,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+				                     "WPA Ad-Hoc requires 'wpa' proto");
+				return FALSE;
+			}
+
+			n = nm_setting_wireless_security_get_num_pairwise (s_wsec);
+			tmp = (n > 0) ? nm_setting_wireless_security_get_pairwise (s_wsec, 0) : NULL;
+			if (n > 1 || g_strcmp0 (tmp, "none")) {
+				g_set_error_literal (error,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+				                     "WPA Ad-Hoc requires 'none' pairwise cipher");
+				return FALSE;
+			}
+
+			n = nm_setting_wireless_security_get_num_groups (s_wsec);
+			tmp = (n > 0) ? nm_setting_wireless_security_get_group (s_wsec, 0) : NULL;
+			if (n > 1 || !tmp || strcmp (tmp, "tkip")) {
+				g_set_error_literal (error,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+				                     "WPA Ad-Hoc requires 'tkip' group cipher");
+				return FALSE;
+			}
+		}
+
+		if (!strcmp (key_mgmt, "wpa-psk")) {
+			/* Make sure the AP's capabilities support WPA-PSK */
+			if (   !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
+			    && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) {
+				g_set_error_literal (error,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+				                     "AP does not support PSK but setting requires it");
+				return FALSE;
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
+verify_wpa_eap (NMSettingWirelessSecurity *s_wsec,
+                NMSetting8021x *s_8021x,
+                guint32 wpa_flags,
+                guint32 rsn_flags,
+                GError **error)
+{
+	const char *key_mgmt, *auth_alg;
+	gboolean is_wpa_eap = FALSE;
+
+	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+	auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
+
+	if (key_mgmt) {
+		if (!strcmp (key_mgmt, "wpa-eap")) {
+			if (!s_8021x) {
+				g_set_error_literal (error,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+				                     "WPA-EAP requires an 802.1x setting");
+				return FALSE;
+			}
+
+			if (auth_alg && strcmp (auth_alg, "open")) {
+				/* WPA must use "open" authentication */
+				g_set_error_literal (error,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+				                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+				                     "WPA-EAP requires 'open' authentication");
+				return FALSE;
+			}
+
+			is_wpa_eap = TRUE;
+		} else if (s_8021x) {
+			g_set_error_literal (error,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			                     "Setting requires 802.1x but does not use 'wpa-eap' key management");
+			return FALSE;
+		}
+	}
+
+	if (is_wpa_eap || s_8021x) {
+		/* Make sure the AP's capabilities support WPA-EAP */
+		if (   !(wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)
+		    && !(rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) {
+			g_set_error_literal (error,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			                     "AP does not support 802.1x but setting requires it");
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
+verify_adhoc (NMSettingWirelessSecurity *s_wsec,
+              NMSetting8021x *s_8021x,
+              gboolean adhoc,
+              GError **error)
+{
+	const char *key_mgmt = NULL, *leap_username = NULL, *auth_alg = NULL;
+
+	if (s_wsec) {
+		key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+		auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
+		leap_username = nm_setting_wireless_security_get_leap_username (s_wsec);
+	}
+
+	if (adhoc) {
+		if (key_mgmt && strcmp (key_mgmt, "wpa-none") && strcmp (key_mgmt, "none")) {
+			g_set_error_literal (error,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			                     "AP mode is Ad-Hoc but setting requires Infrastructure security");
+			return FALSE;
+		}
+
+		if (s_8021x) {
+			g_set_error_literal (error,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			                     "Ad-Hoc mode incompatible with 802.1x security");
+			return FALSE;
+		}
+
+		if (leap_username) {
+			g_set_error_literal (error,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			                     "Ad-Hoc mode incompatible with LEAP security");
+			return FALSE;
+		}
+
+		if (auth_alg && strcmp (auth_alg, "open")) {
+			g_set_error_literal (error,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			                     "Ad-Hoc mode requires 'open' authentication");
+			return FALSE;
+		}
+	} else {
+		if (key_mgmt && !strcmp (key_mgmt, "wpa-none")) {
+			g_set_error_literal (error,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			                     "AP mode is Infrastructure but setting requires Ad-Hoc security");
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+gboolean
+nm_ap_utils_complete_connection (const GByteArray *ap_ssid,
+                                 const guint8 ap_bssid[ETH_ALEN],
+                                 NM80211Mode ap_mode,
+                                 guint32 ap_flags,
+                                 guint32 ap_wpa_flags,
+                                 guint32 ap_rsn_flags,
+                                 NMConnection *connection,
+                                 gboolean lock_bssid,
+                                 GError **error)
+{
+	NMSettingWireless *s_wifi;
+	NMSettingWirelessSecurity *s_wsec;
+	NMSetting8021x *s_8021x;
+	const GByteArray *ssid;
+	const char *mode, *key_mgmt, *auth_alg, *leap_username;
+	gboolean adhoc = FALSE;
+
+	s_wifi = nm_connection_get_setting_wireless (connection);
+	g_assert (s_wifi);
+	s_wsec = nm_connection_get_setting_wireless_security (connection);
+	s_8021x = nm_connection_get_setting_802_1x (connection);
+
+	/* Fill in missing SSID */
+	ssid = nm_setting_wireless_get_ssid (s_wifi);
+	if (!ssid)
+		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_SSID, ap_ssid, NULL);
+	else if (   ssid->len != ap_ssid->len
+	         || memcmp (ssid->data, ap_ssid->data, ssid->len)) {
+		g_set_error_literal (error,
+		                     NM_SETTING_WIRELESS_ERROR,
+		                     NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+		                     "Setting SSID did not match AP SSID");
+		return FALSE;
+	}
+
+	if (lock_bssid && !nm_setting_wireless_get_bssid (s_wifi)) {
+		GByteArray *bssid;
+
+		bssid = g_byte_array_sized_new (ETH_ALEN);
+		g_byte_array_append (bssid, ap_bssid, ETH_ALEN);
+		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_BSSID, bssid, NULL);
+		g_byte_array_free (bssid, TRUE);
+	}
+
+	/* And mode */
+	mode = nm_setting_wireless_get_mode (s_wifi);
+	if (mode) {
+		gboolean valid = FALSE;
+
+		/* Make sure the supplied mode matches the AP's */
+		if (   !strcmp (mode, NM_SETTING_WIRELESS_MODE_INFRA)
+		    || !strcmp (mode, NM_SETTING_WIRELESS_MODE_AP)) {
+			if (ap_mode == NM_802_11_MODE_INFRA)
+				valid = TRUE;
+		} else if (!strcmp (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) {
+			if (ap_mode == NM_802_11_MODE_ADHOC)
+				valid = TRUE;
+			adhoc = TRUE;
+		}
+
+		if (valid == FALSE) {
+			g_set_error (error,
+			             NM_SETTING_WIRELESS_ERROR,
+			             NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY,
+			             NM_SETTING_WIRELESS_MODE);
+			return FALSE;
+		}
+	} else {
+		mode = NM_SETTING_WIRELESS_MODE_INFRA;
+		if (ap_mode == NM_802_11_MODE_ADHOC) {
+			mode = NM_SETTING_WIRELESS_MODE_ADHOC;
+			adhoc = TRUE;
+		}
+		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, mode, NULL);
+	}
+
+	/* Security */
+
+	/* Open */
+	if (   !(ap_flags & NM_802_11_AP_FLAGS_PRIVACY)
+	    && (ap_wpa_flags == NM_802_11_AP_SEC_NONE)
+	    && (ap_rsn_flags == NM_802_11_AP_SEC_NONE)) {
+		/* Make sure the connection doesn't specify security */
+		if (s_wsec || s_8021x) {
+			g_set_error_literal (error,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+			                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+			                     "AP is unencrypted but setting specifies security");
+			return FALSE;
+		}
+		return TRUE;
+	}
+
+	/* Everything else requires security */
+	if (!s_wsec) {
+		s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
+		nm_connection_add_setting (connection, NM_SETTING (s_wsec));
+	}
+
+	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+	auth_alg = nm_setting_wireless_security_get_auth_alg (s_wsec);
+	leap_username = nm_setting_wireless_security_get_leap_username (s_wsec);
+
+	/* Ad-Hoc checks */
+	if (!verify_adhoc (s_wsec, s_8021x, adhoc, error))
+		return FALSE;
+
+	/* Static WEP, Dynamic WEP, or LEAP */
+	if (   (ap_flags & NM_802_11_AP_FLAGS_PRIVACY)
+	    && (ap_wpa_flags == NM_802_11_AP_SEC_NONE)
+	    && (ap_rsn_flags == NM_802_11_AP_SEC_NONE)) {
+		const char *tag = "WEP";
+		gboolean is_dynamic_wep = FALSE;
+
+		if (!verify_leap (s_wsec, s_8021x, adhoc, error))
+			return FALSE;
+
+		if (leap_username) {
+			tag = "LEAP";
+		} else {
+			/* Static or Dynamic WEP */
+			if (!verify_dynamic_wep (s_wsec, s_8021x, adhoc, error))
+				return FALSE;
+
+			if (s_8021x || (key_mgmt && !strcmp (key_mgmt, "ieee8021x"))) {
+				is_dynamic_wep = TRUE;
+				tag = "Dynamic WEP";
+			}
+		}
+
+		/* Nothing WPA-related can be set */
+		if (!verify_no_wpa (s_wsec, tag, error))
+			return FALSE;
+
+		if (leap_username) {
+			/* LEAP */
+			g_object_set (s_wsec,
+			              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x",
+			              NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap",
+			              NULL);
+		} else if (is_dynamic_wep) {
+			/* Dynamic WEP */
+			g_object_set (s_wsec,
+			              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x",
+			              NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open",
+			              NULL);
+
+			if (s_8021x) {
+				/* Dynamic WEP requires a valid 802.1x setting since we can't
+				 * autocomplete 802.1x.
+				 */
+				if (!nm_setting_verify (NM_SETTING (s_8021x), NULL, error))
+					return FALSE;
+			}
+		} else {
+			/* Static WEP */
+			g_object_set (s_wsec,
+			              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none",
+			              NULL);
+		}
+
+		return TRUE;
+	}
+
+	/* WPA/RSN */
+	g_assert (ap_wpa_flags || ap_rsn_flags);
+
+	/* Ensure key management is valid for WPA */
+	if ((key_mgmt && !strcmp (key_mgmt, "ieee8021x")) || leap_username) {
+		g_set_error_literal (error,
+		                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+		                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		                     "WPA incompatible with non-EAP (original) LEAP or Dynamic WEP");
+		return FALSE;
+	}
+
+	/* 'shared' auth incompatible with any type of WPA */
+	if (auth_alg && strcmp (auth_alg, "open")) {
+		g_set_error_literal (error,
+		                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+		                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		                     "WPA incompatible with Shared Key authentication");
+		return FALSE;
+	}
+
+	if (!verify_no_wep (s_wsec, "WPA", error))
+		return FALSE;
+
+	if (!verify_wpa_psk (s_wsec, s_8021x, adhoc, ap_wpa_flags, ap_rsn_flags, error))
+		return FALSE;
+
+	if (!adhoc && !verify_wpa_eap (s_wsec, s_8021x, ap_wpa_flags, ap_rsn_flags, error))
+		return FALSE;
+
+	if (adhoc) {
+		g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-none", NULL);
+		/* Ad-Hoc does not support RSN/WPA2 */
+		nm_setting_wireless_security_add_proto (s_wsec, "wpa");
+		nm_setting_wireless_security_add_pairwise (s_wsec, "none");
+		nm_setting_wireless_security_add_group (s_wsec, "tkip");
+	} else if (s_8021x) {
+		g_object_set (s_wsec,
+		              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap",
+		              NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open",
+		              NULL);
+		/* Leave proto/pairwise/group as client set them; if they are unset the
+		 * supplicant will figure out the best combination at connect time.
+		 */
+
+		/* 802.1x also requires the client to completely fill in the 8021x
+		 * setting.  Since there's so much configuration required for it, there's
+		 * no way it can be automatically completed.
+		 */
+	} else if (   (key_mgmt && !strcmp (key_mgmt, "wpa-psk"))
+	           || (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
+	           || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) {
+		g_object_set (s_wsec,
+		              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk",
+		              NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open",
+		              NULL);
+		/* Leave proto/pairwise/group as client set them; if they are unset the
+		 * supplicant will figure out the best combination at connect time.
+		 */
+	} else {
+		g_set_error_literal (error,
+		                     NM_SETTING_WIRELESS_SECURITY_ERROR,
+		                     NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY,
+		                     "Failed to determine AP security information");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+guint32
+nm_ap_utils_level_to_quality (gint val)
+{
+	if (val < 0) {
+		/* Assume dBm already; rough conversion: best = -40, worst = -100 */
+		val = abs (CLAMP (val, -100, -40) + 40);  /* normalize to 0 */
+		val = 100 - (int) ((100.0 * (double) val) / 60.0);
+	} else if (val > 110 && val < 256) {
+		/* assume old-style WEXT 8-bit unsigned signal level */
+		val -= 256;  /* subtract 256 to convert to dBm */
+		val = abs (CLAMP (val, -100, -40) + 40);  /* normalize to 0 */
+		val = 100 - (int) ((100.0 * (double) val) / 60.0);
+	} else {
+		/* Assume signal is a "quality" percentage */
+		val = CLAMP (val, 0, 100);
+	}
+	g_assert (val >= 0);
+
+	return (guint32) val;
+}
+
diff --git a/src/devices/wifi/nm-wifi-ap-utils.h b/src/devices/wifi/nm-wifi-ap-utils.h
new file mode 100644
index 00000000..992b839d
--- /dev/null
+++ b/src/devices/wifi/nm-wifi-ap-utils.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2011 Red Hat, Inc.
+ */
+
+#ifndef NM_WIFI_AP_UTILS_H
+#define NM_WIFI_AP_UTILS_H
+
+#include <net/ethernet.h>
+
+#include <NetworkManager.h>
+#include <nm-connection.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-wireless-security.h>
+#include <nm-setting-8021x.h>
+
+gboolean nm_ap_utils_complete_connection (const GByteArray *ssid,
+                                          const guint8 bssid[ETH_ALEN],
+                                          NM80211Mode mode,
+                                          guint32 flags,
+                                          guint32 wpa_flags,
+                                          guint32 rsn_flags,
+                                          NMConnection *connection,
+                                          gboolean lock_bssid,
+                                          GError **error);
+
+guint32 nm_ap_utils_level_to_quality (gint val);
+
+#endif  /* NM_WIFI_AP_UTILS_H */
+
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
new file mode 100644
index 00000000..363be2e3
--- /dev/null
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -0,0 +1,1293 @@
+/* -*- 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) 2004 - 2011 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <netinet/ether.h>
+
+#include "nm-wifi-ap.h"
+#include "nm-wifi-ap-utils.h"
+#include "NetworkManagerUtils.h"
+#include "nm-utils.h"
+#include "nm-logging.h"
+#include "nm-dbus-manager.h"
+
+#include "nm-setting-wireless.h"
+#include "nm-glib-compat.h"
+
+#include "nm-access-point-glue.h"
+
+/*
+ * Encapsulates Access Point information
+ */
+typedef struct
+{
+	char *dbus_path;
+	char *supplicant_path;   /* D-Bus object path of this AP from wpa_supplicant */
+
+	/* Scanned or cached values */
+	GByteArray *	ssid;
+	struct ether_addr	address;
+	NM80211Mode		mode;
+	gint8			strength;
+	guint32			freq;		/* Frequency in MHz; ie 2412 (== 2.412 GHz) */
+	guint32			max_bitrate;/* Maximum bitrate of the AP in Kbit/s (ie 54000 Kb/s == 54Mbit/s) */
+
+	NM80211ApFlags         flags;      /* General flags */
+	NM80211ApSecurityFlags wpa_flags;  /* WPA-related flags */
+	NM80211ApSecurityFlags rsn_flags;  /* RSN (WPA2) -related flags */
+
+	/* Non-scanned attributes */
+	gboolean			fake;	/* Whether or not the AP is from a scan */
+	gboolean            hotspot;    /* Whether the AP is a local device's hotspot network */
+	gboolean			broadcast;	/* Whether or not the AP is broadcasting (hidden) */
+	gint32              last_seen;  /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */
+} NMAccessPointPrivate;
+
+#define NM_AP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_AP, NMAccessPointPrivate))
+
+G_DEFINE_TYPE (NMAccessPoint, nm_ap, G_TYPE_OBJECT)
+
+enum {
+	PROP_0,
+	PROP_FLAGS,
+	PROP_WPA_FLAGS,
+	PROP_RSN_FLAGS,
+	PROP_SSID,
+	PROP_FREQUENCY,
+	PROP_HW_ADDRESS,
+	PROP_MODE,
+	PROP_MAX_BITRATE,
+	PROP_STRENGTH,
+	LAST_PROP
+};
+
+static void
+nm_ap_init (NMAccessPoint *ap)
+{
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (ap);
+
+	priv->dbus_path = NULL;
+	priv->mode = NM_802_11_MODE_INFRA;
+	priv->flags = NM_802_11_AP_FLAGS_NONE;
+	priv->wpa_flags = NM_802_11_AP_SEC_NONE;
+	priv->rsn_flags = NM_802_11_AP_SEC_NONE;
+	priv->broadcast = TRUE;
+}
+
+static void
+finalize (GObject *object)
+{
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object);
+
+	g_free (priv->dbus_path);
+	g_free (priv->supplicant_path);
+	if (priv->ssid)
+		g_byte_array_free (priv->ssid, TRUE);
+
+	G_OBJECT_CLASS (nm_ap_parent_class)->finalize (object);
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+		    const GValue *value, GParamSpec *pspec)
+{
+	NMAccessPoint *ap = NM_AP (object);
+
+	switch (prop_id) {
+	case PROP_FLAGS:
+		nm_ap_set_flags (ap, g_value_get_uint (value));
+		break;
+	case PROP_WPA_FLAGS:
+		nm_ap_set_wpa_flags (ap, g_value_get_uint (value));
+		break;
+	case PROP_RSN_FLAGS:
+		nm_ap_set_rsn_flags (ap, g_value_get_uint (value));
+		break;
+	case PROP_SSID:
+		nm_ap_set_ssid (ap, (GByteArray *) g_value_get_boxed (value));
+		break;
+	case PROP_FREQUENCY:
+		nm_ap_set_freq (ap, g_value_get_uint (value));
+		break;
+	case PROP_MODE:
+		nm_ap_set_mode (ap, g_value_get_uint (value));
+		break;
+	case PROP_MAX_BITRATE:
+		nm_ap_set_max_bitrate (ap, g_value_get_uint (value));
+		break;
+	case PROP_STRENGTH:
+		nm_ap_set_strength (ap, g_value_get_schar (value));
+		break;
+	case PROP_HW_ADDRESS:
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec)
+{
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (object);
+	GArray * ssid;
+	int len;
+	int i;
+
+	switch (prop_id) {
+	case PROP_FLAGS:
+		g_value_set_uint (value, priv->flags);
+		break;
+	case PROP_WPA_FLAGS:
+		g_value_set_uint (value, priv->wpa_flags);
+		break;
+	case PROP_RSN_FLAGS:
+		g_value_set_uint (value, priv->rsn_flags);
+		break;
+	case PROP_SSID:
+		len = priv->ssid ? priv->ssid->len : 0;
+		ssid = g_array_sized_new (FALSE, TRUE, sizeof (unsigned char), len);
+		for (i = 0; i < len; i++)
+			g_array_append_val (ssid, priv->ssid->data[i]);
+		g_value_set_boxed (value, ssid);
+		g_array_free (ssid, TRUE);
+		break;
+	case PROP_FREQUENCY:
+		g_value_set_uint (value, priv->freq);
+		break;
+	case PROP_HW_ADDRESS:
+		g_value_take_string (value, nm_utils_hwaddr_ntoa (&priv->address, ARPHRD_ETHER));
+		break;
+	case PROP_MODE:
+		g_value_set_uint (value, priv->mode);
+		break;
+	case PROP_MAX_BITRATE:
+		g_value_set_uint (value, priv->max_bitrate);
+		break;
+	case PROP_STRENGTH:
+		g_value_set_schar (value, priv->strength);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nm_ap_class_init (NMAccessPointClass *ap_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (ap_class);
+	const NM80211ApSecurityFlags all_sec_flags =   NM_802_11_AP_SEC_NONE
+	                                             | NM_802_11_AP_SEC_PAIR_WEP40
+	                                             | NM_802_11_AP_SEC_PAIR_WEP104
+	                                             | NM_802_11_AP_SEC_PAIR_TKIP
+	                                             | NM_802_11_AP_SEC_PAIR_CCMP
+	                                             | NM_802_11_AP_SEC_GROUP_WEP40
+	                                             | NM_802_11_AP_SEC_GROUP_WEP104
+	                                             | NM_802_11_AP_SEC_GROUP_TKIP
+	                                             | NM_802_11_AP_SEC_GROUP_CCMP
+	                                             | NM_802_11_AP_SEC_KEY_MGMT_PSK
+	                                             | NM_802_11_AP_SEC_KEY_MGMT_802_1X;
+
+	g_type_class_add_private (ap_class, sizeof (NMAccessPointPrivate));
+
+	/* virtual methods */
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+	object_class->finalize = finalize;
+
+	/* properties */
+	g_object_class_install_property
+		(object_class, PROP_FLAGS,
+		 g_param_spec_uint (NM_AP_FLAGS,
+							"Flags",
+							"Flags",
+							NM_802_11_AP_FLAGS_NONE,
+							NM_802_11_AP_FLAGS_PRIVACY,
+							NM_802_11_AP_FLAGS_NONE,
+							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_WPA_FLAGS,
+		 g_param_spec_uint (NM_AP_WPA_FLAGS,
+							"WPA Flags",
+							"WPA Flags",
+							NM_802_11_AP_SEC_NONE,
+							all_sec_flags,
+							NM_802_11_AP_SEC_NONE,
+							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_RSN_FLAGS,
+		 g_param_spec_uint (NM_AP_RSN_FLAGS,
+							"RSN Flags",
+							"RSN Flags",
+							NM_802_11_AP_SEC_NONE,
+							all_sec_flags,
+							NM_802_11_AP_SEC_NONE,
+							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_SSID,
+	     g_param_spec_boxed (NM_AP_SSID,
+	                         "SSID",
+	                         "SSID",
+	                         DBUS_TYPE_G_UCHAR_ARRAY,
+	                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_FREQUENCY,
+		 g_param_spec_uint (NM_AP_FREQUENCY,
+							"Frequency",
+							"Frequency",
+							0, 10000, 0,
+							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_HW_ADDRESS,
+		 g_param_spec_string (NM_AP_HW_ADDRESS,
+							  "MAC Address",
+							  "Hardware MAC address",
+							  NULL,
+							  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	
+	g_object_class_install_property
+		(object_class, PROP_MODE,
+		 g_param_spec_uint (NM_AP_MODE,
+						   "Mode",
+						   "Mode",
+						   NM_802_11_MODE_ADHOC, NM_802_11_MODE_INFRA, NM_802_11_MODE_INFRA,
+						   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_MAX_BITRATE,
+		 g_param_spec_uint (NM_AP_MAX_BITRATE,
+							"Max Bitrate",
+							"Max Bitrate",
+							0, G_MAXUINT16, 0,
+							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property
+		(object_class, PROP_STRENGTH,
+		 g_param_spec_char (NM_AP_STRENGTH,
+							"Strength",
+							"Strength",
+							G_MININT8, G_MAXINT8, 0,
+							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
+	                                        G_TYPE_FROM_CLASS (ap_class),
+	                                        &dbus_glib_nm_access_point_object_info);
+}
+
+void
+nm_ap_export_to_dbus (NMAccessPoint *ap)
+{
+	NMAccessPointPrivate *priv;
+	static guint32 counter = 0;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->dbus_path) {
+		nm_log_err (LOGD_CORE, "Tried to export AP %s twice.", priv->dbus_path);
+		return;
+	}
+
+	priv->dbus_path = g_strdup_printf (NM_DBUS_PATH_ACCESS_POINT "/%d", counter++);
+	nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->dbus_path, ap);
+}
+
+/*
+ * nm_ap_new
+ *
+ * Create a new, blank user access point info structure
+ *
+ */
+static NMAccessPoint *
+nm_ap_new (void)
+{
+	return (NMAccessPoint *) g_object_new (NM_TYPE_AP, NULL);
+}
+
+static NM80211ApSecurityFlags
+pair_to_flags (const char *str)
+{
+	g_return_val_if_fail (str != NULL, NM_802_11_AP_SEC_NONE);
+
+	if (strcmp (str, "tkip") == 0)
+		return NM_802_11_AP_SEC_PAIR_TKIP;
+	if (strcmp (str, "ccmp") == 0)
+		return NM_802_11_AP_SEC_PAIR_CCMP;
+	return NM_802_11_AP_SEC_NONE;
+}
+
+static NM80211ApSecurityFlags
+group_to_flags (const char *str)
+{
+	g_return_val_if_fail (str != NULL, NM_802_11_AP_SEC_NONE);
+
+	if (strcmp (str, "wep40") == 0)
+		return NM_802_11_AP_SEC_GROUP_WEP40;
+	if (strcmp (str, "wep104") == 0)
+		return NM_802_11_AP_SEC_GROUP_WEP104;
+	if (strcmp (str, "tkip") == 0)
+		return NM_802_11_AP_SEC_GROUP_TKIP;
+	if (strcmp (str, "ccmp") == 0)
+		return NM_802_11_AP_SEC_GROUP_CCMP;
+	return NM_802_11_AP_SEC_NONE;
+}
+
+static NM80211ApSecurityFlags
+security_from_dict (GHashTable *security)
+{
+	GValue *value;
+	NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE;
+	const char **items, **iter;
+
+	value = g_hash_table_lookup (security, "KeyMgmt");
+	if (value) {
+		items = g_value_get_boxed (value);
+		for (iter = items; iter && *iter; iter++) {
+			if (strcmp (*iter, "wpa-psk") == 0)
+				flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
+			else if (strcmp (*iter, "wpa-eap") == 0)
+				flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X;
+		}
+	}
+
+	value = g_hash_table_lookup (security, "Pairwise");
+	if (value) {
+		items = g_value_get_boxed (value);
+		for (iter = items; iter && *iter; iter++)
+			flags |= pair_to_flags (*iter);
+	}
+
+	value = g_hash_table_lookup (security, "Group");
+	if (value)
+		flags |= group_to_flags (g_value_get_string (value));
+
+	return flags;
+}
+
+static void
+foreach_property_cb (gpointer key, gpointer value, gpointer user_data)
+{
+	GValue *variant = (GValue *) value;
+	NMAccessPoint *ap = (NMAccessPoint *) user_data;
+
+	if (G_VALUE_HOLDS_BOXED (variant)) {
+		GArray *array = g_value_get_boxed (variant);
+
+		if (!strcmp (key, "SSID")) {
+			guint32 len = MIN (32, array->len);
+			GByteArray *ssid;
+
+			/* Stupid ieee80211 layer uses <hidden> */
+			if (((len == 8) || (len == 9))
+				&& (memcmp (array->data, "<hidden>", 8) == 0))
+				return;
+
+			if (nm_utils_is_empty_ssid ((const guint8 *) array->data, len))
+				return;
+
+			ssid = g_byte_array_sized_new (len);
+			g_byte_array_append (ssid, (const guint8 *) array->data, len);
+			nm_ap_set_ssid (ap, ssid);
+			g_byte_array_free (ssid, TRUE);
+		} else if (!strcmp (key, "BSSID")) {
+			struct ether_addr addr;
+
+			if (array->len != ETH_ALEN)
+				return;
+			memset (&addr, 0, sizeof (struct ether_addr));
+			memcpy (&addr, array->data, ETH_ALEN);
+			nm_ap_set_address (ap, &addr);
+		} else if (!strcmp (key, "Rates")) {
+			guint32 maxrate = 0;
+			int i;
+
+			/* Find the max AP rate */
+			for (i = 0; i < array->len; i++) {
+				guint32 r = g_array_index (array, guint32, i);
+
+				if (r > maxrate) {
+					maxrate = r;
+					nm_ap_set_max_bitrate (ap, r / 1000);
+				}
+			}
+		} else if (!strcmp (key, "WPA")) {
+			NM80211ApSecurityFlags flags = nm_ap_get_wpa_flags (ap);
+
+			flags |= security_from_dict (g_value_get_boxed (variant));
+			nm_ap_set_wpa_flags (ap, flags);
+		} else if (!strcmp (key, "RSN")) {
+			NM80211ApSecurityFlags flags = nm_ap_get_rsn_flags (ap);
+
+			flags |= security_from_dict (g_value_get_boxed (variant));
+			nm_ap_set_rsn_flags (ap, flags);
+		}
+	} else if (G_VALUE_HOLDS_UINT (variant)) {
+		guint32 val = g_value_get_uint (variant);
+
+		if (!strcmp (key, "Frequency"))
+			nm_ap_set_freq (ap, val);
+	} else if (G_VALUE_HOLDS_INT (variant)) {
+		gint val = g_value_get_int (variant);
+
+		if (!strcmp (key, "Signal"))
+			nm_ap_set_strength (ap, nm_ap_utils_level_to_quality (val));
+	} else if (G_VALUE_HOLDS_STRING (variant)) {
+		const char *val = g_value_get_string (variant);
+
+		if (val && !strcmp (key, "Mode")) {
+			if (strcmp (val, "infrastructure") == 0)
+				nm_ap_set_mode (ap, NM_802_11_MODE_INFRA);
+			else if (strcmp (val, "ad-hoc") == 0)
+				nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC);
+		}
+	} else if (G_VALUE_HOLDS_BOOLEAN (variant)) {
+		gboolean val = g_value_get_boolean (variant);
+
+		if (strcmp (key, "Privacy") == 0) {
+			if (val) {
+				NM80211ApFlags flags = nm_ap_get_flags (ap);
+				nm_ap_set_flags (ap, flags | NM_802_11_AP_FLAGS_PRIVACY);
+			}
+		}
+	}
+}
+
+NMAccessPoint *
+nm_ap_new_from_properties (const char *supplicant_path, GHashTable *properties)
+{
+	NMAccessPoint *ap;
+	const struct ether_addr * addr;
+	const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+	g_return_val_if_fail (properties != NULL, NULL);
+
+	ap = nm_ap_new ();
+
+	g_object_freeze_notify (G_OBJECT (ap));
+	g_hash_table_foreach (properties, foreach_property_cb, ap);
+
+	nm_ap_set_supplicant_path (ap, supplicant_path);
+
+	/* ignore APs with invalid BSSIDs */
+	addr = nm_ap_get_address (ap);
+	if (   !(memcmp (addr->ether_addr_octet, bad_bssid1, ETH_ALEN))
+	    || !(memcmp (addr->ether_addr_octet, bad_bssid2, ETH_ALEN))) {
+		g_object_unref (ap);
+		return NULL;
+	}
+
+	nm_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ());
+
+	if (!nm_ap_get_ssid (ap))
+		nm_ap_set_broadcast (ap, FALSE);
+
+	g_object_thaw_notify (G_OBJECT (ap));
+
+	return ap;
+}
+
+#define PROTO_WPA "wpa"
+#define PROTO_RSN "rsn"
+
+static gboolean
+has_proto (NMSettingWirelessSecurity *sec, const char *proto)
+{
+	guint32 num_protos = nm_setting_wireless_security_get_num_protos (sec);
+	guint32 i;
+
+	if (num_protos == 0)
+		return TRUE; /* interpret no protos as "all" */
+
+	for (i = 0; i < num_protos; i++) {
+		if (!strcmp (nm_setting_wireless_security_get_proto (sec, i), proto))
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static void
+add_pair_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec)
+{
+	guint32 num = nm_setting_wireless_security_get_num_pairwise (sec);
+	NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE;
+	guint32 i;
+
+	/* If no ciphers are specified, that means "all" WPA ciphers */
+	if (num == 0) {
+		flags |= NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP;
+	} else {
+		for (i = 0; i < num; i++) {
+			const char *cipher = nm_setting_wireless_security_get_pairwise (sec, i);
+
+			if (!strcmp (cipher, "tkip"))
+				flags |= NM_802_11_AP_SEC_PAIR_TKIP;
+			else if (!strcmp (cipher, "ccmp"))
+				flags |= NM_802_11_AP_SEC_PAIR_CCMP;
+		}
+	}
+
+	if (has_proto (sec, PROTO_WPA))
+		nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | flags);
+	if (has_proto (sec, PROTO_RSN))
+		nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | flags);
+}
+
+static void
+add_group_ciphers (NMAccessPoint *ap, NMSettingWirelessSecurity *sec)
+{
+	guint32 num = nm_setting_wireless_security_get_num_groups (sec);
+	NM80211ApSecurityFlags flags = NM_802_11_AP_SEC_NONE;
+	guint32 i;
+
+	/* If no ciphers are specified, that means "all" WPA ciphers */
+	if (num == 0) {
+		flags |= NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_GROUP_CCMP;
+	} else {
+		for (i = 0; i < num; i++) {
+			const char *cipher = nm_setting_wireless_security_get_group (sec, i);
+
+			if (!strcmp (cipher, "wep40"))
+				flags |= NM_802_11_AP_SEC_GROUP_WEP40;
+			else if (!strcmp (cipher, "wep104"))
+				flags |= NM_802_11_AP_SEC_GROUP_WEP104;
+			else if (!strcmp (cipher, "tkip"))
+				flags |= NM_802_11_AP_SEC_GROUP_TKIP;
+			else if (!strcmp (cipher, "ccmp"))
+				flags |= NM_802_11_AP_SEC_GROUP_CCMP;
+		}
+	}
+
+	if (has_proto (sec, PROTO_WPA))
+		nm_ap_set_wpa_flags (ap, nm_ap_get_wpa_flags (ap) | flags);
+	if (has_proto (sec, PROTO_RSN))
+		nm_ap_set_rsn_flags (ap, nm_ap_get_rsn_flags (ap) | flags);
+}
+
+NMAccessPoint *
+nm_ap_new_fake_from_connection (NMConnection *connection)
+{
+	NMAccessPoint *ap;
+	NMSettingWireless *s_wireless;
+	NMSettingWirelessSecurity *s_wireless_sec;
+	const GByteArray *ssid;
+	const char *mode, *band, *key_mgmt;
+	guint32 channel;
+	NM80211ApSecurityFlags flags;
+	gboolean psk = FALSE, eap = FALSE;
+
+	g_return_val_if_fail (connection != NULL, NULL);
+
+	s_wireless = nm_connection_get_setting_wireless (connection);
+	g_return_val_if_fail (s_wireless != NULL, NULL);
+
+	ssid = nm_setting_wireless_get_ssid (s_wireless);
+	g_return_val_if_fail (ssid != NULL, NULL);
+	g_return_val_if_fail (ssid->len > 0, NULL);
+
+	ap = nm_ap_new ();
+	nm_ap_set_fake (ap, TRUE);
+	nm_ap_set_ssid (ap, ssid);
+
+	// FIXME: bssid too?
+
+	mode = nm_setting_wireless_get_mode (s_wireless);
+	if (mode) {
+		if (!strcmp (mode, "infrastructure"))
+			nm_ap_set_mode (ap, NM_802_11_MODE_INFRA);
+		else if (!strcmp (mode, "adhoc"))
+			nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC);
+		else if (!strcmp (mode, "ap")) {
+			nm_ap_set_mode (ap, NM_802_11_MODE_INFRA);
+			NM_AP_GET_PRIVATE (ap)->hotspot = TRUE;
+		} else
+			goto error;
+	} else {
+		nm_ap_set_mode (ap, NM_802_11_MODE_INFRA);
+	}
+
+	band = nm_setting_wireless_get_band (s_wireless);
+	channel = nm_setting_wireless_get_channel (s_wireless);
+
+	if (band && channel) {
+		guint32 freq = nm_utils_wifi_channel_to_freq (channel, band);
+
+		if (freq == 0)
+			goto error;
+
+		nm_ap_set_freq (ap, freq);
+	}
+
+	s_wireless_sec = nm_connection_get_setting_wireless_security (connection);
+	/* Assume presence of a security setting means the AP is encrypted */
+	if (!s_wireless_sec)
+		goto done;
+
+	key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wireless_sec);
+
+	/* Everything below here uses encryption */
+	nm_ap_set_flags (ap, nm_ap_get_flags (ap) | NM_802_11_AP_FLAGS_PRIVACY);
+
+	/* Static & Dynamic WEP */
+	if (!strcmp (key_mgmt, "none") || !strcmp (key_mgmt, "ieee8021x"))
+		goto done;
+
+	psk = !strcmp (key_mgmt, "wpa-psk");
+	eap = !strcmp (key_mgmt, "wpa-eap");
+	if (psk || eap) {
+		if (has_proto (s_wireless_sec, PROTO_WPA)) {
+			flags = nm_ap_get_wpa_flags (ap);
+			flags |= eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK;
+			nm_ap_set_wpa_flags (ap, flags);
+		}
+		if (has_proto (s_wireless_sec, PROTO_RSN)) {
+			flags = nm_ap_get_rsn_flags (ap);
+			flags |= eap ? NM_802_11_AP_SEC_KEY_MGMT_802_1X : NM_802_11_AP_SEC_KEY_MGMT_PSK;
+			nm_ap_set_rsn_flags (ap, flags);
+		}
+
+		add_pair_ciphers (ap, s_wireless_sec);
+		add_group_ciphers (ap, s_wireless_sec);
+	} else if (!strcmp (key_mgmt, "wpa-none")) {
+		guint32 i;
+
+		/* Ad-Hoc has special requirements: proto=WPA, pairwise=(none), and
+		 * group=TKIP/CCMP (but not both).
+		 */
+
+		flags = nm_ap_get_wpa_flags (ap);
+		flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
+
+		/* Clear ciphers; pairwise must be unset anyway, and group gets set below */
+		flags &= ~(  NM_802_11_AP_SEC_PAIR_WEP40
+		           | NM_802_11_AP_SEC_PAIR_WEP104
+		           | NM_802_11_AP_SEC_PAIR_TKIP
+		           | NM_802_11_AP_SEC_PAIR_CCMP
+		           | NM_802_11_AP_SEC_GROUP_WEP40
+		           | NM_802_11_AP_SEC_GROUP_WEP104
+		           | NM_802_11_AP_SEC_GROUP_TKIP
+		           | NM_802_11_AP_SEC_GROUP_CCMP);
+
+		for (i = 0; i < nm_setting_wireless_security_get_num_groups (s_wireless_sec); i++) {
+			if (!strcmp (nm_setting_wireless_security_get_group (s_wireless_sec, i), "ccmp")) {
+				flags |= NM_802_11_AP_SEC_GROUP_CCMP;
+				break;
+			}
+		}
+
+		/* Default to TKIP since not all WPA-capable cards can do CCMP */
+		if (!(flags & NM_802_11_AP_SEC_GROUP_CCMP))
+			flags |= NM_802_11_AP_SEC_GROUP_TKIP;
+
+		nm_ap_set_wpa_flags (ap, flags);
+
+		/* Don't use Ad-Hoc RSN yet */
+		nm_ap_set_rsn_flags (ap, NM_802_11_AP_SEC_NONE);
+	}
+
+done:
+	return ap;
+
+error:
+	g_object_unref (ap);
+	return NULL;
+}
+
+
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MAC_ARG(x) ((guint8*)(x))[0],((guint8*)(x))[1],((guint8*)(x))[2],((guint8*)(x))[3],((guint8*)(x))[4],((guint8*)(x))[5]
+
+void
+nm_ap_dump (NMAccessPoint *ap, const char *prefix)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	nm_log_dbg (LOGD_WIFI_SCAN, "%s'%s' (%p)",
+	            prefix,
+	            priv->ssid ? nm_utils_escape_ssid (priv->ssid->data, priv->ssid->len) : "(none)",
+	            ap);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    BSSID     " MAC_FMT, MAC_ARG (priv->address.ether_addr_octet));
+	nm_log_dbg (LOGD_WIFI_SCAN, "    mode      %d", priv->mode);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    flags     0x%X", priv->flags);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    wpa flags 0x%X", priv->wpa_flags);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    rsn flags 0x%X", priv->rsn_flags);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    quality   %d", priv->strength);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    frequency %d", priv->freq);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    max rate  %d", priv->max_bitrate);
+	nm_log_dbg (LOGD_WIFI_SCAN, "    last-seen %d", (int) priv->last_seen);
+}
+
+const char *
+nm_ap_get_dbus_path (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), NULL);
+
+	return NM_AP_GET_PRIVATE (ap)->dbus_path;
+}
+
+const char *
+nm_ap_get_supplicant_path (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), NULL);
+
+	return NM_AP_GET_PRIVATE (ap)->supplicant_path;
+}
+
+void
+nm_ap_set_supplicant_path (NMAccessPoint *ap, const char *path)
+{
+	g_return_if_fail (NM_IS_AP (ap));
+	g_return_if_fail (path != NULL);
+
+	g_free (NM_AP_GET_PRIVATE (ap)->supplicant_path);
+	NM_AP_GET_PRIVATE (ap)->supplicant_path = g_strdup (path);
+}
+
+/*
+ * Get/set functions for ssid
+ *
+ */
+const GByteArray * nm_ap_get_ssid (const NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), NULL);
+
+	return NM_AP_GET_PRIVATE (ap)->ssid;
+}
+
+void
+nm_ap_set_ssid (NMAccessPoint *ap, const GByteArray * ssid)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (ssid == priv->ssid)
+		return;
+
+	/* same SSID */
+	if ((ssid && priv->ssid) && (ssid->len == priv->ssid->len)) {
+		if (!memcmp (ssid->data, priv->ssid->data, ssid->len))
+			return;
+	}
+
+	if (priv->ssid) {
+		g_byte_array_free (priv->ssid, TRUE);
+		priv->ssid = NULL;
+	}
+
+	if (ssid) {
+		/* Should never get zero-length SSIDs */
+		g_warn_if_fail (ssid->len > 0);
+
+		if (ssid->len) {
+			priv->ssid = g_byte_array_sized_new (ssid->len);
+			priv->ssid->len = ssid->len;
+			memcpy (priv->ssid->data, ssid->data, ssid->len);
+		}
+	}
+
+	g_object_notify (G_OBJECT (ap), NM_AP_SSID);
+}
+
+
+NM80211ApFlags
+nm_ap_get_flags (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE);
+
+	return NM_AP_GET_PRIVATE (ap)->flags;
+}
+
+
+void
+nm_ap_set_flags (NMAccessPoint *ap, NM80211ApFlags flags)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->flags != flags) {
+		priv->flags = flags;
+		g_object_notify (G_OBJECT (ap), NM_AP_FLAGS);
+	}
+}
+
+NM80211ApSecurityFlags
+nm_ap_get_wpa_flags (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE);
+
+	return NM_AP_GET_PRIVATE (ap)->wpa_flags;
+}
+
+
+void
+nm_ap_set_wpa_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+	if (priv->wpa_flags != flags) {
+		priv->wpa_flags = flags;
+		g_object_notify (G_OBJECT (ap), NM_AP_WPA_FLAGS);
+	}
+}
+
+NM80211ApSecurityFlags
+nm_ap_get_rsn_flags (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), NM_802_11_AP_SEC_NONE);
+
+	return NM_AP_GET_PRIVATE (ap)->rsn_flags;
+}
+
+
+void
+nm_ap_set_rsn_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+	if (priv->rsn_flags != flags) {
+		priv->rsn_flags = flags;
+		g_object_notify (G_OBJECT (ap), NM_AP_RSN_FLAGS);
+	}
+}
+
+/*
+ * Get/set functions for address
+ *
+ */
+const struct ether_addr * nm_ap_get_address (const NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), NULL);
+
+	return &NM_AP_GET_PRIVATE (ap)->address;
+}
+
+void nm_ap_set_address (NMAccessPoint *ap, const struct ether_addr * addr)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+	g_return_if_fail (addr != NULL);
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (memcmp (addr, &priv->address, sizeof (priv->address))) {
+		memcpy (&NM_AP_GET_PRIVATE (ap)->address, addr, sizeof (struct ether_addr));
+		g_object_notify (G_OBJECT (ap), NM_AP_HW_ADDRESS);
+	}
+}
+
+
+/*
+ * Get/set functions for mode (ie Ad-Hoc, Infrastructure, etc)
+ *
+ */
+NM80211Mode nm_ap_get_mode (NMAccessPoint *ap)
+{
+	NM80211Mode mode;
+
+	g_return_val_if_fail (NM_IS_AP (ap), -1);
+
+	g_object_get (ap, NM_AP_MODE, &mode, NULL);
+
+	return mode;
+}
+
+void nm_ap_set_mode (NMAccessPoint *ap, const NM80211Mode mode)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+	g_return_if_fail (   mode == NM_802_11_MODE_ADHOC
+	                  || mode == NM_802_11_MODE_INFRA);
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->mode != mode) {
+		priv->mode = mode;
+		g_object_notify (G_OBJECT (ap), NM_AP_MODE);
+	}
+}
+
+gboolean
+nm_ap_is_hotspot (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), FALSE);
+
+	return NM_AP_GET_PRIVATE (ap)->hotspot;
+}
+
+/*
+ * Get/set functions for strength
+ *
+ */
+gint8 nm_ap_get_strength (NMAccessPoint *ap)
+{
+	gint8 strength;
+
+	g_return_val_if_fail (NM_IS_AP (ap), 0);
+
+	g_object_get (ap, NM_AP_STRENGTH, &strength, NULL);
+
+	return strength;
+}
+
+void nm_ap_set_strength (NMAccessPoint *ap, const gint8 strength)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->strength != strength) {
+		priv->strength = strength;
+		g_object_notify (G_OBJECT (ap), NM_AP_STRENGTH);
+	}
+}
+
+
+/*
+ * Get/set functions for frequency
+ *
+ */
+guint32
+nm_ap_get_freq (NMAccessPoint *ap)
+{
+	guint32 freq;
+
+	g_return_val_if_fail (NM_IS_AP (ap), 0);
+
+	g_object_get (ap, NM_AP_FREQUENCY, &freq, NULL);
+
+	return freq;
+}
+
+void
+nm_ap_set_freq (NMAccessPoint *ap,
+                const guint32 freq)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->freq != freq) {
+		priv->freq = freq;
+		g_object_notify (G_OBJECT (ap), NM_AP_FREQUENCY);
+	}
+}
+
+
+/*
+ * Get/set functions for max bitrate (in kbit/s)
+ *
+ */
+guint32 nm_ap_get_max_bitrate (NMAccessPoint *ap)
+{
+	guint32 rate;
+
+	g_return_val_if_fail (NM_IS_AP (ap), 0);
+
+	g_object_get (ap, NM_AP_MAX_BITRATE, &rate, NULL);
+
+	return rate;
+}
+
+void
+nm_ap_set_max_bitrate (NMAccessPoint *ap, guint32 bitrate)
+{
+	NMAccessPointPrivate *priv;
+
+	g_return_if_fail (NM_IS_AP (ap));
+
+	priv = NM_AP_GET_PRIVATE (ap);
+
+	if (priv->max_bitrate != bitrate) {
+		priv->max_bitrate = bitrate;
+		g_object_notify (G_OBJECT (ap), NM_AP_MAX_BITRATE);
+	}
+}
+
+/*
+ * Get/Set functions to indicate that an access point is 'fake', ie whether
+ * or not it was created from scan results
+ */
+gboolean nm_ap_get_fake (const NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), FALSE);
+
+	return NM_AP_GET_PRIVATE (ap)->fake;
+}
+
+void nm_ap_set_fake (NMAccessPoint *ap, gboolean fake)
+{
+	g_return_if_fail (NM_IS_AP (ap));
+
+	NM_AP_GET_PRIVATE (ap)->fake = fake;
+}
+
+
+/*
+ * Get/Set functions to indicate whether an AP broadcasts its SSID.
+ */
+gboolean nm_ap_get_broadcast (NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), TRUE);
+
+	return NM_AP_GET_PRIVATE (ap)->broadcast;
+}
+
+
+void nm_ap_set_broadcast (NMAccessPoint *ap, gboolean broadcast)
+{
+	g_return_if_fail (NM_IS_AP (ap));
+
+	NM_AP_GET_PRIVATE (ap)->broadcast = broadcast;
+}
+
+
+/*
+ * Get/Set functions for how long ago the AP was last seen in a scan.
+ * APs older than a certain date are dropped from the list.
+ *
+ */
+gint32
+nm_ap_get_last_seen (const NMAccessPoint *ap)
+{
+	g_return_val_if_fail (NM_IS_AP (ap), FALSE);
+
+	return NM_AP_GET_PRIVATE (ap)->last_seen;
+}
+
+void
+nm_ap_set_last_seen (NMAccessPoint *ap, gint32 last_seen)
+{
+	g_return_if_fail (NM_IS_AP (ap));
+
+	NM_AP_GET_PRIVATE (ap)->last_seen = last_seen;
+}
+
+gboolean
+nm_ap_check_compatible (NMAccessPoint *self,
+                        NMConnection *connection)
+{
+	NMAccessPointPrivate *priv;
+	NMSettingWireless *s_wireless;
+	NMSettingWirelessSecurity *s_wireless_sec;
+	const char *mode;
+	const char *band;
+	const GByteArray *bssid;
+	guint32 channel;
+
+	g_return_val_if_fail (NM_IS_AP (self), FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+	priv = NM_AP_GET_PRIVATE (self);
+
+	s_wireless = nm_connection_get_setting_wireless (connection);
+	if (s_wireless == NULL)
+		return FALSE;
+	
+	if (!nm_utils_same_ssid (nm_setting_wireless_get_ssid (s_wireless), priv->ssid, TRUE))
+		return FALSE;
+
+	bssid = nm_setting_wireless_get_bssid (s_wireless);
+	if (bssid && memcmp (bssid->data, &priv->address, ETH_ALEN))
+		return FALSE;
+
+	mode = nm_setting_wireless_get_mode (s_wireless);
+	if (mode) {
+		if (!strcmp (mode, "infrastructure") && (priv->mode != NM_802_11_MODE_INFRA))
+			return FALSE;
+		if (!strcmp (mode, "adhoc") && (priv->mode != NM_802_11_MODE_ADHOC))
+			return FALSE;
+		if (   !strcmp (mode, "ap")
+		    && (priv->mode != NM_802_11_MODE_INFRA || priv->hotspot != TRUE))
+			return FALSE;
+	}
+
+	band = nm_setting_wireless_get_band (s_wireless);
+	if (band) {
+		if (!strcmp (band, "a")) {
+			if (priv->freq < 4915 || priv->freq > 5825)
+				return FALSE;
+		} else if (!strcmp (band, "bg")) {
+			if (priv->freq < 2412 || priv->freq > 2484)
+				return FALSE;
+		}
+	}
+
+	channel = nm_setting_wireless_get_channel (s_wireless);
+	if (channel) {
+		guint32 ap_chan = nm_utils_wifi_freq_to_channel (priv->freq);
+
+		if (channel != ap_chan)
+			return FALSE;
+	}
+
+	s_wireless_sec = nm_connection_get_setting_wireless_security (connection);
+
+	return nm_setting_wireless_ap_security_compatible (s_wireless,
+	                                                   s_wireless_sec,
+	                                                   nm_ap_get_flags (self),
+	                                                   nm_ap_get_wpa_flags (self),
+	                                                   nm_ap_get_rsn_flags (self),
+	                                                   nm_ap_get_mode (self));
+}
+
+gboolean
+nm_ap_complete_connection (NMAccessPoint *self,
+                           NMConnection *connection,
+                           gboolean lock_bssid,
+                           GError **error)
+{
+	NMAccessPointPrivate *priv = NM_AP_GET_PRIVATE (self);
+
+	g_return_val_if_fail (connection != NULL, FALSE);
+
+	return nm_ap_utils_complete_connection (priv->ssid,
+	                                        priv->address.ether_addr_octet,
+	                                        priv->mode,
+	                                        priv->flags,
+	                                        priv->wpa_flags,
+	                                        priv->rsn_flags,
+	                                        connection,
+	                                        lock_bssid,
+	                                        error);
+}
+
+static gboolean
+capabilities_compatible (NM80211ApSecurityFlags a_flags, NM80211ApSecurityFlags b_flags)
+{
+	if (a_flags == b_flags)
+		return TRUE;
+
+	/* Make sure there's a common key management method */
+	if (!((a_flags & 0x300) & (b_flags & 0x300)))
+		return FALSE;
+
+	/* Ensure common pairwise ciphers */
+	if (!((a_flags & 0xF) & (b_flags & 0xF)))
+		return FALSE;
+
+	/* Ensure common group ciphers */
+	if (!((a_flags & 0xF0) & (b_flags & 0xF0)))
+		return FALSE;
+
+	return TRUE;
+}
+
+NMAccessPoint *
+nm_ap_match_in_list (NMAccessPoint *find_ap,
+                     GSList *ap_list,
+                     gboolean strict_match)
+{
+	GSList *iter;
+
+	g_return_val_if_fail (find_ap != NULL, NULL);
+
+	for (iter = ap_list; iter; iter = g_slist_next (iter)) {
+		NMAccessPoint * list_ap = NM_AP (iter->data);
+		const GByteArray * list_ssid = nm_ap_get_ssid (list_ap);
+		const struct ether_addr * list_addr = nm_ap_get_address (list_ap);
+
+		const GByteArray * find_ssid = nm_ap_get_ssid (find_ap);
+		const struct ether_addr * find_addr = nm_ap_get_address (find_ap);
+
+		/* SSID match; if both APs are hiding their SSIDs,
+		 * let matching continue on BSSID and other properties
+		 */
+		if (   (!list_ssid && find_ssid)
+		    || (list_ssid && !find_ssid)
+		    || !nm_utils_same_ssid (list_ssid, find_ssid, TRUE))
+			continue;
+
+		/* BSSID match */
+		if (   (strict_match || nm_ethernet_address_is_valid (find_addr))
+		    && nm_ethernet_address_is_valid (list_addr)
+		    && memcmp (list_addr->ether_addr_octet, 
+		               find_addr->ether_addr_octet,
+		               ETH_ALEN) != 0) {
+			continue;
+		}
+
+		/* mode match */
+		if (nm_ap_get_mode (list_ap) != nm_ap_get_mode (find_ap))
+			continue;
+
+		/* Frequency match */
+		if (nm_ap_get_freq (list_ap) != nm_ap_get_freq (find_ap))
+			continue;
+
+		/* AP flags */
+		if (nm_ap_get_flags (list_ap) != nm_ap_get_flags (find_ap))
+			continue;
+
+		if (strict_match) {
+			if (nm_ap_get_wpa_flags (list_ap) != nm_ap_get_wpa_flags (find_ap))
+				continue;
+
+			if (nm_ap_get_rsn_flags (list_ap) != nm_ap_get_rsn_flags (find_ap))
+				continue;
+		} else {
+			NM80211ApSecurityFlags list_wpa_flags = nm_ap_get_wpa_flags (list_ap);
+			NM80211ApSecurityFlags find_wpa_flags = nm_ap_get_wpa_flags (find_ap);
+			NM80211ApSecurityFlags list_rsn_flags = nm_ap_get_rsn_flags (list_ap);
+			NM80211ApSecurityFlags find_rsn_flags = nm_ap_get_rsn_flags (find_ap);
+
+			/* Just ensure that there is overlap in the capabilities */
+			if (   !capabilities_compatible (list_wpa_flags, find_wpa_flags)
+			    && !capabilities_compatible (list_rsn_flags, find_rsn_flags))
+				continue;
+		}
+
+		return list_ap;
+	}
+
+	return NULL;
+}
+
diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h
new file mode 100644
index 00000000..f51fa078
--- /dev/null
+++ b/src/devices/wifi/nm-wifi-ap.h
@@ -0,0 +1,121 @@
+/* -*- 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) 2004 - 2011 Red Hat, Inc.
+ * Copyright (C) 2006 - 2008 Novell, Inc.
+ */
+
+#ifndef NM_ACCESS_POINT_H
+#define NM_ACCESS_POINT_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include "NetworkManager.h"
+#include "nm-connection.h"
+
+#define NM_TYPE_AP            (nm_ap_get_type ())
+#define NM_AP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AP, NMAccessPoint))
+#define NM_AP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_AP, NMAccessPointClass))
+#define NM_IS_AP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_AP))
+#define NM_IS_AP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_AP))
+#define NM_AP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_AP, NMAccessPointClass))
+
+#define NM_AP_FLAGS "flags"
+#define NM_AP_WPA_FLAGS "wpa-flags"
+#define NM_AP_RSN_FLAGS "rsn-flags"
+#define NM_AP_SSID "ssid"
+#define NM_AP_FREQUENCY "frequency"
+#define NM_AP_HW_ADDRESS "hw-address"
+#define NM_AP_MODE "mode"
+#define NM_AP_MAX_BITRATE "max-bitrate"
+#define NM_AP_STRENGTH "strength"
+
+typedef struct {
+	GObject parent;
+} NMAccessPoint;
+
+typedef struct {
+	GObjectClass parent;
+
+} NMAccessPointClass;
+
+GType nm_ap_get_type (void);
+
+NMAccessPoint * nm_ap_new_from_properties (const char *supplicant_path,
+                                           GHashTable *properties);
+NMAccessPoint * nm_ap_new_fake_from_connection (NMConnection *connection);
+void            nm_ap_export_to_dbus    (NMAccessPoint *ap);
+
+const char *		nm_ap_get_dbus_path (NMAccessPoint *ap);
+
+const char *        nm_ap_get_supplicant_path (NMAccessPoint *ap);
+void                nm_ap_set_supplicant_path (NMAccessPoint *ap,
+                                               const char *path);
+
+const GByteArray *	nm_ap_get_ssid (const NMAccessPoint * ap);
+void				nm_ap_set_ssid (NMAccessPoint * ap, const GByteArray * ssid);
+
+NM80211ApFlags         nm_ap_get_flags     (NMAccessPoint *ap);
+void                   nm_ap_set_flags     (NMAccessPoint *ap, NM80211ApFlags flags);
+
+NM80211ApSecurityFlags nm_ap_get_wpa_flags (NMAccessPoint *ap);
+void                   nm_ap_set_wpa_flags (NMAccessPoint *ap, NM80211ApSecurityFlags flags);
+
+NM80211ApSecurityFlags nm_ap_get_rsn_flags	(NMAccessPoint *ap);
+void                   nm_ap_set_rsn_flags  (NMAccessPoint *ap, NM80211ApSecurityFlags flags);
+
+const struct ether_addr * nm_ap_get_address	(const NMAccessPoint *ap);
+void				nm_ap_set_address		(NMAccessPoint *ap, const struct ether_addr *addr);
+
+NM80211Mode			nm_ap_get_mode			(NMAccessPoint *ap);
+void				nm_ap_set_mode			(NMAccessPoint *ap, const NM80211Mode mode);
+
+gboolean            nm_ap_is_hotspot        (NMAccessPoint *ap);
+
+gint8			nm_ap_get_strength		(NMAccessPoint *ap);
+void				nm_ap_set_strength		(NMAccessPoint *ap, gint8 strength);
+
+guint32			nm_ap_get_freq			(NMAccessPoint *ap);
+void				nm_ap_set_freq			(NMAccessPoint *ap, guint32 freq);
+
+guint32			nm_ap_get_max_bitrate			(NMAccessPoint *ap);
+void				nm_ap_set_max_bitrate		(NMAccessPoint *ap, guint32 bitrate);
+
+gboolean			nm_ap_get_fake	(const NMAccessPoint *ap);
+void				nm_ap_set_fake	(NMAccessPoint *ap, gboolean fake);
+
+gboolean			nm_ap_get_broadcast		(NMAccessPoint *ap);
+void				nm_ap_set_broadcast		(NMAccessPoint *ap, gboolean broadcast);
+
+gint32                  nm_ap_get_last_seen     (const NMAccessPoint *ap);
+void                    nm_ap_set_last_seen     (NMAccessPoint *ap, gint32 last_seen);
+
+gboolean			nm_ap_check_compatible (NMAccessPoint *self,
+                                            NMConnection *connection);
+
+gboolean            nm_ap_complete_connection (NMAccessPoint *self,
+                                               NMConnection *connection,
+                                               gboolean lock_bssid,
+                                               GError **error);
+
+NMAccessPoint *     nm_ap_match_in_list (NMAccessPoint *find_ap,
+                                         GSList *ap_list,
+                                         gboolean strict_match);
+
+void				nm_ap_dump (NMAccessPoint *ap, const char *prefix);
+
+#endif /* NM_ACCESS_POINT_H */
diff --git a/src/devices/wifi/nm-wifi-enum-types.c b/src/devices/wifi/nm-wifi-enum-types.c
new file mode 100644
index 00000000..e914cfd6
--- /dev/null
+++ b/src/devices/wifi/nm-wifi-enum-types.c
@@ -0,0 +1,58 @@
+
+
+
+/* Generated by glib-mkenums. Do not edit */
+
+#include "nm-wifi-enum-types.h"
+
+#include "nm-device-wifi.h" 
+#include "nm-wifi-ap.h" 
+#include "nm-device-olpc-mesh.h"
+
+GType
+nm_wifi_error_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      static const GEnumValue values[] = {
+        { NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS, "NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS", "ConnectionNotWireless" },
+        { NM_WIFI_ERROR_CONNECTION_INVALID, "NM_WIFI_ERROR_CONNECTION_INVALID", "ConnectionInvalid" },
+        { NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, "NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE", "ConnectionIncompatible" },
+        { NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, "NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND", "AccessPointNotFound" },
+        { NM_WIFI_ERROR_SCAN_NOT_ALLOWED, "NM_WIFI_ERROR_SCAN_NOT_ALLOWED", "ScanNotAllowed" },
+        { NM_WIFI_ERROR_AP_MODE_UNSUPPORTED, "NM_WIFI_ERROR_AP_MODE_UNSUPPORTED", "ApModeUnsupported" },
+        { NM_WIFI_ERROR_ADHOC_MODE_UNSUPPORTED, "NM_WIFI_ERROR_ADHOC_MODE_UNSUPPORTED", "AdhocModeUnsupported" },
+        { 0, NULL, NULL }
+      };
+      GType g_define_type_id =
+        g_enum_register_static (g_intern_static_string ("NMWifiError"), values);
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+GType
+nm_olpc_mesh_error_get_type (void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if (g_once_init_enter (&g_define_type_id__volatile))
+    {
+      static const GEnumValue values[] = {
+        { NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH, "NM_OLPC_MESH_ERROR_CONNECTION_NOT_MESH", "ConnectionNotMesh" },
+        { NM_OLPC_MESH_ERROR_CONNECTION_INVALID, "NM_OLPC_MESH_ERROR_CONNECTION_INVALID", "ConnectionInvalid" },
+        { NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE, "NM_OLPC_MESH_ERROR_CONNECTION_INCOMPATIBLE", "ConnectionIncompatible" },
+        { 0, NULL, NULL }
+      };
+      GType g_define_type_id =
+        g_enum_register_static (g_intern_static_string ("NMOlpcMeshError"), values);
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+  return g_define_type_id__volatile;
+}
+
+
+
diff --git a/src/devices/wifi/nm-wifi-enum-types.h b/src/devices/wifi/nm-wifi-enum-types.h
new file mode 100644
index 00000000..d4dca5bf
--- /dev/null
+++ b/src/devices/wifi/nm-wifi-enum-types.h
@@ -0,0 +1,21 @@
+
+
+
+/* Generated by glib-mkenums. Do not edit */
+
+#ifndef __NM_WIFI_ENUM_TYPES_H__
+#define __NM_WIFI_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+GType nm_wifi_error_get_type (void) G_GNUC_CONST;
+#define NM_TYPE_WIFI_ERROR (nm_wifi_error_get_type ())
+GType nm_olpc_mesh_error_get_type (void) G_GNUC_CONST;
+#define NM_TYPE_OLPC_MESH_ERROR (nm_olpc_mesh_error_get_type ())
+G_END_DECLS
+
+#endif /* __NM_WIFI_ENUM_TYPES_H__ */
+
+
+
diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c
new file mode 100644
index 00000000..02ad93f4
--- /dev/null
+++ b/src/devices/wifi/nm-wifi-factory.c
@@ -0,0 +1,89 @@
+/* -*- 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) 2011 - 2014 Red Hat, Inc.
+ */
+
+#include <gmodule.h>
+
+#include "nm-device-factory.h"
+#include "nm-device-wifi.h"
+#include "nm-device-olpc-mesh.h"
+#include "nm-settings-connection.h"
+
+#define NM_TYPE_WIFI_FACTORY (nm_wifi_factory_get_type ())
+#define NM_WIFI_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WIFI_FACTORY, NMWifiFactory))
+
+typedef struct {
+	GObject parent;
+} NMWifiFactory;
+
+typedef struct {
+	GObjectClass parent;
+} NMWifiFactoryClass;
+
+static GType nm_wifi_factory_get_type (void);
+
+static void device_factory_interface_init (NMDeviceFactory *factory_iface);
+
+G_DEFINE_TYPE_EXTENDED (NMWifiFactory, nm_wifi_factory, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_FACTORY, device_factory_interface_init))
+
+/**************************************************************************/
+
+#define PLUGIN_TYPE NM_DEVICE_TYPE_WIFI
+
+G_MODULE_EXPORT NMDeviceFactory *
+nm_device_factory_create (GError **error)
+{
+	return (NMDeviceFactory *) g_object_new (NM_TYPE_WIFI_FACTORY, NULL);
+}
+
+G_MODULE_EXPORT NMDeviceType
+nm_device_factory_get_device_type (void)
+{
+	return PLUGIN_TYPE;
+}
+
+/**************************************************************************/
+
+static NMDevice *
+new_link (NMDeviceFactory *factory, NMPlatformLink *plink, GError **error)
+{
+	if (plink->type == NM_LINK_TYPE_WIFI)
+		return nm_device_wifi_new (plink);
+	else if (plink->type == NM_LINK_TYPE_OLPC_MESH)
+		return nm_device_olpc_mesh_new (plink);
+	return NULL;
+}
+
+static void
+device_factory_interface_init (NMDeviceFactory *factory_iface)
+{
+	factory_iface->new_link = new_link;
+}
+
+static void
+nm_wifi_factory_init (NMWifiFactory *self)
+{
+}
+
+static void
+nm_wifi_factory_class_init (NMWifiFactoryClass *wf_class)
+{
+}
+
diff --git a/src/devices/wifi/tests/Makefile.am b/src/devices/wifi/tests/Makefile.am
new file mode 100644
index 00000000..2667c5f1
--- /dev/null
+++ b/src/devices/wifi/tests/Makefile.am
@@ -0,0 +1,28 @@
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/include \
+	-I$(top_builddir)/include \
+	-I$(top_srcdir)/libnm-util \
+	-I$(top_builddir)/libnm-util \
+	-I$(top_srcdir)/src/platform \
+	-I$(top_srcdir)/src/logging \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/devices/wifi \
+	-I$(top_builddir)/src \
+	-DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+noinst_PROGRAMS = test-wifi-ap-utils
+
+test_wifi_ap_utils_SOURCES = \
+	test-wifi-ap-utils.c \
+	$(srcdir)/../nm-wifi-ap.c \
+	$(srcdir)/../nm-wifi-ap.h \
+	$(srcdir)/../nm-wifi-ap-utils.c \
+	$(srcdir)/../nm-wifi-ap-utils.h
+
+test_wifi_ap_utils_LDADD = $(top_builddir)/src/libNetworkManager.la
+
+TESTS = test-wifi-ap-utils
+
diff --git a/src/devices/wifi/tests/Makefile.in b/src/devices/wifi/tests/Makefile.in
new file mode 100644
index 00000000..c0e75eed
--- /dev/null
+++ b/src/devices/wifi/tests/Makefile.in
@@ -0,0 +1,891 @@
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+noinst_PROGRAMS = test-wifi-ap-utils$(EXEEXT)
+TESTS = test-wifi-ap-utils$(EXEEXT)
+subdir = src/devices/wifi/tests
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/build-aux/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gettext.m4 \
+	$(top_srcdir)/m4/gnome-code-coverage.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
+	$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \
+	$(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+	$(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+PROGRAMS = $(noinst_PROGRAMS)
+am_test_wifi_ap_utils_OBJECTS = test-wifi-ap-utils.$(OBJEXT) \
+	nm-wifi-ap.$(OBJEXT) nm-wifi-ap-utils.$(OBJEXT)
+test_wifi_ap_utils_OBJECTS = $(am_test_wifi_ap_utils_OBJECTS)
+test_wifi_ap_utils_DEPENDENCIES =  \
+	$(top_builddir)/src/libNetworkManager.la
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(test_wifi_ap_utils_SOURCES)
+DIST_SOURCES = $(test_wifi_ap_utils_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+  mgn= red= grn= lgn= blu= brg= std=; \
+  am__color_tests=no
+am__tty_colors = { \
+  $(am__tty_colors_dummy); \
+  if test "X$(AM_COLOR_TESTS)" = Xno; then \
+    am__color_tests=no; \
+  elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+    am__color_tests=yes; \
+  elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+    am__color_tests=yes; \
+  fi; \
+  if test $$am__color_tests = yes; then \
+    red=''; \
+    grn=''; \
+    lgn=''; \
+    blu=''; \
+    mgn=''; \
+    brg=''; \
+    std=''; \
+  fi; \
+}
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CKDB_PATH = @CKDB_PATH@
+CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
+DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
+DLLTOOL = @DLLTOOL@
+DNSMASQ_PATH = @DNSMASQ_PATH@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MAKEFILE = @GLIB_MAKEFILE@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
+INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
+INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@
+INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@
+INTROSPECTION_LIBS = @INTROSPECTION_LIBS@
+INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
+INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
+INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
+IPTABLES_PATH = @IPTABLES_PATH@
+IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@
+IWMX_SDK_LIBS = @IWMX_SDK_LIBS@
+KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBM = @LIBM@
+LIBNDP_CFLAGS = @LIBNDP_CFLAGS@
+LIBNDP_LIBS = @LIBNDP_LIBS@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
+LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
+LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@
+MM_GLIB_LIBS = @MM_GLIB_LIBS@
+MOC = @MOC@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NEWT_CFLAGS = @NEWT_CFLAGS@
+NEWT_LIBS = @NEWT_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
+NM_MICRO_VERSION = @NM_MICRO_VERSION@
+NM_MINOR_VERSION = @NM_MINOR_VERSION@
+NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@
+NM_VERSION = @NM_VERSION@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PPPD_PATH = @PPPD_PATH@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+PPPOE_PATH = @PPPOE_PATH@
+QT_CFLAGS = @QT_CFLAGS@
+QT_LIBS = @QT_LIBS@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+SED = @SED@
+SELINUX_CFLAGS = @SELINUX_CFLAGS@
+SELINUX_LIBS = @SELINUX_LIBS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@
+SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@
+SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@
+SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
+SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
+SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_BASE_DIR = @UDEV_BASE_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VALGRIND_RULES = @VALGRIND_RULES@
+VAPIGEN = @VAPIGEN@
+VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
+VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+nmbinary = @nmbinary@
+nmconfdir = @nmconfdir@
+nmdatadir = @nmdatadir@
+nmrundir = @nmrundir@
+nmstatedir = @nmstatedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+systemdsystemunitdir = @systemdsystemunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+with_dhclient = @with_dhclient@
+with_dhcpcd = @with_dhcpcd@
+with_netconfig = @with_netconfig@
+with_resolvconf = @with_resolvconf@
+with_valgrind = @with_valgrind@
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/include \
+	-I$(top_builddir)/include \
+	-I$(top_srcdir)/libnm-util \
+	-I$(top_builddir)/libnm-util \
+	-I$(top_srcdir)/src/platform \
+	-I$(top_srcdir)/src/logging \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/devices/wifi \
+	-I$(top_builddir)/src \
+	-DG_LOG_DOMAIN=\""NetworkManager-wifi"\" \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+	$(GLIB_CFLAGS) \
+	$(DBUS_CFLAGS)
+
+test_wifi_ap_utils_SOURCES = \
+	test-wifi-ap-utils.c \
+	$(srcdir)/../nm-wifi-ap.c \
+	$(srcdir)/../nm-wifi-ap.h \
+	$(srcdir)/../nm-wifi-ap-utils.c \
+	$(srcdir)/../nm-wifi-ap-utils.h
+
+test_wifi_ap_utils_LDADD = $(top_builddir)/src/libNetworkManager.la
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/wifi/tests/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/devices/wifi/tests/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstPROGRAMS:
+	@list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+test-wifi-ap-utils$(EXEEXT): $(test_wifi_ap_utils_OBJECTS) $(test_wifi_ap_utils_DEPENDENCIES) $(EXTRA_test_wifi_ap_utils_DEPENDENCIES) 
+	@rm -f test-wifi-ap-utils$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(test_wifi_ap_utils_OBJECTS) $(test_wifi_ap_utils_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wifi-ap-utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-wifi-ap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-wifi-ap-utils.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+nm-wifi-ap.o: $(srcdir)/../nm-wifi-ap.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-wifi-ap.o -MD -MP -MF $(DEPDIR)/nm-wifi-ap.Tpo -c -o nm-wifi-ap.o `test -f '$(srcdir)/../nm-wifi-ap.c' || echo '$(srcdir)/'`$(srcdir)/../nm-wifi-ap.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-wifi-ap.Tpo $(DEPDIR)/nm-wifi-ap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$(srcdir)/../nm-wifi-ap.c' object='nm-wifi-ap.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-wifi-ap.o `test -f '$(srcdir)/../nm-wifi-ap.c' || echo '$(srcdir)/'`$(srcdir)/../nm-wifi-ap.c
+
+nm-wifi-ap.obj: $(srcdir)/../nm-wifi-ap.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-wifi-ap.obj -MD -MP -MF $(DEPDIR)/nm-wifi-ap.Tpo -c -o nm-wifi-ap.obj `if test -f '$(srcdir)/../nm-wifi-ap.c'; then $(CYGPATH_W) '$(srcdir)/../nm-wifi-ap.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../nm-wifi-ap.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-wifi-ap.Tpo $(DEPDIR)/nm-wifi-ap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$(srcdir)/../nm-wifi-ap.c' object='nm-wifi-ap.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-wifi-ap.obj `if test -f '$(srcdir)/../nm-wifi-ap.c'; then $(CYGPATH_W) '$(srcdir)/../nm-wifi-ap.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../nm-wifi-ap.c'; fi`
+
+nm-wifi-ap-utils.o: $(srcdir)/../nm-wifi-ap-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-wifi-ap-utils.o -MD -MP -MF $(DEPDIR)/nm-wifi-ap-utils.Tpo -c -o nm-wifi-ap-utils.o `test -f '$(srcdir)/../nm-wifi-ap-utils.c' || echo '$(srcdir)/'`$(srcdir)/../nm-wifi-ap-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-wifi-ap-utils.Tpo $(DEPDIR)/nm-wifi-ap-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$(srcdir)/../nm-wifi-ap-utils.c' object='nm-wifi-ap-utils.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-wifi-ap-utils.o `test -f '$(srcdir)/../nm-wifi-ap-utils.c' || echo '$(srcdir)/'`$(srcdir)/../nm-wifi-ap-utils.c
+
+nm-wifi-ap-utils.obj: $(srcdir)/../nm-wifi-ap-utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-wifi-ap-utils.obj -MD -MP -MF $(DEPDIR)/nm-wifi-ap-utils.Tpo -c -o nm-wifi-ap-utils.obj `if test -f '$(srcdir)/../nm-wifi-ap-utils.c'; then $(CYGPATH_W) '$(srcdir)/../nm-wifi-ap-utils.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../nm-wifi-ap-utils.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-wifi-ap-utils.Tpo $(DEPDIR)/nm-wifi-ap-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$(srcdir)/../nm-wifi-ap-utils.c' object='nm-wifi-ap-utils.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-wifi-ap-utils.obj `if test -f '$(srcdir)/../nm-wifi-ap-utils.c'; then $(CYGPATH_W) '$(srcdir)/../nm-wifi-ap-utils.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../nm-wifi-ap-utils.c'; fi`
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+	@failed=0; all=0; xfail=0; xpass=0; skip=0; \
+	srcdir=$(srcdir); export srcdir; \
+	list=' $(TESTS) '; \
+	$(am__tty_colors); \
+	if test -n "$$list"; then \
+	  for tst in $$list; do \
+	    if test -f ./$$tst; then dir=./; \
+	    elif test -f $$tst; then dir=; \
+	    else dir="$(srcdir)/"; fi; \
+	    if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
+	      all=`expr $$all + 1`; \
+	      case " $(XFAIL_TESTS) " in \
+	      *[\ \	]$$tst[\ \	]*) \
+		xpass=`expr $$xpass + 1`; \
+		failed=`expr $$failed + 1`; \
+		col=$$red; res=XPASS; \
+	      ;; \
+	      *) \
+		col=$$grn; res=PASS; \
+	      ;; \
+	      esac; \
+	    elif test $$? -ne 77; then \
+	      all=`expr $$all + 1`; \
+	      case " $(XFAIL_TESTS) " in \
+	      *[\ \	]$$tst[\ \	]*) \
+		xfail=`expr $$xfail + 1`; \
+		col=$$lgn; res=XFAIL; \
+	      ;; \
+	      *) \
+		failed=`expr $$failed + 1`; \
+		col=$$red; res=FAIL; \
+	      ;; \
+	      esac; \
+	    else \
+	      skip=`expr $$skip + 1`; \
+	      col=$$blu; res=SKIP; \
+	    fi; \
+	    echo "$${col}$$res$${std}: $$tst"; \
+	  done; \
+	  if test "$$all" -eq 1; then \
+	    tests="test"; \
+	    All=""; \
+	  else \
+	    tests="tests"; \
+	    All="All "; \
+	  fi; \
+	  if test "$$failed" -eq 0; then \
+	    if test "$$xfail" -eq 0; then \
+	      banner="$$All$$all $$tests passed"; \
+	    else \
+	      if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+	      banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+	    fi; \
+	  else \
+	    if test "$$xpass" -eq 0; then \
+	      banner="$$failed of $$all $$tests failed"; \
+	    else \
+	      if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+	      banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+	    fi; \
+	  fi; \
+	  dashes="$$banner"; \
+	  skipped=""; \
+	  if test "$$skip" -ne 0; then \
+	    if test "$$skip" -eq 1; then \
+	      skipped="($$skip test was not run)"; \
+	    else \
+	      skipped="($$skip tests were not run)"; \
+	    fi; \
+	    test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+	      dashes="$$skipped"; \
+	  fi; \
+	  report=""; \
+	  if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+	    report="Please report to $(PACKAGE_BUGREPORT)"; \
+	    test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+	      dashes="$$report"; \
+	  fi; \
+	  dashes=`echo "$$dashes" | sed s/./=/g`; \
+	  if test "$$failed" -eq 0; then \
+	    col="$$grn"; \
+	  else \
+	    col="$$red"; \
+	  fi; \
+	  echo "$${col}$$dashes$${std}"; \
+	  echo "$${col}$$banner$${std}"; \
+	  test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+	  test -z "$$report" || echo "$${col}$$report$${std}"; \
+	  echo "$${col}$$dashes$${std}"; \
+	  test "$$failed" -eq 0; \
+	else :; fi
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-TESTS check-am clean \
+	clean-generic clean-libtool clean-noinstPROGRAMS cscopelist-am \
+	ctags ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/devices/wifi/tests/test-wifi-ap-utils.c b/src/devices/wifi/tests/test-wifi-ap-utils.c
new file mode 100644
index 00000000..36d58f2a
--- /dev/null
+++ b/src/devices/wifi/tests/test-wifi-ap-utils.c
@@ -0,0 +1,1545 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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, 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) 2011 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "nm-wifi-ap-utils.h"
+#include "nm-dbus-glib-types.h"
+
+#include "nm-setting-connection.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-8021x.h"
+
+#define DEBUG 1
+
+/*******************************************/
+
+#define COMPARE(src, expected, success, error, edomain, ecode) \
+{ \
+	if (expected) { \
+		if (!success) { \
+			g_assert (error != NULL); \
+			g_warning ("Failed to complete connection: (%d) %s", error->code, error->message); \
+		} \
+		g_assert (success == TRUE); \
+		g_assert (error == NULL); \
+\
+		success = nm_connection_compare (src, expected, NM_SETTING_COMPARE_FLAG_EXACT); \
+		if (success == FALSE && DEBUG) { \
+			g_message ("\n- COMPLETED ---------------------------------\n"); \
+			nm_connection_dump (src); \
+			g_message ("+ EXPECTED ++++++++++++++++++++++++++++++++++++\n"); \
+			nm_connection_dump (expected); \
+			g_message ("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"); \
+		} \
+		g_assert (success == TRUE); \
+	} else { \
+		if (success) { \
+			g_message ("\n- COMPLETED ---------------------------------\n"); \
+			nm_connection_dump (src); \
+		} \
+		g_assert (success == FALSE); \
+		g_assert_error (error, edomain, ecode); \
+	} \
+ \
+	g_clear_error (&error); \
+}
+
+static gboolean
+complete_connection (const char *ssid,
+                     const guint8 bssid[ETH_ALEN],
+                     NM80211Mode mode,
+                     guint32 flags,
+                     guint32 wpa_flags,
+                     guint32 rsn_flags,
+                     gboolean lock_bssid,
+                     NMConnection *src,
+                     GError **error)
+{
+	GByteArray *tmp;
+	gboolean success;
+	NMSettingWireless *s_wifi;
+
+	/* Add a wifi setting if one doesn't exist */
+	s_wifi = nm_connection_get_setting_wireless (src);
+	if (!s_wifi) {
+		s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+		nm_connection_add_setting (src, NM_SETTING (s_wifi));
+	}
+
+	tmp = g_byte_array_sized_new (strlen (ssid));
+	g_byte_array_append (tmp, (const guint8 *) ssid, strlen (ssid));
+
+	success = nm_ap_utils_complete_connection (tmp,
+	                                           bssid,
+	                                           mode,
+	                                           flags,
+	                                           wpa_flags,
+	                                           rsn_flags,
+	                                           src,
+	                                           lock_bssid,
+	                                           error);
+	g_byte_array_free (tmp, TRUE);
+	return success;
+}
+
+typedef struct {
+	const char *key;
+	const char *str;
+	guint32     uint;
+} KeyData;
+
+static void
+set_items (NMSetting *setting, const KeyData *items)
+{
+	const KeyData *item;
+	GParamSpec *pspec;
+	GByteArray *tmp;
+
+	for (item = items; item && item->key; item++) {
+		g_assert (item->key);
+		pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), item->key);
+		g_assert (pspec);
+
+		if (pspec->value_type == G_TYPE_STRING) {
+			g_assert (item->uint == 0);
+			if (item->str)
+				g_object_set (G_OBJECT (setting), item->key, item->str, NULL);
+		} else if (pspec->value_type == G_TYPE_UINT) {
+			g_assert (item->str == NULL);
+			g_object_set (G_OBJECT (setting), item->key, item->uint, NULL);
+		} else if (pspec->value_type == G_TYPE_INT) {
+			gint foo = (gint) item->uint;
+
+			g_assert (item->str == NULL);
+			g_object_set (G_OBJECT (setting), item->key, foo, NULL);
+		} else if (pspec->value_type == G_TYPE_BOOLEAN) {
+			gboolean foo = !! (item->uint);
+
+			g_assert (item->str == NULL);
+			g_object_set (G_OBJECT (setting), item->key, foo, NULL);
+		} else if (pspec->value_type == DBUS_TYPE_G_UCHAR_ARRAY) {
+			g_assert (item->str);
+			tmp = g_byte_array_sized_new (strlen (item->str));
+			g_byte_array_append (tmp, (const guint8 *) item->str, strlen (item->str));
+			g_object_set (G_OBJECT (setting), item->key, tmp, NULL);
+			g_byte_array_free (tmp, TRUE);
+		} else {
+			/* Special types, check based on property name */
+			if (!strcmp (item->key, NM_SETTING_WIRELESS_SECURITY_PROTO))
+				nm_setting_wireless_security_add_proto (NM_SETTING_WIRELESS_SECURITY (setting), item->str);
+			else if (!strcmp (item->key, NM_SETTING_WIRELESS_SECURITY_PAIRWISE))
+				nm_setting_wireless_security_add_pairwise (NM_SETTING_WIRELESS_SECURITY (setting), item->str);
+			else if (!strcmp (item->key, NM_SETTING_WIRELESS_SECURITY_GROUP))
+				nm_setting_wireless_security_add_group (NM_SETTING_WIRELESS_SECURITY (setting), item->str);
+			else if (!strcmp (item->key, NM_SETTING_802_1X_EAP))
+				nm_setting_802_1x_add_eap_method (NM_SETTING_802_1X (setting), item->str);
+		}
+	}
+}
+
+static NMSettingWireless *
+fill_wifi_empty (NMConnection *connection)
+{
+	NMSettingWireless *s_wifi;
+
+	s_wifi = nm_connection_get_setting_wireless (connection);
+	if (!s_wifi) {
+		s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+		nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+	}
+	return s_wifi;
+}
+
+static NMSettingWireless *
+fill_wifi (NMConnection *connection, const KeyData items[])
+{
+	NMSettingWireless *s_wifi;
+
+	s_wifi = nm_connection_get_setting_wireless (connection);
+	if (!s_wifi) {
+		s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+		nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+	}
+
+	set_items (NM_SETTING (s_wifi), items);
+	return s_wifi;
+}
+
+static NMSettingWirelessSecurity *
+fill_wsec (NMConnection *connection, const KeyData items[])
+{
+	NMSettingWirelessSecurity *s_wsec;
+
+	s_wsec = nm_connection_get_setting_wireless_security (connection);
+	if (!s_wsec) {
+		s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
+		nm_connection_add_setting (connection, NM_SETTING (s_wsec));
+	}
+
+	set_items (NM_SETTING (s_wsec), items);
+	return s_wsec;
+}
+
+static NMSetting8021x *
+fill_8021x (NMConnection *connection, const KeyData items[])
+{
+	NMSetting8021x *s_8021x;
+
+	s_8021x = nm_connection_get_setting_802_1x (connection);
+	if (!s_8021x) {
+		s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
+		nm_connection_add_setting (connection, NM_SETTING (s_8021x));
+	}
+
+	set_items (NM_SETTING (s_8021x), items);
+	return s_8021x;
+}
+
+static NMConnection *
+create_basic (const char *ssid,
+              const guint8 *bssid,
+              NM80211Mode mode)
+{
+	NMConnection *connection;
+	NMSettingWireless *s_wifi = NULL;
+	GByteArray *tmp;
+
+	connection = nm_connection_new ();
+
+	s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+
+	/* SSID */
+	tmp = g_byte_array_sized_new (strlen (ssid));
+	g_byte_array_append (tmp, (const guint8 *) ssid, strlen (ssid));
+	g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_SSID, tmp, NULL);
+	g_byte_array_free (tmp, TRUE);
+
+	/* BSSID */
+	if (bssid) {
+		tmp = g_byte_array_sized_new (ETH_ALEN);
+		g_byte_array_append (tmp, bssid, ETH_ALEN);
+		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_BSSID, tmp, NULL);
+		g_byte_array_free (tmp, TRUE);
+	}
+
+	if (mode == NM_802_11_MODE_INFRA)
+		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, "infrastructure", NULL);
+	else if (mode == NM_802_11_MODE_ADHOC)
+		g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MODE, "adhoc", NULL);
+	else
+		g_assert_not_reached ();
+
+	return connection;
+}
+
+/*******************************************/
+
+static void
+test_lock_bssid (void)
+{
+	NMConnection *src, *expected;
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *ssid = "blahblah";
+	gboolean success;
+	GError *error = NULL;
+
+	src = nm_connection_new ();
+	success = complete_connection (ssid, bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE,
+	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
+	                               TRUE,
+	                               src, &error);
+	expected = create_basic (ssid, bssid, NM_802_11_MODE_INFRA);
+	COMPARE (src, expected, success, error, 0, 0);
+
+	g_object_unref (src);
+	g_object_unref (expected);
+}
+
+/*******************************************/
+
+static void
+test_open_ap_empty_connection (void)
+{
+	NMConnection *src, *expected;
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *ssid = "blahblah";
+	gboolean success;
+	GError *error = NULL;
+
+	/* Test that an empty source connection is correctly filled with the
+	 * SSID and Infra modes of the given AP details.
+	 */
+
+	src = nm_connection_new ();
+	success = complete_connection (ssid, bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE,
+	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
+	                               FALSE,
+	                               src, &error);
+	expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA);
+	COMPARE (src, expected, success, error, 0, 0);
+
+	g_object_unref (src);
+	g_object_unref (expected);
+}
+
+/*******************************************/
+
+static void
+test_open_ap_leap_connection_1 (gconstpointer add_wifi)
+{
+	NMConnection *src;
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const KeyData src_wsec[] = { { NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, "Bill Smith", 0 }, { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	/* Test that a basic connection filled with a LEAP username is
+	 * rejected when completion is attempted with an open AP.  LEAP requires
+	 * the AP to have the Privacy bit set.
+	 */
+
+	src = nm_connection_new ();
+	if (add_wifi)
+		fill_wifi_empty (src);
+	fill_wsec (src, src_wsec);
+
+	success = complete_connection ("blahblah", bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE,
+	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
+	                               FALSE,
+	                               src, &error);
+	/* We expect failure */
+	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+
+	g_object_unref (src);
+}
+
+/*******************************************/
+
+static void
+test_open_ap_leap_connection_2 (void)
+{
+	NMConnection *src;
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const KeyData src_wsec[] = { { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 }, { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	/* Test that a basic connection specifying IEEE8021x security (ie, Dynamic
+	 * WEP or LEAP) is rejected when completion is attempted with an open AP.
+	 */
+
+	src = nm_connection_new ();
+	fill_wifi_empty (src);
+	fill_wsec (src, src_wsec);
+
+	success = complete_connection ("blahblah", bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE,
+	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
+	                               FALSE,
+	                               src, &error);
+	/* We expect failure */
+	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+
+	g_object_unref (src);
+}
+
+/*******************************************/
+
+static void
+test_open_ap_wep_connection (gconstpointer add_wifi)
+{
+	NMConnection *src;
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const KeyData src_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, "11111111111111111111111111", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, NULL, 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
+	    { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	/* Test that a static WEP connection is rejected when completion is
+	 * attempted with an open AP.
+	 */
+
+	src = nm_connection_new ();
+	if (add_wifi)
+		fill_wifi_empty (src);
+	fill_wsec (src, src_wsec);
+	success = complete_connection ("blahblah", bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_NONE,
+	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
+	                               FALSE,
+	                               src, &error);
+	/* We expect failure */
+	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+
+	g_object_unref (src);
+}
+
+/*******************************************/
+
+static void
+test_ap_wpa_psk_connection_base (const char *key_mgmt,
+                                 const char *auth_alg,
+                                 guint32 flags,
+                                 guint32 wpa_flags,
+                                 guint32 rsn_flags,
+                                 gboolean add_wifi,
+                                 NMConnection *expected)
+{
+	NMConnection *src;
+	const char *ssid = "blahblah";
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const KeyData exp_wifi[] = {
+		{ NM_SETTING_WIRELESS_SSID, ssid, 0 },
+		{ NM_SETTING_WIRELESS_MODE, "infrastructure", 0 },
+		{ NULL } };
+	const KeyData both_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, auth_alg, 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_PSK, "asdfasdfasdfasdfasdfafs", 0 },
+	    { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	src = nm_connection_new ();
+	if (add_wifi)
+		fill_wifi_empty (src);
+	fill_wsec (src, both_wsec);
+	success = complete_connection (ssid, bssid, NM_802_11_MODE_INFRA,
+	                               flags, wpa_flags, rsn_flags,
+	                               FALSE, src, &error);
+	if (expected) {
+		fill_wifi (expected, exp_wifi);
+		fill_wsec (expected, both_wsec);
+	}
+	COMPARE (src, expected, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+
+	g_object_unref (src);
+}
+
+static void
+test_open_ap_wpa_psk_connection_1 (void)
+{
+	/* Test that a WPA-PSK connection filling only the PSK itself and *not*
+	 * filling the wifi setting is rejected when completion is attempted with
+	 * an open AP.
+	 */
+	test_ap_wpa_psk_connection_base (NULL, NULL,
+	                                 NM_802_11_AP_FLAGS_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 FALSE, NULL);
+}
+
+static void
+test_open_ap_wpa_psk_connection_2 (void)
+{
+	/* Test that a WPA-PSK connection filling only the PSK itself and also
+	 * filling the wifi setting is rejected when completion is attempted with
+	 * an open AP.
+	 */
+	test_ap_wpa_psk_connection_base (NULL, NULL,
+	                                 NM_802_11_AP_FLAGS_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 TRUE, NULL);
+}
+
+static void
+test_open_ap_wpa_psk_connection_3 (void)
+{
+	/* Test that a WPA-PSK connection filling the PSK and setting the auth alg
+	 * to 'open' is rejected when completion is attempted with an open AP.
+	 */
+	test_ap_wpa_psk_connection_base (NULL, "open",
+	                                 NM_802_11_AP_FLAGS_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 FALSE, NULL);
+}
+
+static void
+test_open_ap_wpa_psk_connection_4 (void)
+{
+	/* Test that a WPA-PSK connection filling the PSK and setting the auth alg
+	 * to 'shared' is rejected when completion is attempted with an open AP.
+	 * Shared auth cannot be used with WPA.
+	 */
+	test_ap_wpa_psk_connection_base (NULL, "shared",
+	                                 NM_802_11_AP_FLAGS_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 FALSE, NULL);
+}
+
+static void
+test_open_ap_wpa_psk_connection_5 (void)
+{
+	/* Test that a WPA-PSK connection filling the PSK, the auth algorithm, and
+	 * key management is rejected when completion is attempted with an open AP.
+	 */
+	test_ap_wpa_psk_connection_base ("wpa-psk", "open",
+	                                 NM_802_11_AP_FLAGS_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 FALSE, NULL);
+}
+
+/*******************************************/
+
+static void
+test_ap_wpa_eap_connection_base (const char *key_mgmt,
+                                 const char *auth_alg,
+                                 guint32 flags,
+                                 guint32 wpa_flags,
+                                 guint32 rsn_flags,
+                                 gboolean add_wifi,
+                                 guint error_domain,
+                                 guint error_code)
+{
+	NMConnection *src;
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const KeyData src_empty[] = { { NULL } };
+	const KeyData src_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, auth_alg, 0 },
+	    { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	src = nm_connection_new ();
+	if (add_wifi)
+		fill_wifi_empty (src);
+	fill_wsec (src, src_wsec);
+	fill_8021x (src, src_empty);
+	success = complete_connection ("blahblah", bssid, NM_802_11_MODE_INFRA,
+	                               flags, wpa_flags, rsn_flags,
+	                               FALSE, src, &error);
+	/* Failure expected */
+	COMPARE (src, NULL, success, error, error_domain, error_code);
+
+	g_object_unref (src);
+}
+
+enum {
+	IDX_NONE = 0,
+	IDX_OPEN,
+	IDX_PRIV,
+	IDX_WPA_PSK_PTKIP_GTKIP,
+	IDX_WPA_PSK_PTKIP_PCCMP_GTKIP,
+	IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP,
+	IDX_WPA_RSN_PSK_PCCMP_GCCMP,
+	IDX_RSN_PSK_PCCMP_GCCMP,
+	IDX_RSN_PSK_PTKIP_PCCMP_GTKIP,
+	IDX_WPA_8021X,
+	IDX_RSN_8021X,
+};
+
+static guint32
+flags_for_idx (guint32 idx)
+{
+	if (idx == IDX_OPEN)
+		return NM_802_11_AP_FLAGS_NONE;
+	else if (   idx == IDX_PRIV
+	         || idx == IDX_WPA_PSK_PTKIP_GTKIP
+	         || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP
+	         || idx == IDX_RSN_PSK_PCCMP_GCCMP
+	         || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP
+	         || idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP
+	         || idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP
+	         || idx == IDX_WPA_8021X
+	         || idx == IDX_RSN_8021X)
+		return NM_802_11_AP_FLAGS_PRIVACY;
+	else
+		g_assert_not_reached ();
+}
+
+static guint32
+wpa_flags_for_idx (guint32 idx)
+{
+	if (idx == IDX_OPEN || idx == IDX_PRIV ||  idx == IDX_RSN_8021X
+	    || idx == IDX_RSN_PSK_PCCMP_GCCMP || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP)
+		return NM_802_11_AP_SEC_NONE;
+	else if (idx == IDX_WPA_PSK_PTKIP_GTKIP)
+		return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_PSK;
+	else if (idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP)
+		return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_PSK;
+	else if (IDX_WPA_RSN_PSK_PCCMP_GCCMP)
+		return NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_CCMP | NM_802_11_AP_SEC_KEY_MGMT_PSK;
+	else if (idx == IDX_WPA_8021X)
+		return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_802_1X;
+	else
+		g_assert_not_reached ();
+}
+
+static guint32
+rsn_flags_for_idx (guint32 idx)
+{
+	if (idx == IDX_OPEN || idx == IDX_PRIV || idx == IDX_WPA_8021X
+	    || idx == IDX_WPA_PSK_PTKIP_GTKIP || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP)
+		return NM_802_11_AP_SEC_NONE;
+	else if (idx == IDX_RSN_PSK_PCCMP_GCCMP)
+		return NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_CCMP | NM_802_11_AP_SEC_KEY_MGMT_PSK;
+	else if (idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP)
+		return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_PSK;
+	else if (idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP)
+		return NM_802_11_AP_SEC_PAIR_TKIP | NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_TKIP | NM_802_11_AP_SEC_KEY_MGMT_PSK;
+	else if (idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP)
+		return NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_CCMP | NM_802_11_AP_SEC_KEY_MGMT_PSK;
+	else if (idx == IDX_RSN_8021X)
+		return NM_802_11_AP_SEC_PAIR_CCMP | NM_802_11_AP_SEC_GROUP_CCMP | NM_802_11_AP_SEC_KEY_MGMT_802_1X;
+	else
+		g_assert_not_reached ();
+}
+
+static guint32
+error_domain_for_idx (guint32 idx, guint num)
+{
+	if (idx == IDX_OPEN)
+		return NM_SETTING_WIRELESS_SECURITY_ERROR;
+	else if (idx == IDX_PRIV) {
+		if (num <= 3)
+			return NM_SETTING_802_1X_ERROR;
+		else
+			return NM_SETTING_WIRELESS_SECURITY_ERROR;
+	} else if (idx == IDX_WPA_PSK_PTKIP_GTKIP || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP
+	           || idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP || idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP
+	           || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP || idx == IDX_RSN_PSK_PCCMP_GCCMP)
+		return NM_SETTING_WIRELESS_SECURITY_ERROR;
+	else
+		g_assert_not_reached ();
+}
+
+static guint32
+error_code_for_idx (guint32 idx, guint num)
+{
+	if (idx == IDX_OPEN)
+		return NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY;
+	else if (idx == IDX_PRIV) {
+		if (num <= 3)
+			return NM_SETTING_802_1X_ERROR_MISSING_PROPERTY;
+		else
+			return NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY;
+	} else if (idx == IDX_WPA_PSK_PTKIP_GTKIP || idx == IDX_WPA_PSK_PTKIP_PCCMP_GTKIP
+	           || idx == IDX_WPA_RSN_PSK_PCCMP_GCCMP || idx == IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP
+	           || idx == IDX_RSN_PSK_PTKIP_PCCMP_GTKIP || idx == IDX_RSN_PSK_PCCMP_GCCMP)
+		return NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY;
+	else
+		g_assert_not_reached ();
+}
+
+static void
+test_ap_wpa_eap_connection_1 (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+
+	test_ap_wpa_eap_connection_base (NULL, NULL,
+	                                 flags_for_idx (idx),
+	                                 wpa_flags_for_idx (idx),
+	                                 rsn_flags_for_idx (idx),
+	                                 FALSE,
+	                                 error_domain_for_idx (idx, 1),
+	                                 error_code_for_idx (idx, 1));
+}
+
+static void
+test_ap_wpa_eap_connection_2 (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+
+	test_ap_wpa_eap_connection_base (NULL, NULL,
+	                                 flags_for_idx (idx),
+	                                 wpa_flags_for_idx (idx),
+	                                 rsn_flags_for_idx (idx),
+	                                 TRUE,
+	                                 error_domain_for_idx (idx, 2),
+	                                 error_code_for_idx (idx, 2));
+}
+
+static void
+test_ap_wpa_eap_connection_3 (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+
+	test_ap_wpa_eap_connection_base (NULL, "open",
+	                                 flags_for_idx (idx),
+	                                 wpa_flags_for_idx (idx),
+	                                 rsn_flags_for_idx (idx),
+	                                 FALSE,
+	                                 error_domain_for_idx (idx, 3),
+	                                 error_code_for_idx (idx, 3));
+}
+
+static void
+test_ap_wpa_eap_connection_4 (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+
+	test_ap_wpa_eap_connection_base (NULL, "shared",
+	                                 flags_for_idx (idx),
+	                                 wpa_flags_for_idx (idx),
+	                                 rsn_flags_for_idx (idx),
+	                                 FALSE,
+	                                 error_domain_for_idx (idx, 4),
+	                                 error_code_for_idx (idx, 4));
+}
+
+static void
+test_ap_wpa_eap_connection_5 (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+
+	test_ap_wpa_eap_connection_base ("wpa-eap", "open",
+	                                 flags_for_idx (idx),
+	                                 wpa_flags_for_idx (idx),
+	                                 rsn_flags_for_idx (idx),
+	                                 FALSE,
+	                                 error_domain_for_idx (idx, 5),
+	                                 error_code_for_idx (idx, 5));
+}
+
+/*******************************************/
+
+static void
+test_priv_ap_empty_connection (void)
+{
+	NMConnection *src, *expected;
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *ssid = "blahblah";
+	const KeyData exp_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", 0 },
+	    { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	/* Test that an empty connection is completed to a valid Static WEP
+	 * connection when completed with an AP with the Privacy bit set.
+	 */
+
+	src = nm_connection_new ();
+	success = complete_connection (ssid, bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY,
+	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
+	                               FALSE,
+	                               src, &error);
+
+	/* Static WEP connection expected */
+	expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA);
+	fill_wsec (expected, exp_wsec);
+	COMPARE (src, expected, success, error, 0, 0);
+
+	g_object_unref (src);
+	g_object_unref (expected);
+}
+
+/*******************************************/
+
+static void
+test_priv_ap_leap_connection_1 (gconstpointer add_wifi)
+{
+	NMConnection *src, *expected;
+	const char *ssid = "blahblah";
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *leap_username = "Bill Smith";
+	const KeyData src_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0 },
+	    { NULL } };
+	const KeyData exp_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0 },
+	    { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	/* Test that an minimal LEAP connection specifying only key management and
+	 * the LEAP username is completed to a full LEAP connection when completed
+	 * with an AP with the Privacy bit set.
+	 */
+
+	src = nm_connection_new ();
+	if (add_wifi)
+		fill_wifi_empty (src);
+	fill_wsec (src, src_wsec);
+	success = complete_connection (ssid, bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY,
+	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
+	                               FALSE,
+	                               src, &error);
+	/* We expect success here; since LEAP APs just set the 'privacy' flag
+	 * there's no way to determine from the AP's beacon whether it's static WEP,
+	 * dynamic WEP, or LEAP.
+	 */
+	expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA);
+	fill_wsec (expected, exp_wsec);
+	COMPARE (src, expected, success, error, 0, 0);
+
+	g_object_unref (src);
+}
+
+/*******************************************/
+
+static void
+test_priv_ap_leap_connection_2 (void)
+{
+	NMConnection *src;
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const KeyData src_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", 0 },
+	    { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	/* Test that an minimal LEAP connection specifying only key management and
+	 * the LEAP auth alg is completed to a full LEAP connection when completed
+	 * with an AP with the Privacy bit set.
+	 */
+
+	src = nm_connection_new ();
+	fill_wifi_empty (src);
+	fill_wsec (src, src_wsec);
+	success = complete_connection ("blahblah", bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY,
+	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
+	                               FALSE,
+	                               src, &error);
+	/* We expect failure here, we need a LEAP username */
+	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_LEAP_REQUIRES_USERNAME);
+
+	g_object_unref (src);
+}
+
+/*******************************************/
+
+static void
+test_priv_ap_dynamic_wep_1 (void)
+{
+	NMConnection *src, *expected;
+	const char *ssid = "blahblah";
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const KeyData src_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
+	    { NULL } };
+	const KeyData both_8021x[] = {
+	    { NM_SETTING_802_1X_EAP, "peap", 0 },
+	    { NM_SETTING_802_1X_IDENTITY, "Bill Smith", 0 },
+	    { NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", 0 },
+	    { NULL } };
+	const KeyData exp_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
+	    { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	/* Test that an minimal Dynamic WEP connection specifying key management,
+	 * the auth algorithm, and valid 802.1x setting is completed to a valid
+	 * Dynamic WEP connection when completed with an AP with the Privacy bit set.
+	 */
+
+	src = nm_connection_new ();
+	fill_wifi_empty (src);
+	fill_wsec (src, src_wsec);
+	fill_8021x (src, both_8021x);
+	success = complete_connection (ssid, bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY,
+	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
+	                               FALSE,
+	                               src, &error);
+
+	/* We expect a completed Dynamic WEP connection */
+	expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA);
+	fill_wsec (expected, exp_wsec);
+	fill_8021x (expected, both_8021x);
+	COMPARE (src, expected, success, error, 0, 0);
+
+	g_object_unref (src);
+}
+
+/*******************************************/
+
+static void
+test_priv_ap_dynamic_wep_2 (void)
+{
+	NMConnection *src, *expected;
+	const char *ssid = "blahblah";
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const KeyData src_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
+	    { NULL } };
+	const KeyData both_8021x[] = {
+	    { NM_SETTING_802_1X_EAP, "peap", 0 },
+	    { NM_SETTING_802_1X_IDENTITY, "Bill Smith", 0 },
+	    { NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", 0 },
+	    { NULL } };
+	const KeyData exp_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
+	    { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	/* Test that an minimal Dynamic WEP connection specifying only the auth
+	 * algorithm and a valid 802.1x setting is completed to a valid Dynamic
+	 * WEP connection when completed with an AP with the Privacy bit set.
+	 */
+
+	src = nm_connection_new ();
+	fill_wifi_empty (src);
+	fill_wsec (src, src_wsec);
+	fill_8021x (src, both_8021x);
+	success = complete_connection (ssid, bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY,
+	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
+	                               FALSE,
+	                               src, &error);
+
+	/* We expect a completed Dynamic WEP connection */
+	expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA);
+	fill_wsec (expected, exp_wsec);
+	fill_8021x (expected, both_8021x);
+	COMPARE (src, expected, success, error, 0, 0);
+
+	g_object_unref (src);
+}
+
+/*******************************************/
+
+static void
+test_priv_ap_dynamic_wep_3 (void)
+{
+	NMConnection *src;
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const KeyData src_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "shared", 0 },
+	    { NULL } };
+	const KeyData src_8021x[] = {
+	    { NM_SETTING_802_1X_EAP, "peap", 0 },
+	    { NM_SETTING_802_1X_IDENTITY, "Bill Smith", 0 },
+	    { NM_SETTING_802_1X_PHASE2_AUTH, "mschapv2", 0 },
+	    { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	/* Ensure that a basic connection specifying 'shared' auth and an 802.1x
+	 * setting is rejected, as 802.1x is incompatible with 'shared' auth.
+	 */
+
+	src = nm_connection_new ();
+	fill_wifi_empty (src);
+	fill_wsec (src, src_wsec);
+	fill_8021x (src, src_8021x);
+	success = complete_connection ("blahblah", bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY,
+	                               NM_802_11_AP_SEC_NONE, NM_802_11_AP_SEC_NONE,
+	                               FALSE,
+	                               src, &error);
+	/* Expect failure; shared is not compatible with dynamic WEP */
+	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+
+	g_object_unref (src);
+}
+
+/*******************************************/
+
+static void
+test_priv_ap_wpa_psk_connection_1 (void)
+{
+	/* Test that a basic WPA-PSK connection is rejected when completion is
+	 * attempted with an AP with just the Privacy bit set.  Lack of WPA/RSN
+	 * flags means the AP provides Static/Dynamic WEP or LEAP, not WPA.
+	 */
+	test_ap_wpa_psk_connection_base (NULL, NULL,
+	                                 NM_802_11_AP_FLAGS_PRIVACY,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 FALSE, NULL);
+}
+
+static void
+test_priv_ap_wpa_psk_connection_2 (void)
+{
+	/* Test that a basic WPA-PSK connection is rejected when completion is
+	 * attempted with an AP with just the Privacy bit set.  Lack of WPA/RSN
+	 * flags means the AP provides Static/Dynamic WEP or LEAP, not WPA.
+	 */
+	test_ap_wpa_psk_connection_base (NULL, NULL,
+	                                 NM_802_11_AP_FLAGS_PRIVACY,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 TRUE, NULL);
+}
+
+static void
+test_priv_ap_wpa_psk_connection_3 (void)
+{
+	/* Test that a basic WPA-PSK connection specifying only the auth algorithm
+	 * is rejected when completion is attempted with an AP with just the Privacy
+	 * bit set.  Lack of WPA/RSN flags means the AP provides Static/Dynamic WEP
+	 * or LEAP, not WPA.
+	 */
+	test_ap_wpa_psk_connection_base (NULL, "open",
+	                                 NM_802_11_AP_FLAGS_PRIVACY,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 FALSE, NULL);
+}
+
+static void
+test_priv_ap_wpa_psk_connection_4 (void)
+{
+	/* Test that a basic WPA-PSK connection specifying only the auth algorithm
+	 * is rejected when completion is attempted with an AP with just the Privacy
+	 * bit set.  Lack of WPA/RSN flags means the AP provides Static/Dynamic WEP
+	 * or LEAP, not WPA.  Second, 'shared' auth is incompatible with WPA.
+	 */
+	test_ap_wpa_psk_connection_base (NULL, "shared",
+	                                 NM_802_11_AP_FLAGS_PRIVACY,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 FALSE, NULL);
+}
+
+static void
+test_priv_ap_wpa_psk_connection_5 (void)
+{
+	/* Test that a WPA-PSK connection specifying both the key management and
+	 * auth algorithm is rejected when completion is attempted with an AP with
+	 * just the Privacy bit set.  Lack of WPA/RSN flags means the AP provides
+	 * Static/Dynamic WEP or LEAP, not WPA.
+	 */
+	test_ap_wpa_psk_connection_base ("wpa-psk", "open",
+	                                 NM_802_11_AP_FLAGS_PRIVACY,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 NM_802_11_AP_SEC_NONE,
+	                                 FALSE, NULL);
+}
+
+/*******************************************/
+
+static void
+test_wpa_ap_empty_connection (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+	NMConnection *src, *expected;
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *ssid = "blahblah";
+	const KeyData exp_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
+	    { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	/* Test that a basic WPA-PSK connection specifying just key management and
+	 * the auth algorithm is completed successfully when given an AP with WPA
+	 * or RSN flags.
+	 */
+
+	src = nm_connection_new ();
+	success = complete_connection (ssid, bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY,
+	                               wpa_flags_for_idx (idx),
+	                               rsn_flags_for_idx (idx),
+	                               FALSE, src, &error);
+
+	/* WPA connection expected */
+	expected = create_basic (ssid, NULL, NM_802_11_MODE_INFRA);
+	fill_wsec (expected, exp_wsec);
+	COMPARE (src, expected, success, error, 0, 0);
+
+	g_object_unref (src);
+	g_object_unref (expected);
+}
+
+/*******************************************/
+
+static void
+test_wpa_ap_leap_connection_1 (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+	NMConnection *src;
+	const char *ssid = "blahblah";
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const char *leap_username = "Bill Smith";
+	const KeyData src_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, leap_username, 0 },
+	    { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	/* Test that completion of a LEAP connection with a WPA-enabled AP is
+	 * rejected since WPA APs (usually) do not support LEAP.
+	 */
+
+	src = nm_connection_new ();
+	fill_wifi_empty (src);
+	fill_wsec (src, src_wsec);
+	success = complete_connection (ssid, bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY,
+	                               wpa_flags_for_idx (idx),
+	                               rsn_flags_for_idx (idx),
+	                               FALSE,
+	                               src, &error);
+	/* Expect failure here; WPA APs don't support old-school LEAP */
+	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+
+	g_object_unref (src);
+}
+
+/*******************************************/
+
+static void
+test_wpa_ap_leap_connection_2 (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+	NMConnection *src;
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const KeyData src_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", 0 },
+	    { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	/* Test that completion of a LEAP connection with a WPA-enabled AP is
+	 * rejected since WPA APs (usually) do not support LEAP.
+	 */
+
+	src = nm_connection_new ();
+	fill_wifi_empty (src);
+	fill_wsec (src, src_wsec);
+	success = complete_connection ("blahblah", bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY,
+	                               wpa_flags_for_idx (idx),
+	                               rsn_flags_for_idx (idx),
+	                               FALSE,
+	                               src, &error);
+	/* We expect failure here, we need a LEAP username */
+	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+
+	g_object_unref (src);
+}
+
+/*******************************************/
+
+static void
+test_wpa_ap_dynamic_wep_connection (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+	NMConnection *src;
+	const guint8 bssid[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+	const KeyData src_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", 0 },
+	    { NULL } };
+	gboolean success;
+	GError *error = NULL;
+
+	/* Test that completion of a Dynamic WEP connection with a WPA-enabled AP is
+	 * rejected since WPA APs (usually) do not support Dynamic WEP.
+	 */
+
+	src = nm_connection_new ();
+	fill_wifi_empty (src);
+	fill_wsec (src, src_wsec);
+	success = complete_connection ("blahblah", bssid,
+	                               NM_802_11_MODE_INFRA, NM_802_11_AP_FLAGS_PRIVACY,
+	                               wpa_flags_for_idx (idx),
+	                               rsn_flags_for_idx (idx),
+	                               FALSE,
+	                               src, &error);
+	/* We expect failure here since Dynamic WEP is incompatible with WPA */
+	COMPARE (src, NULL, success, error, NM_SETTING_WIRELESS_SECURITY_ERROR, NM_SETTING_WIRELESS_SECURITY_ERROR_INVALID_PROPERTY);
+
+	g_object_unref (src);
+}
+
+/*******************************************/
+
+static void
+test_wpa_ap_wpa_psk_connection_1 (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+	NMConnection *expected;
+	const KeyData exp_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
+	    { NULL } };
+
+	expected = nm_connection_new ();
+	fill_wsec (expected, exp_wsec);
+	test_ap_wpa_psk_connection_base (NULL, NULL,
+	                                 NM_802_11_AP_FLAGS_PRIVACY,
+	                                 wpa_flags_for_idx (idx),
+	                                 rsn_flags_for_idx (idx),
+	                                 FALSE, expected);
+	g_object_unref (expected);
+}
+
+static void
+test_wpa_ap_wpa_psk_connection_2 (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+	NMConnection *expected;
+	const KeyData exp_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
+	    { NULL } };
+
+	expected = nm_connection_new ();
+	fill_wsec (expected, exp_wsec);
+	test_ap_wpa_psk_connection_base (NULL, NULL,
+	                                 NM_802_11_AP_FLAGS_PRIVACY,
+	                                 wpa_flags_for_idx (idx),
+	                                 rsn_flags_for_idx (idx),
+	                                 TRUE, expected);
+	g_object_unref (expected);
+}
+
+static void
+test_wpa_ap_wpa_psk_connection_3 (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+	NMConnection *expected;
+	const KeyData exp_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
+	    { NULL } };
+
+	expected = nm_connection_new ();
+	fill_wsec (expected, exp_wsec);
+	test_ap_wpa_psk_connection_base (NULL, "open",
+	                                 NM_802_11_AP_FLAGS_PRIVACY,
+	                                 wpa_flags_for_idx (idx),
+	                                 rsn_flags_for_idx (idx),
+	                                 FALSE, expected);
+	g_object_unref (expected);
+}
+
+static void
+test_wpa_ap_wpa_psk_connection_4 (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+	test_ap_wpa_psk_connection_base (NULL, "shared",
+	                                 NM_802_11_AP_FLAGS_PRIVACY,
+	                                 wpa_flags_for_idx (idx),
+	                                 rsn_flags_for_idx (idx),
+	                                 FALSE, NULL);
+}
+
+static void
+test_wpa_ap_wpa_psk_connection_5 (gconstpointer data)
+{
+	guint idx = GPOINTER_TO_UINT (data);
+	NMConnection *expected;
+	const KeyData exp_wsec[] = {
+	    { NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", 0 },
+	    { NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", 0 },
+	    { NULL } };
+
+	expected = nm_connection_new ();
+	fill_wsec (expected, exp_wsec);
+	test_ap_wpa_psk_connection_base ("wpa-psk", "open",
+	                                 NM_802_11_AP_FLAGS_PRIVACY,
+	                                 wpa_flags_for_idx (idx),
+	                                 rsn_flags_for_idx (idx),
+	                                 FALSE, expected);
+	g_object_unref (expected);
+}
+
+/*******************************************/
+
+static void
+test_strength_dbm (void)
+{
+	/* boundary conditions first */
+	g_assert_cmpint (nm_ap_utils_level_to_quality (-1), ==, 100);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (-40), ==, 100);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (-30), ==, 100);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (-100), ==, 0);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (-200), ==, 0);
+
+	g_assert_cmpint (nm_ap_utils_level_to_quality (-81), ==, 32);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (-92), ==, 14);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (-74), ==, 44);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (-81), ==, 32);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (-66), ==, 57);
+}
+
+static void
+test_strength_percent (void)
+{
+	int i;
+
+	/* boundary conditions first */
+	g_assert_cmpint (nm_ap_utils_level_to_quality (0), ==, 0);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (100), ==, 100);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (110), ==, 100);
+
+	for (i = 0; i <= 100; i++)
+		g_assert_cmpint (nm_ap_utils_level_to_quality (i), ==, i);
+}
+
+static void
+test_strength_wext (void)
+{
+	/* boundary conditions that we assume aren't WEXT first */
+	g_assert_cmpint (nm_ap_utils_level_to_quality (256), ==, 100);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (110), ==, 100);
+
+	/* boundary conditions that we assume are WEXT */
+	g_assert_cmpint (nm_ap_utils_level_to_quality (111), ==, 0);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (150), ==, 0);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (225), ==, 100);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (255), ==, 100);
+
+	g_assert_cmpint (nm_ap_utils_level_to_quality (157), ==, 2);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (200), ==, 74);
+	g_assert_cmpint (nm_ap_utils_level_to_quality (215), ==, 99);
+}
+
+/*******************************************/
+
+int
+main (int argc, char **argv)
+{
+	gsize i;
+
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+	g_type_init ();
+#endif
+
+	g_test_init (&argc, &argv, NULL);
+
+	g_test_add_func ("/wifi/lock_bssid",
+	                 test_lock_bssid);
+
+	/* Open AP tests; make sure that connections to be completed that have
+	 * various security-related settings already set cause the completion
+	 * to fail.
+	 */
+	g_test_add_func ("/wifi/open_ap/empty_connection",
+	                 test_open_ap_empty_connection);
+	g_test_add_data_func ("/wifi/open_ap/leap_connection/1",
+	                      (gconstpointer) TRUE,
+	                      test_open_ap_leap_connection_1);
+	g_test_add_data_func ("/wifi/open_ap/leap_connection/1_no_add_wifi",
+	                      (gconstpointer) FALSE,
+	                      test_open_ap_leap_connection_1);
+	g_test_add_func ("/wifi/open_ap/leap_connection/2",
+	                 test_open_ap_leap_connection_2);
+	g_test_add_data_func ("/wifi/open_ap/wep_connection",
+	                      (gconstpointer) TRUE,
+	                      test_open_ap_wep_connection);
+	g_test_add_data_func ("/wifi/open_ap/wep_connection",
+	                      (gconstpointer) FALSE,
+	                      test_open_ap_wep_connection);
+
+	g_test_add_func ("/wifi/open_ap/wpa_psk_connection/1",
+	                 test_open_ap_wpa_psk_connection_1);
+	g_test_add_func ("/wifi/open_ap/wpa_psk_connection/2",
+	                 test_open_ap_wpa_psk_connection_2);
+	g_test_add_func ("/wifi/open_ap/wpa_psk_connection/3",
+	                 test_open_ap_wpa_psk_connection_3);
+	g_test_add_func ("/wifi/open_ap/wpa_psk_connection/4",
+	                 test_open_ap_wpa_psk_connection_4);
+	g_test_add_func ("/wifi/open_ap/wpa_psk_connection/5",
+	                 test_open_ap_wpa_psk_connection_5);
+
+	g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/1",
+	                      (gconstpointer) IDX_OPEN,
+	                      test_ap_wpa_eap_connection_1);
+	g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/2",
+	                      (gconstpointer) IDX_OPEN,
+	                      test_ap_wpa_eap_connection_2);
+	g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/3",
+	                      (gconstpointer) IDX_OPEN,
+	                      test_ap_wpa_eap_connection_3);
+	g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/4",
+	                      (gconstpointer) IDX_OPEN,
+	                      test_ap_wpa_eap_connection_4);
+	g_test_add_data_func ("/wifi/open_ap/wpa_eap_connection/5",
+	                      (gconstpointer) IDX_OPEN,
+	                      test_ap_wpa_eap_connection_5);
+
+	/* WEP AP tests */
+	g_test_add_func ("/wifi/priv_ap/empty_connection",
+	                 test_priv_ap_empty_connection);
+	g_test_add_data_func ("/wifi/priv_ap/leap_connection/1",
+	                      (gconstpointer) FALSE,
+	                      test_priv_ap_leap_connection_1);
+	g_test_add_func ("/wifi/priv_ap/leap_connection/2",
+	                 test_priv_ap_leap_connection_2);
+
+	g_test_add_func ("/wifi/priv_ap/dynamic_wep/1",
+	                 test_priv_ap_dynamic_wep_1);
+	g_test_add_func ("/wifi/priv_ap/dynamic_wep/2",
+	                 test_priv_ap_dynamic_wep_2);
+	g_test_add_func ("/wifi/priv_ap/dynamic_wep/3",
+	                 test_priv_ap_dynamic_wep_3);
+
+	g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/1",
+	                 test_priv_ap_wpa_psk_connection_1);
+	g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/2",
+	                 test_priv_ap_wpa_psk_connection_2);
+	g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/3",
+	                 test_priv_ap_wpa_psk_connection_3);
+	g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/4",
+	                 test_priv_ap_wpa_psk_connection_4);
+	g_test_add_func ("/wifi/priv_ap/wpa_psk_connection/5",
+	                 test_priv_ap_wpa_psk_connection_5);
+
+	g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/1",
+	                      (gconstpointer) IDX_PRIV,
+	                      test_ap_wpa_eap_connection_1);
+	g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/2",
+	                      (gconstpointer) IDX_PRIV,
+	                      test_ap_wpa_eap_connection_2);
+	g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/3",
+	                      (gconstpointer) IDX_PRIV,
+	                      test_ap_wpa_eap_connection_3);
+	g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/4",
+	                      (gconstpointer) IDX_PRIV,
+	                      test_ap_wpa_eap_connection_4);
+	g_test_add_data_func ("/wifi/priv_ap/wpa_eap_connection/5",
+	                      (gconstpointer) IDX_PRIV,
+	                      test_ap_wpa_eap_connection_5);
+
+	/* WPA-PSK tests */
+	for (i = IDX_WPA_PSK_PTKIP_GTKIP; i <= IDX_WPA_RSN_PSK_PCCMP_GCCMP; i++) {
+		g_test_add_data_func ("/wifi/wpa_psk/empty_connection",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_empty_connection);
+		g_test_add_data_func ("/wifi/wpa_psk/leap_connection/1",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_leap_connection_1);
+		g_test_add_data_func ("/wifi/wpa_psk/leap_connection/2",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_leap_connection_2);
+
+		g_test_add_data_func ("/wifi/wpa_psk/dynamic_wep_connection",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_dynamic_wep_connection);
+
+		g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/1",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_wpa_psk_connection_1);
+		g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/2",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_wpa_psk_connection_2);
+		g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/3",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_wpa_psk_connection_3);
+		g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/4",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_wpa_psk_connection_4);
+		g_test_add_data_func ("/wifi/wpa_psk/wpa_psk_connection/5",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_wpa_psk_connection_5);
+
+		g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/1",
+		                      (gconstpointer) i,
+		                      test_ap_wpa_eap_connection_1);
+		g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/2",
+		                      (gconstpointer) i,
+		                      test_ap_wpa_eap_connection_2);
+		g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/3",
+		                      (gconstpointer) i,
+		                      test_ap_wpa_eap_connection_3);
+		g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/4",
+		                      (gconstpointer) i,
+		                      test_ap_wpa_eap_connection_4);
+		g_test_add_data_func ("/wifi/wpa_psk/wpa_eap_connection/5",
+		                      (gconstpointer) i,
+		                      test_ap_wpa_eap_connection_5);
+	}
+
+	/* RSN-PSK tests */
+	for (i = IDX_WPA_RSN_PSK_PTKIP_PCCMP_GTKIP; i <= IDX_RSN_PSK_PTKIP_PCCMP_GTKIP; i++) {
+		g_test_add_data_func ("/wifi/rsn_psk/empty_connection",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_empty_connection);
+		g_test_add_data_func ("/wifi/rsn_psk/leap_connection/1",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_leap_connection_1);
+		g_test_add_data_func ("/wifi/rsn_psk/leap_connection/2",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_leap_connection_2);
+
+		g_test_add_data_func ("/wifi/rsn_psk/dynamic_wep_connection",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_dynamic_wep_connection);
+
+		g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/1",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_wpa_psk_connection_1);
+		g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/2",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_wpa_psk_connection_2);
+		g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/3",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_wpa_psk_connection_3);
+		g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/4",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_wpa_psk_connection_4);
+		g_test_add_data_func ("/wifi/rsn_psk/wpa_psk_connection/5",
+		                      (gconstpointer) i,
+		                      test_wpa_ap_wpa_psk_connection_5);
+
+		g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/1",
+		                      (gconstpointer) i,
+		                      test_ap_wpa_eap_connection_1);
+		g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/2",
+		                      (gconstpointer) i,
+		                      test_ap_wpa_eap_connection_2);
+		g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/3",
+		                      (gconstpointer) i,
+		                      test_ap_wpa_eap_connection_3);
+		g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/4",
+		                      (gconstpointer) i,
+		                      test_ap_wpa_eap_connection_4);
+		g_test_add_data_func ("/wifi/rsn_psk/wpa_eap_connection/5",
+		                      (gconstpointer) i,
+		                      test_ap_wpa_eap_connection_5);
+	}
+
+	/* Scanned signal strength conversion tests */
+	g_test_add_func ("/wifi/strength/dbm",
+	                 test_strength_dbm);
+	g_test_add_func ("/wifi/strength/percent",
+	                 test_strength_percent);
+	g_test_add_func ("/wifi/strength/wext",
+	                 test_strength_wext);
+
+	return g_test_run ();
+}