summary refs log tree commit diff
path: root/libnm-glib/nm-device.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2012-03-24 01:37:02 +0100
committerMichael Biebl <biebl@debian.org>2012-03-24 01:37:02 +0100
commitde06e5715e780baade318f3490ac7a4c9ce84e32 (patch)
tree23fbc3fafc12072476eff98bee60100eb54c29db /libnm-glib/nm-device.c
parentb436a68a20ff3114ded32a7a3d70cdd4954039f9 (diff)
Imported Upstream version 0.9.4.0 upstream/0.9.4.0
Diffstat (limited to 'libnm-glib/nm-device.c')
-rw-r--r--libnm-glib/nm-device.c786
1 files changed, 320 insertions, 466 deletions
diff --git a/libnm-glib/nm-device.c b/libnm-glib/nm-device.c
index 1f9f3671..96a037d5 100644
--- a/libnm-glib/nm-device.c
+++ b/libnm-glib/nm-device.c
@@ -18,7 +18,7 @@
  * Boston, MA 02110-1301 USA.
  *
  * Copyright (C) 2007 - 2008 Novell, Inc.
- * Copyright (C) 2007 - 2011 Red Hat, Inc.
+ * Copyright (C) 2007 - 2012 Red Hat, Inc.
  */
 
 #include <string.h>
@@ -30,16 +30,32 @@
 #include "nm-device-wifi.h"
 #include "nm-device-modem.h"
 #include "nm-device-bt.h"
+#include "nm-device-olpc-mesh.h"
 #include "nm-device-wimax.h"
+#include "nm-device-infiniband.h"
+#include "nm-device-bond.h"
+#include "nm-device-vlan.h"
 #include "nm-device.h"
 #include "nm-device-private.h"
 #include "nm-object-private.h"
 #include "nm-object-cache.h"
-#include "nm-marshal.h"
+#include "nm-glib-marshal.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
 
-#include "nm-device-bindings.h"
+static GType _nm_device_type_for_path (DBusGConnection *connection,
+                                       const char *path);
+static void _nm_device_type_for_path_async (DBusGConnection *connection,
+                                            const char *path,
+                                            NMObjectTypeCallbackFunc callback,
+                                            gpointer user_data);
 
-G_DEFINE_TYPE (NMDevice, nm_device, NM_TYPE_OBJECT)
+G_DEFINE_TYPE_WITH_CODE (NMDevice, nm_device, NM_TYPE_OBJECT,
+                         _nm_object_register_type_func (g_define_type_id, _nm_device_type_for_path,
+                                                        _nm_device_type_for_path_async);
+                         )
+
+#define DBUS_G_TYPE_UINT_STRUCT (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID))
 
 #define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate))
 
@@ -56,17 +72,13 @@ typedef struct {
 	gboolean managed;
 	gboolean firmware_missing;
 	NMIP4Config *ip4_config;
-	gboolean got_ip4_config;
 	NMDHCP4Config *dhcp4_config;
-	gboolean got_dhcp4_config;
 	NMIP6Config *ip6_config;
-	gboolean got_ip6_config;
 	NMDHCP6Config *dhcp6_config;
-	gboolean got_dhcp6_config;
 	NMDeviceState state;
+	NMDeviceStateReason reason;
 
 	NMActiveConnection *active_connection;
-	gboolean got_active_connection;
 
 	GUdevClient *client;
 	char *product;
@@ -85,6 +97,7 @@ enum {
 	PROP_DHCP4_CONFIG,
 	PROP_IP6_CONFIG,
 	PROP_STATE,
+	PROP_STATE_REASON,
 	PROP_PRODUCT,
 	PROP_VENDOR,
 	PROP_DHCP6_CONFIG,
@@ -110,211 +123,56 @@ nm_device_init (NMDevice *device)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
 
 	priv->state = NM_DEVICE_STATE_UNKNOWN;
+	priv->reason = NM_DEVICE_STATE_REASON_NONE;
 }
 
 static gboolean
-demarshal_ip4_config (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
-	const char *path;
-	NMIP4Config *config = NULL;
-	DBusGConnection *connection;
-
-	if (value) {
-		if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
-			return FALSE;
-
-		path = g_value_get_boxed (value);
-		if (path) {
-			config = NM_IP4_CONFIG (_nm_object_cache_get (path));
-			if (!config) {
-				connection = nm_object_get_connection (object);
-				config = NM_IP4_CONFIG (nm_ip4_config_new (connection, path));
-			}
-		}
-	}
-
-	priv->got_ip4_config = TRUE;
-
-	if (priv->ip4_config) {
-		g_object_unref (priv->ip4_config);
-		priv->ip4_config = NULL;
-	}
-
-	if (config)
-		priv->ip4_config = config;
-
-	_nm_object_queue_notify (object, NM_DEVICE_IP4_CONFIG);
-	return TRUE;
-}
-
-static gboolean
-demarshal_dhcp4_config (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
-	const char *path;
-	NMDHCP4Config *config = NULL;
-	DBusGConnection *connection;
-
-	if (value) {
-		if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
-			return FALSE;
-
-		path = g_value_get_boxed (value);
-		if (path) {
-			config = NM_DHCP4_CONFIG (_nm_object_cache_get (path));
-			if (!config) {
-				connection = nm_object_get_connection (object);
-				config = NM_DHCP4_CONFIG (nm_dhcp4_config_new (connection, path));
-			}
-		}
-	}
-
-	priv->got_dhcp4_config = TRUE;
-
-	if (priv->dhcp4_config) {
-		g_object_unref (priv->dhcp4_config);
-		priv->dhcp4_config = NULL;
-	}
-
-	if (config)
-		priv->dhcp4_config = config;
-
-	_nm_object_queue_notify (object, NM_DEVICE_DHCP4_CONFIG);
-	return TRUE;
-}
-
-static gboolean
-demarshal_ip6_config (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
-	const char *path;
-	NMIP6Config *config = NULL;
-	DBusGConnection *connection;
-
-	if (value) {
-		if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
-			return FALSE;
-
-		path = g_value_get_boxed (value);
-		if (path) {
-			config = NM_IP6_CONFIG (_nm_object_cache_get (path));
-			if (!config) {
-				connection = nm_object_get_connection (object);
-				config = NM_IP6_CONFIG (nm_ip6_config_new (connection, path));
-			}
-		}
-	}
-
-	priv->got_ip6_config = TRUE;
-
-	if (priv->ip6_config) {
-		g_object_unref (priv->ip6_config);
-		priv->ip6_config = NULL;
-	}
-
-	if (config)
-		priv->ip6_config = config;
-
-	_nm_object_queue_notify (object, NM_DEVICE_IP6_CONFIG);
-	return TRUE;
-}
-
-static gboolean
-demarshal_dhcp6_config (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
+demarshal_state_reason (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
-	const char *path;
-	NMDHCP6Config *config = NULL;
-	DBusGConnection *connection;
-
-	if (value) {
-		if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
-			return FALSE;
-
-		path = g_value_get_boxed (value);
-		if (path) {
-			config = NM_DHCP6_CONFIG (_nm_object_cache_get (path));
-			if (!config) {
-				connection = nm_object_get_connection (object);
-				config = NM_DHCP6_CONFIG (nm_dhcp6_config_new (connection, path));
-			}
-		}
-	}
 
-	priv->got_dhcp6_config = TRUE;
-
-	if (priv->dhcp6_config) {
-		g_object_unref (priv->dhcp6_config);
-		priv->dhcp6_config = NULL;
-	}
-
-	if (config)
-		priv->dhcp6_config = config;
-
-	_nm_object_queue_notify (object, NM_DEVICE_DHCP6_CONFIG);
-	return TRUE;
-}
-
-static gboolean
-demarshal_active_connection (NMObject *object, GParamSpec *pspec, GValue *value, gpointer field)
-{
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
-	const char *path;
-	NMActiveConnection *active = NULL;
-	DBusGConnection *connection;
-
-	if (!G_VALUE_HOLDS (value, DBUS_TYPE_G_OBJECT_PATH))
+	if (!G_VALUE_HOLDS (value, DBUS_G_TYPE_UINT_STRUCT))
 		return FALSE;
 
-	priv->got_active_connection = TRUE;
+	dbus_g_type_struct_get (value,
+	                        0, &priv->state,
+	                        1, &priv->reason,
+	                        G_MAXUINT);
 
-	if (value) {
-		path = g_value_get_boxed (value);
-		if (path) {
-			active = NM_ACTIVE_CONNECTION (_nm_object_cache_get (path));
-			if (!active) {
-				connection = nm_object_get_connection (object);
-				active = NM_ACTIVE_CONNECTION (nm_active_connection_new (connection, path));
-			}
-		}
-	}
-
-	if (priv->active_connection) {
-		g_object_unref (priv->active_connection);
-		priv->active_connection = NULL;
-	}
-
-	if (active)
-		priv->active_connection = active;
-
-	_nm_object_queue_notify (object, NM_DEVICE_ACTIVE_CONNECTION);
+	_nm_object_queue_notify (object, NM_DEVICE_STATE_REASON);
 	return TRUE;
 }
 
 static void
-register_for_property_changed (NMDevice *device)
+register_properties (NMDevice *device)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
-	const NMPropertiesChangedInfo property_changed_info[] = {
-		{ NM_DEVICE_UDI,              _nm_object_demarshal_generic, &priv->udi },
-		{ NM_DEVICE_INTERFACE,        _nm_object_demarshal_generic, &priv->iface },
-		{ NM_DEVICE_IP_INTERFACE,     _nm_object_demarshal_generic, &priv->ip_iface },
-		{ NM_DEVICE_DRIVER,           _nm_object_demarshal_generic, &priv->driver },
-		{ NM_DEVICE_CAPABILITIES,     _nm_object_demarshal_generic, &priv->capabilities },
-		{ NM_DEVICE_MANAGED,          _nm_object_demarshal_generic, &priv->managed },
-		{ NM_DEVICE_FIRMWARE_MISSING, _nm_object_demarshal_generic, &priv->firmware_missing },
-		{ NM_DEVICE_IP4_CONFIG,       demarshal_ip4_config,         &priv->ip4_config },
-		{ NM_DEVICE_DHCP4_CONFIG,     demarshal_dhcp4_config,       &priv->dhcp4_config },
-		{ NM_DEVICE_IP6_CONFIG,       demarshal_ip6_config,         &priv->ip6_config },
-		{ NM_DEVICE_DHCP6_CONFIG,     demarshal_dhcp6_config,       &priv->dhcp6_config },
-		{ NM_DEVICE_ACTIVE_CONNECTION,demarshal_active_connection,  &priv->active_connection },
+	const NMPropertiesInfo property_info[] = {
+		{ NM_DEVICE_UDI,               &priv->udi },
+		{ NM_DEVICE_INTERFACE,         &priv->iface },
+		{ NM_DEVICE_IP_INTERFACE,      &priv->ip_iface },
+		{ NM_DEVICE_DRIVER,            &priv->driver },
+		{ NM_DEVICE_CAPABILITIES,      &priv->capabilities },
+		{ NM_DEVICE_MANAGED,           &priv->managed },
+		{ NM_DEVICE_FIRMWARE_MISSING,  &priv->firmware_missing },
+		{ NM_DEVICE_IP4_CONFIG,        &priv->ip4_config, NULL, NM_TYPE_IP4_CONFIG },
+		{ NM_DEVICE_DHCP4_CONFIG,      &priv->dhcp4_config, NULL, NM_TYPE_DHCP4_CONFIG },
+		{ NM_DEVICE_IP6_CONFIG,        &priv->ip6_config, NULL, NM_TYPE_IP6_CONFIG },
+		{ NM_DEVICE_DHCP6_CONFIG,      &priv->dhcp6_config, NULL, NM_TYPE_DHCP6_CONFIG },
+		{ NM_DEVICE_STATE,             &priv->state },
+		{ NM_DEVICE_STATE_REASON,      &priv->state, demarshal_state_reason },
+		{ NM_DEVICE_ACTIVE_CONNECTION, &priv->active_connection, NULL, NM_TYPE_ACTIVE_CONNECTION },
+
+		/* Properties that exist in D-Bus but that we don't track */
+		{ "ip4-address", NULL },
+		{ "device-type", NULL },
+
 		{ NULL },
 	};
 
-	_nm_object_handle_properties_changed (NM_OBJECT (device),
-	                                     priv->proxy,
-	                                     property_changed_info);
+	_nm_object_register_properties (NM_OBJECT (device),
+	                                priv->proxy,
+	                                property_info);
 }
 
 static void
@@ -325,39 +183,68 @@ device_state_changed (DBusGProxy *proxy,
                       gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
-	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	if (priv->state != new_state) {
-		priv->state = new_state;
+	if (old_state != new_state) {
+		/* Update state here since the PropertyChanged signal for state
+		 * might come in a bit later, but a client might ask for the
+		 * state via nm_device_get_state() as a result of this signal.
+		 * When the PC signal does come in that will trigger the glib
+		 * property notify signal so we don't need to do that here.
+		 */
+		NM_DEVICE_GET_PRIVATE (self)->state = new_state;
 		g_signal_emit (self, signals[STATE_CHANGED], 0, new_state, old_state, reason);
-		_nm_object_queue_notify (NM_OBJECT (self), "state");
 	}
 }
 
-static GObject*
-constructor (GType type,
-			 guint n_construct_params,
-			 GObjectConstructParam *construct_params)
+static GType
+_nm_device_gtype_from_dtype (NMDeviceType dtype)
+{
+	switch (dtype) {
+	case NM_DEVICE_TYPE_ETHERNET:
+		return NM_TYPE_DEVICE_ETHERNET;
+	case NM_DEVICE_TYPE_WIFI:
+		return NM_TYPE_DEVICE_WIFI;
+	case NM_DEVICE_TYPE_MODEM:
+		return NM_TYPE_DEVICE_MODEM;
+	case NM_DEVICE_TYPE_BT:
+		return NM_TYPE_DEVICE_BT;
+	case NM_DEVICE_TYPE_OLPC_MESH:
+		return NM_TYPE_DEVICE_OLPC_MESH;
+	case NM_DEVICE_TYPE_WIMAX:
+		return NM_TYPE_DEVICE_WIMAX;
+	case NM_DEVICE_TYPE_INFINIBAND:
+		return NM_TYPE_DEVICE_INFINIBAND;
+	case NM_DEVICE_TYPE_BOND:
+		return NM_TYPE_DEVICE_BOND;
+	case NM_DEVICE_TYPE_VLAN:
+		return NM_TYPE_DEVICE_VLAN;
+	default:
+		g_warning ("Unknown device type %d", dtype);
+		return G_TYPE_INVALID;
+	}
+}
+
+static void
+constructed (GObject *object)
 {
-	NMObject *object;
 	NMDevicePrivate *priv;
 
-	object = (NMObject *) G_OBJECT_CLASS (nm_device_parent_class)->constructor (type,
-																				n_construct_params,
-																				construct_params);
-	if (!object)
-		return NULL;
+	G_OBJECT_CLASS (nm_device_parent_class)->constructed (object);
 
 	priv = NM_DEVICE_GET_PRIVATE (object);
+	/* Catch failure of subclasses to call _nm_device_set_device_type() */
+	g_warn_if_fail (priv->device_type != NM_DEVICE_TYPE_UNKNOWN);
+	/* Catch a subclass setting the wrong type */
+	g_warn_if_fail (G_OBJECT_TYPE (object) == _nm_device_gtype_from_dtype (priv->device_type));
 
-	priv->proxy = dbus_g_proxy_new_for_name (nm_object_get_connection (object),
+	priv->proxy = dbus_g_proxy_new_for_name (nm_object_get_connection (NM_OBJECT (object)),
 											 NM_DBUS_SERVICE,
-											 nm_object_get_path (object),
+											 nm_object_get_path (NM_OBJECT (object)),
 											 NM_DBUS_INTERFACE_DEVICE);
 
-	register_for_property_changed (NM_DEVICE (object));
+	register_properties (NM_DEVICE (object));
 
-	dbus_g_object_register_marshaller (_nm_marshal_VOID__UINT_UINT_UINT,
+	dbus_g_object_register_marshaller (_nm_glib_marshal_VOID__UINT_UINT_UINT,
 									   G_TYPE_NONE,
 									   G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT,
 									   G_TYPE_INVALID);
@@ -371,8 +258,6 @@ constructor (GType type,
 								 G_CALLBACK (device_state_changed),
 								 NM_DEVICE (object),
 								 NULL);
-
-	return G_OBJECT (object);
 }
 
 static void
@@ -426,6 +311,7 @@ get_property (GObject *object,
               GParamSpec *pspec)
 {
 	NMDevice *device = NM_DEVICE (object);
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
 
 	switch (prop_id) {
 	case PROP_DEVICE_TYPE:
@@ -467,6 +353,14 @@ get_property (GObject *object,
 	case PROP_STATE:
 		g_value_set_uint (value, nm_device_get_state (device));
 		break;
+	case PROP_STATE_REASON:
+		g_value_set_boxed (value,
+		                   dbus_g_type_specialized_construct (DBUS_G_TYPE_UINT_STRUCT));
+		dbus_g_type_struct_set (value,
+		                        0, priv->state,
+		                        1, priv->reason,
+		                        G_MAXUINT);
+		break;
 	case PROP_ACTIVE_CONNECTION:
 		g_value_set_object (value, nm_device_get_active_connection (device));
 		break;
@@ -510,7 +404,7 @@ nm_device_class_init (NMDeviceClass *device_class)
 	g_type_class_add_private (device_class, sizeof (NMDevicePrivate));
 
 	/* virtual methods */
-	object_class->constructor = constructor;
+	object_class->constructed = constructed;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 	object_class->dispose = dispose;
@@ -556,7 +450,7 @@ nm_device_class_init (NMDeviceClass *device_class)
 						  "Device Type",
 						  "Numeric device type (ie ethernet, wifi, etc)",
 						  NM_DEVICE_TYPE_UNKNOWN, G_MAXUINT32, NM_DEVICE_TYPE_UNKNOWN,
-						  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+						  G_PARAM_READABLE));
 	/**
 	 * NMDevice:udi:
 	 *
@@ -694,6 +588,19 @@ nm_device_class_init (NMDeviceClass *device_class)
 						  G_PARAM_READABLE));
 
 	/**
+	 * NMDevice:state-reason:
+	 *
+	 * The state and reason of the device.
+	 **/
+	g_object_class_install_property
+		(object_class, PROP_STATE_REASON,
+		 g_param_spec_boxed (NM_DEVICE_STATE_REASON,
+		                     "StateReason",
+		                     "StateReason",
+		                     DBUS_G_TYPE_UINT_STRUCT,
+		                     G_PARAM_READABLE));
+
+	/**
 	 * NMDevice:active-connection:
 	 *
 	 * The #NMActiveConnection object that "owns" this device during activation.
@@ -747,40 +654,50 @@ nm_device_class_init (NMDeviceClass *device_class)
 				    G_SIGNAL_RUN_FIRST,
 				    G_STRUCT_OFFSET (NMDeviceClass, state_changed),
 				    NULL, NULL,
-				    _nm_marshal_VOID__UINT_UINT_UINT,
+				    _nm_glib_marshal_VOID__UINT_UINT_UINT,
 				    G_TYPE_NONE, 3,
 				    G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
 }
 
 /**
- * nm_device_new:
- * @connection: the #DBusGConnection
- * @path: the DBus object path of the device
- *
- * Creates a new #NMDevice.
+ * _nm_device_set_device_type:
+ * @device: the device
+ * @dtype: the NM device type
  *
- * Returns: (transfer full): a new device
+ * Sets the NM device type if it wasn't set during construction.  INTERNAL
+ * ONLY METHOD.
  **/
-GObject *
-nm_device_new (DBusGConnection *connection, const char *path)
+void
+_nm_device_set_device_type (NMDevice *device, NMDeviceType dtype)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (device != NULL);
+	g_return_if_fail (dtype != NM_DEVICE_TYPE_UNKNOWN);
+
+	priv = NM_DEVICE_GET_PRIVATE (device);
+	if (priv->device_type == NM_DEVICE_TYPE_UNKNOWN)
+		priv->device_type = dtype;
+	else
+		g_warn_if_fail (dtype == priv->device_type);
+}
+
+static GType
+_nm_device_type_for_path (DBusGConnection *connection,
+                          const char *path)
 {
 	DBusGProxy *proxy;
 	GError *err = NULL;
 	GValue value = {0,};
-	GType dtype = 0;
-	NMDevice *device = NULL;
 	NMDeviceType nm_dtype;
 
-	g_return_val_if_fail (connection != NULL, NULL);
-	g_return_val_if_fail (path != NULL, NULL);
-
 	proxy = dbus_g_proxy_new_for_name (connection,
 									   NM_DBUS_SERVICE,
 									   path,
 									   "org.freedesktop.DBus.Properties");
 	if (!proxy) {
 		g_warning ("%s: couldn't create D-Bus object proxy.", __func__);
-		return NULL;
+		return G_TYPE_INVALID;
 	}
 
 	if (!dbus_g_proxy_call (proxy,
@@ -789,44 +706,100 @@ nm_device_new (DBusGConnection *connection, const char *path)
 						    G_TYPE_STRING, "DeviceType",
 						    G_TYPE_INVALID,
 						    G_TYPE_VALUE, &value, G_TYPE_INVALID)) {
+		g_object_unref (proxy);
 		g_warning ("Error in get_property: %s\n", err->message);
 		g_error_free (err);
-		goto out;
 	}
+	g_object_unref (proxy);
 
 	nm_dtype = g_value_get_uint (&value);
-	switch (nm_dtype) {
-	case NM_DEVICE_TYPE_ETHERNET:
-		dtype = NM_TYPE_DEVICE_ETHERNET;
-		break;
-	case NM_DEVICE_TYPE_WIFI:
-		dtype = NM_TYPE_DEVICE_WIFI;
-		break;
-	case NM_DEVICE_TYPE_MODEM:
-		dtype = NM_TYPE_DEVICE_MODEM;
-		break;
-	case NM_DEVICE_TYPE_BT:
-		dtype = NM_TYPE_DEVICE_BT;
-		break;
-	case NM_DEVICE_TYPE_WIMAX:
-		dtype = NM_TYPE_DEVICE_WIMAX;
-		break;
-	default:
-		g_warning ("Unknown device type %d", g_value_get_uint (&value));
-		break;
-	}
+	return _nm_device_gtype_from_dtype (nm_dtype);
+}
 
-	if (dtype) {
-		device = (NMDevice *) g_object_new (dtype,
-											NM_OBJECT_DBUS_CONNECTION, connection,
-											NM_OBJECT_DBUS_PATH, path,
-											NM_DEVICE_DEVICE_TYPE, nm_dtype,
-											NULL);
+/**
+ * nm_device_new:
+ * @connection: the #DBusGConnection
+ * @path: the DBus object path of the device
+ *
+ * Creates a new #NMDevice.
+ *
+ * Returns: (transfer full): a new device
+ **/
+GObject *
+nm_device_new (DBusGConnection *connection, const char *path)
+{
+	GType dtype;
+	NMDevice *device = NULL;
+
+	g_return_val_if_fail (connection != NULL, NULL);
+	g_return_val_if_fail (path != NULL, NULL);
+
+	dtype = _nm_device_type_for_path (connection, path);
+	if (dtype == G_TYPE_INVALID)
+		return NULL;
+
+	device = (NMDevice *) g_object_new (dtype,
+	                                    NM_OBJECT_DBUS_CONNECTION, connection,
+	                                    NM_OBJECT_DBUS_PATH, path,
+	                                    NULL);
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return G_OBJECT (device);
+}
+
+typedef struct {
+	DBusGConnection *connection;
+	NMObjectTypeCallbackFunc callback;
+	gpointer user_data;
+} NMDeviceAsyncData;
+
+static void
+async_got_type (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
+{
+	NMDeviceAsyncData *async_data = user_data;
+	GValue value = G_VALUE_INIT;
+	const char *path = dbus_g_proxy_get_path (proxy);
+	GError *error = NULL;
+	GType type;
+
+	if (dbus_g_proxy_end_call (proxy, call, &error,
+	                           G_TYPE_VALUE, &value,
+	                           G_TYPE_INVALID)) {
+		NMDeviceType dtype;
+
+		dtype = g_value_get_uint (&value);
+		type = _nm_device_gtype_from_dtype (dtype);
+	} else {
+		g_warning ("%s: could not read properties for %s: %s", __func__, path, error->message);
+		g_error_free (error);
+		type = G_TYPE_INVALID;
 	}
 
-out:
+	async_data->callback (type, async_data->user_data);
 	g_object_unref (proxy);
-	return G_OBJECT (device);
+	g_slice_free (NMDeviceAsyncData, async_data);
+}
+
+static void
+_nm_device_type_for_path_async (DBusGConnection *connection,
+                                const char *path,
+                                NMObjectTypeCallbackFunc callback,
+                                gpointer user_data)
+{
+	NMDeviceAsyncData *async_data;
+	DBusGProxy *proxy;
+
+	async_data = g_slice_new (NMDeviceAsyncData);
+	async_data->connection = connection;
+	async_data->callback = callback;
+	async_data->user_data = user_data;
+
+	proxy = dbus_g_proxy_new_for_name (connection, NM_DBUS_SERVICE, path,
+	                                   "org.freedesktop.DBus.Properties");
+	dbus_g_proxy_begin_call (proxy, "Get",
+	                         async_got_type, async_data, NULL,
+	                         G_TYPE_STRING, NM_DBUS_INTERFACE_DEVICE,
+	                         G_TYPE_STRING, "DeviceType",
+	                         G_TYPE_INVALID);
 }
 
 /**
@@ -841,19 +814,10 @@ out:
 const char *
 nm_device_get_iface (NMDevice *device)
 {
-	NMDevicePrivate *priv;
-
 	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (!priv->iface) {
-		priv->iface = _nm_object_get_string_property (NM_OBJECT (device),
-		                                             NM_DBUS_INTERFACE_DEVICE,
-		                                             "Interface",
-		                                             NULL);
-	}
-
-	return priv->iface;
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return NM_DEVICE_GET_PRIVATE (device)->iface;
 }
 
 /**
@@ -869,19 +833,10 @@ nm_device_get_iface (NMDevice *device)
 const char *
 nm_device_get_ip_iface (NMDevice *device)
 {
-	NMDevicePrivate *priv;
-
 	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (!priv->ip_iface) {
-		priv->ip_iface = _nm_object_get_string_property (NM_OBJECT (device),
-		                                                 NM_DBUS_INTERFACE_DEVICE,
-		                                                 "IpInterface",
-		                                                 NULL);
-	}
-
-	return priv->ip_iface;
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return NM_DEVICE_GET_PRIVATE (device)->ip_iface;
 }
 
 /**
@@ -913,19 +868,10 @@ nm_device_get_device_type (NMDevice *self)
 const char *
 nm_device_get_udi (NMDevice *device)
 {
-	NMDevicePrivate *priv;
-
 	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (!priv->udi) {
-		priv->udi = _nm_object_get_string_property (NM_OBJECT (device),
-		                                           NM_DBUS_INTERFACE_DEVICE,
-		                                           "Udi",
-		                                           NULL);
-	}
-
-	return priv->udi;
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return NM_DEVICE_GET_PRIVATE (device)->udi;
 }
 
 /**
@@ -940,19 +886,10 @@ nm_device_get_udi (NMDevice *device)
 const char *
 nm_device_get_driver (NMDevice *device)
 {
-	NMDevicePrivate *priv;
-
 	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (!priv->driver) {
-		priv->driver = _nm_object_get_string_property (NM_OBJECT (device),
-		                                              NM_DBUS_INTERFACE_DEVICE,
-		                                              "Driver",
-		                                              NULL);
-	}
-
-	return priv->driver;
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return NM_DEVICE_GET_PRIVATE (device)->driver;
 }
 
 /**
@@ -966,19 +903,10 @@ nm_device_get_driver (NMDevice *device)
 NMDeviceCapabilities
 nm_device_get_capabilities (NMDevice *device)
 {
-	NMDevicePrivate *priv;
-
 	g_return_val_if_fail (NM_IS_DEVICE (device), 0);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (!priv->capabilities) {
-		priv->capabilities = _nm_object_get_uint_property (NM_OBJECT (device),
-		                                                  NM_DBUS_INTERFACE_DEVICE,
-		                                                  "Capabilities",
-		                                                  NULL);
-	}
-
-	return priv->capabilities;
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return NM_DEVICE_GET_PRIVATE (device)->capabilities;
 }
 
 /**
@@ -992,19 +920,10 @@ nm_device_get_capabilities (NMDevice *device)
 gboolean
 nm_device_get_managed (NMDevice *device)
 {
-	NMDevicePrivate *priv;
-
 	g_return_val_if_fail (NM_IS_DEVICE (device), 0);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (!priv->managed) {
-		priv->managed = _nm_object_get_boolean_property (NM_OBJECT (device),
-		                                                NM_DBUS_INTERFACE_DEVICE,
-		                                                "Managed",
-		                                                NULL);
-	}
-
-	return priv->managed;
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return NM_DEVICE_GET_PRIVATE (device)->managed;
 }
 
 /**
@@ -1020,19 +939,10 @@ nm_device_get_managed (NMDevice *device)
 gboolean
 nm_device_get_firmware_missing (NMDevice *device)
 {
-	NMDevicePrivate *priv;
-
 	g_return_val_if_fail (NM_IS_DEVICE (device), 0);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (!priv->firmware_missing) {
-		priv->firmware_missing = _nm_object_get_boolean_property (NM_OBJECT (device),
-		                                                          NM_DBUS_INTERFACE_DEVICE,
-		                                                          "FirmwareMissing",
-		                                                          NULL);
-	}
-
-	return priv->firmware_missing;
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return NM_DEVICE_GET_PRIVATE (device)->firmware_missing;
 }
 
 /**
@@ -1046,30 +956,10 @@ nm_device_get_firmware_missing (NMDevice *device)
 NMIP4Config *
 nm_device_get_ip4_config (NMDevice *device)
 {
-	NMDevicePrivate *priv;
-	char *path;
-	GValue value = { 0, };
-	GError *error = NULL;
-
 	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (priv->got_ip4_config == TRUE)
-		return priv->ip4_config;
-
-	path = _nm_object_get_object_path_property (NM_OBJECT (device),
-	                                            NM_DBUS_INTERFACE_DEVICE,
-	                                            "Ip4Config",
-	                                            &error);
-	if (error == NULL) {
-		g_value_init (&value, DBUS_TYPE_G_OBJECT_PATH);
-		g_value_take_boxed (&value, path);
-		demarshal_ip4_config (NM_OBJECT (device), NULL, &value, &priv->ip4_config);
-		g_value_unset (&value);
-	}
-	g_clear_error (&error);
-
-	return priv->ip4_config;
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return NM_DEVICE_GET_PRIVATE (device)->ip4_config;
 }
 
 /**
@@ -1084,30 +974,10 @@ nm_device_get_ip4_config (NMDevice *device)
 NMDHCP4Config *
 nm_device_get_dhcp4_config (NMDevice *device)
 {
-	NMDevicePrivate *priv;
-	char *path;
-	GValue value = { 0, };
-	GError *error = NULL;
-
 	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (priv->got_dhcp4_config == TRUE)
-		return priv->dhcp4_config;
-
-	path = _nm_object_get_object_path_property (NM_OBJECT (device),
-	                                            NM_DBUS_INTERFACE_DEVICE,
-	                                            "Dhcp4Config",
-	                                            &error);
-	if (error == NULL) {
-		g_value_init (&value, DBUS_TYPE_G_OBJECT_PATH);
-		g_value_take_boxed (&value, path);
-		demarshal_dhcp4_config (NM_OBJECT (device), NULL, &value, &priv->dhcp4_config);
-		g_value_unset (&value);
-	}
-	g_clear_error (&error);
-
-	return priv->dhcp4_config;
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return NM_DEVICE_GET_PRIVATE (device)->dhcp4_config;
 }
 
 /**
@@ -1121,30 +991,10 @@ nm_device_get_dhcp4_config (NMDevice *device)
 NMIP6Config *
 nm_device_get_ip6_config (NMDevice *device)
 {
-	NMDevicePrivate *priv;
-	char *path;
-	GValue value = { 0, };
-	GError *error = NULL;
-
 	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (priv->got_ip6_config == TRUE)
-		return priv->ip6_config;
-
-	path = _nm_object_get_object_path_property (NM_OBJECT (device),
-	                                            NM_DBUS_INTERFACE_DEVICE,
-	                                            "Ip6Config",
-	                                            &error);
-	if (error == NULL) {
-		g_value_init (&value, DBUS_TYPE_G_OBJECT_PATH);
-		g_value_take_boxed (&value, path);
-		demarshal_ip6_config (NM_OBJECT (device), NULL, &value, &priv->ip6_config);
-		g_value_unset (&value);
-	}
-	g_clear_error (&error);
-
-	return priv->ip6_config;
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return NM_DEVICE_GET_PRIVATE (device)->ip6_config;
 }
 
 /**
@@ -1159,30 +1009,10 @@ nm_device_get_ip6_config (NMDevice *device)
 NMDHCP6Config *
 nm_device_get_dhcp6_config (NMDevice *device)
 {
-	NMDevicePrivate *priv;
-	char *path;
-	GValue value = { 0, };
-	GError *error = NULL;
-
 	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (priv->got_dhcp6_config == TRUE)
-		return priv->dhcp6_config;
-
-	path = _nm_object_get_object_path_property (NM_OBJECT (device),
-	                                            NM_DBUS_INTERFACE_DEVICE,
-	                                            "Dhcp6Config",
-	                                            &error);
-	if (error == NULL) {
-		g_value_init (&value, DBUS_TYPE_G_OBJECT_PATH);
-		g_value_take_boxed (&value, path);
-		demarshal_dhcp6_config (NM_OBJECT (device), NULL, &value, &priv->dhcp6_config);
-		g_value_unset (&value);
-	}
-	g_clear_error (&error);
-
-	return priv->dhcp6_config;
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return NM_DEVICE_GET_PRIVATE (device)->dhcp6_config;
 }
 
 /**
@@ -1196,19 +1026,31 @@ nm_device_get_dhcp6_config (NMDevice *device)
 NMDeviceState
 nm_device_get_state (NMDevice *device)
 {
-	NMDevicePrivate *priv;
-
 	g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (priv->state == NM_DEVICE_STATE_UNKNOWN) {
-		priv->state = _nm_object_get_uint_property (NM_OBJECT (device), 
-		                                           NM_DBUS_INTERFACE_DEVICE,
-		                                           "State",
-		                                           NULL);
-	}
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return NM_DEVICE_GET_PRIVATE (device)->state;
+}
 
-	return priv->state;
+/**
+ * nm_device_get_state_reason:
+ * @device: a #NMDevice
+ * @reason: (out) (allow-none): location to store reason (#NMDeviceStateReason), or NULL
+ *
+ * Gets the current #NMDevice state (return value) and the reason for entering
+ * the state (@reason argument).
+ *
+ * Returns: the current device state
+ **/
+NMDeviceState
+nm_device_get_state_reason (NMDevice *device, NMDeviceStateReason *reason)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN);
+
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	if (reason)
+		*reason = NM_DEVICE_GET_PRIVATE (device)->reason;
+	return NM_DEVICE_GET_PRIVATE (device)->state;
 }
 
 /**
@@ -1223,30 +1065,10 @@ nm_device_get_state (NMDevice *device)
 NMActiveConnection *
 nm_device_get_active_connection (NMDevice *device)
 {
-	NMDevicePrivate *priv;
-	char *path;
-	GValue value = { 0, };
-	GError *error = NULL;
-
 	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
 
-	priv = NM_DEVICE_GET_PRIVATE (device);
-	if (priv->got_active_connection == TRUE)
-		return priv->active_connection;
-
-	path = _nm_object_get_object_path_property (NM_OBJECT (device),
-	                                            NM_DBUS_INTERFACE_DEVICE,
-	                                            "ActiveConnection",
-	                                            &error);
-	if (error == NULL) {
-		g_value_init (&value, DBUS_TYPE_G_OBJECT_PATH);
-		g_value_take_boxed (&value, path);
-		demarshal_active_connection (NM_OBJECT (device), NULL, &value, &priv->active_connection);
-		g_value_unset (&value);
-	}
-	g_clear_error (&error);
-
-	return priv->active_connection;
+	_nm_object_ensure_inited (NM_OBJECT (device));
+	return NM_DEVICE_GET_PRIVATE (device)->active_connection;
 }
 
 /* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */
@@ -1453,11 +1275,14 @@ typedef struct {
 
 static void
 deactivate_cb (DBusGProxy *proxy,
-               GError *error,
+               DBusGProxyCall *call,
                gpointer user_data)
 {
 	DeactivateInfo *info = user_data;
+	GError *error = NULL;
 
+	dbus_g_proxy_end_call (proxy, call, &error,
+	                       G_TYPE_INVALID);
 	if (info->fn)
 		info->fn (info->device, error, info->user_data);
 	else if (error) {
@@ -1467,6 +1292,7 @@ deactivate_cb (DBusGProxy *proxy,
 		           error ? error->code : -1,
 		           error && error->message ? error->message : "(unknown)");
 	}
+	g_clear_error (&error);
 
 	g_object_unref (info->device);
 	g_slice_free (DeactivateInfo, info);
@@ -1497,9 +1323,9 @@ nm_device_disconnect (NMDevice *device,
 	info->user_data = user_data;
 	info->device = g_object_ref (device);
 
-	org_freedesktop_NetworkManager_Device_disconnect_async (NM_DEVICE_GET_PRIVATE (device)->proxy,
-	                                                        deactivate_cb,
-	                                                        info);
+	dbus_g_proxy_begin_call (NM_DEVICE_GET_PRIVATE (device)->proxy, "Disconnect",
+	                         deactivate_cb, info, NULL,
+	                         G_TYPE_INVALID);
 }
 
 /**
@@ -1521,8 +1347,35 @@ nm_device_disconnect (NMDevice *device,
 gboolean
 nm_device_connection_valid (NMDevice *device, NMConnection *connection)
 {
-	if (NM_DEVICE_GET_CLASS (device)->connection_valid)
-		return NM_DEVICE_GET_CLASS (device)->connection_valid (device, connection);
+	return nm_device_connection_compatible (device, connection, NULL);
+}
+
+/**
+ * nm_device_connection_compatible:
+ * @device: an #NMDevice to validate @connection against
+ * @connection: an #NMConnection to validate against @device
+ * @error: return location for a #GError, or %NULL
+ *
+ * Validates a given connection for a given #NMDevice object and returns
+ * whether the connection may be activated with the device. For example if
+ * @device is a WiFi device that supports only WEP encryption, the connection
+ * will only be valid if it is a WiFi connection which describes a WEP or open
+ * network, and will not be valid if it describes a WPA network, or if it is
+ * an Ethernet, Bluetooth, WWAN, etc connection that is incompatible with the
+ * device.
+ *
+ * This function does the same as nm_device_connection_valid(), i.e. checking
+ * compatibility of the given device and connection. But, in addition, it sets
+ * GError when FALSE is returned.
+ *
+ * Returns: %TRUE if the connection may be activated with this device, %FALSE
+ * if is incompatible with the device's capabilities and characteristics.
+ **/
+gboolean
+nm_device_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+	if (NM_DEVICE_GET_CLASS (device)->connection_compatible)
+		return NM_DEVICE_GET_CLASS (device)->connection_compatible (device, connection, error);
 	return FALSE;
 }
 
@@ -1537,7 +1390,8 @@ nm_device_connection_valid (NMDevice *device, NMConnection *connection)
  * contain any WiFi connections in @connections that allow connection to
  * unencrypted or WEP-enabled SSIDs.  The returned list will not contain
  * Ethernet, Bluetooth, WiFi WPA connections, or any other connection that is
- * incompatible with the device.
+ * incompatible with the device. To get the full list of connections see
+ * nm_remote_settings_list_connections().
  *
  * Returns: (transfer container) (element-type NetworkManager.Connection): a
  * list of #NMConnection objects that could be activated with the given @device.