summary refs log tree commit diff
path: root/src/devices/wwan/nm-modem.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2015-05-05 17:48:57 +0200
committerMichael Biebl <biebl@debian.org>2015-05-05 17:48:57 +0200
commitf408e27bccfacf347605a8d98649975a68f38a17 (patch)
tree654fd6695c31511baf919b1c0870d119a352ed75 /src/devices/wwan/nm-modem.c
parent2c032d8f1c6292c1338a615e6ec40252889ba85c (diff)
Imported Upstream version 1.0.2 upstream/1.0.2
Diffstat (limited to 'src/devices/wwan/nm-modem.c')
-rw-r--r--src/devices/wwan/nm-modem.c310
1 files changed, 260 insertions, 50 deletions
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index ba1db8a4..69eaddf3 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -175,23 +175,23 @@ nm_modem_set_mm_enabled (NMModem *self,
 	NMModemState prev_state = priv->state;
 
 	if (enabled && priv->state >= NM_MODEM_STATE_ENABLING) {
-		nm_log_dbg (LOGD_MB, "(%s) cannot enable modem: already enabled",
+		nm_log_dbg (LOGD_MB, "(%s): cannot enable modem: already enabled",
 		            nm_modem_get_uid (self));
 		return;
 	}
 	if (!enabled && priv->state <= NM_MODEM_STATE_DISABLING) {
-		nm_log_dbg (LOGD_MB, "(%s) cannot disable modem: already disabled",
+		nm_log_dbg (LOGD_MB, "(%s): cannot disable modem: already disabled",
 		            nm_modem_get_uid (self));
 		return;
 	}
 
 	if (priv->state <= NM_MODEM_STATE_INITIALIZING) {
-		nm_log_dbg (LOGD_MB, "(%s) cannot enable/disable modem: initializing or failed",
+		nm_log_dbg (LOGD_MB, "(%s): cannot enable/disable modem: initializing or failed",
 		            nm_modem_get_uid (self));
 		return;
 	} else if (priv->state == NM_MODEM_STATE_LOCKED) {
 		/* Don't try to enable if the modem is locked since that will fail */
-		nm_log_warn (LOGD_MB, "(%s) cannot enable/disable modem: locked",
+		nm_log_warn (LOGD_MB, "(%s): cannot enable/disable modem: locked",
 		             nm_modem_get_uid (self));
 
 		/* Try to unlock the modem if it's being enabled */
@@ -221,17 +221,39 @@ nm_modem_get_supported_ip_types (NMModem *self)
 	return NM_MODEM_GET_PRIVATE (self)->ip_types;
 }
 
+const gchar *
+nm_modem_ip_type_to_string (NMModemIPType ip_type)
+{
+	switch (ip_type) {
+	case NM_MODEM_IP_TYPE_IPV4:
+		return "ipv4";
+	case NM_MODEM_IP_TYPE_IPV6:
+		return "ipv6";
+	case NM_MODEM_IP_TYPE_IPV4V6:
+		return "ipv4v6";
+	default:
+		g_return_val_if_reached ("unknown");
+	}
+}
+
+static GArray *
+build_single_ip_type_array (NMModemIPType type)
+{
+	return g_array_append_val (g_array_sized_new (FALSE, FALSE, sizeof (NMModemIPType), 1), type);
+}
+
 /**
  * nm_modem_get_connection_ip_type:
  * @self: the #NMModem
  * @connection: the #NMConnection to determine IP type to use
  *
- * Given a modem and a connection, determine which NMModemIpType to use
+ * Given a modem and a connection, determine which #NMModemIPTypes to use
  * when connecting.
  *
- * Returns: a single %NMModemIpType value
+ * Returns: an array of #NMModemIpType values, in the order in which they
+ * should be tried.
  */
-NMModemIPType
+GArray *
 nm_modem_get_connection_ip_type (NMModem *self,
                                  NMConnection *connection,
                                  GError **error)
@@ -265,9 +287,9 @@ nm_modem_get_connection_ip_type (NMModem *self,
 			                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
 			                     "Connection requested IPv4 but IPv4 is "
 			                     "unsuported by the modem.");
-			return NM_MODEM_IP_TYPE_UNKNOWN;
+			return NULL;
 		}
-		return NM_MODEM_IP_TYPE_IPV4;
+		return build_single_ip_type_array (NM_MODEM_IP_TYPE_IPV4);
 	}
 
 	if (ip6 && !ip4) {
@@ -277,38 +299,54 @@ nm_modem_get_connection_ip_type (NMModem *self,
 			                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
 			                     "Connection requested IPv6 but IPv6 is "
 			                     "unsuported by the modem.");
-			return NM_MODEM_IP_TYPE_UNKNOWN;
+			return NULL;
 		}
-		return NM_MODEM_IP_TYPE_IPV6;
+		return build_single_ip_type_array (NM_MODEM_IP_TYPE_IPV6);
 	}
 
 	if (ip4 && ip6) {
-		/* Modem supports dual-stack */
-		if (priv->ip_types & NM_MODEM_IP_TYPE_IPV4V6)
-			return NM_MODEM_IP_TYPE_IPV4V6;
+		NMModemIPType type;
+		GArray *out;
 
-		/* Both IPv4 and IPv6 requested, but modem doesn't support dual-stack;
-		 * if one method is marked "may-fail" then use the other.
-		 */
-		if (ip6_may_fail)
-			return NM_MODEM_IP_TYPE_IPV4;
-		else if (ip4_may_fail)
-			return NM_MODEM_IP_TYPE_IPV6;
+		out = g_array_sized_new (FALSE, FALSE, sizeof (NMModemIPType), 3);
 
+		/* Modem supports dual-stack? */
+		if (priv->ip_types & NM_MODEM_IP_TYPE_IPV4V6) {
+			type = NM_MODEM_IP_TYPE_IPV4V6;
+			g_array_append_val (out, type);
+		}
+
+		/* If IPv6 may-fail=false, we should NOT try IPv4 as fallback */
+		if ((priv->ip_types & NM_MODEM_IP_TYPE_IPV4) && ip6_may_fail) {
+			type = NM_MODEM_IP_TYPE_IPV4;
+			g_array_append_val (out, type);
+		}
+
+		/* If IPv4 may-fail=false, we should NOT try IPv6 as fallback */
+		if ((priv->ip_types & NM_MODEM_IP_TYPE_IPV6) && ip4_may_fail) {
+			type = NM_MODEM_IP_TYPE_IPV6;
+			g_array_append_val (out, type);
+		}
+
+		if (out->len > 0)
+			return out;
+
+		/* Error... */
+		g_array_unref (out);
 		g_set_error_literal (error,
 		                     NM_DEVICE_ERROR,
 		                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
 		                     "Connection requested both IPv4 and IPv6 "
 		                     "but dual-stack addressing is unsupported "
 		                     "by the modem.");
-		return NM_MODEM_IP_TYPE_UNKNOWN;
+		return NULL;
 	}
 
 	g_set_error_literal (error,
 	                     NM_DEVICE_ERROR,
 	                     NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
 	                     "Connection specified no IP configuration!");
-	return NM_MODEM_IP_TYPE_UNKNOWN;
+	return NULL;
 }
 
 /*****************************************************************************/
@@ -343,9 +381,9 @@ set_data_port (NMModem *self, const char *new_data_port)
 
 static void
 ppp_ip4_config (NMPPPManager *ppp_manager,
-				const char *iface,
-				NMIP4Config *config,
-				gpointer user_data)
+                const char *iface,
+                NMIP4Config *config,
+                gpointer user_data)
 {
 	NMModem *self = NM_MODEM (user_data);
 	guint32 i, num;
@@ -416,9 +454,9 @@ ppp_ip6_config (NMPPPManager *ppp_manager,
 
 static void
 ppp_stats (NMPPPManager *ppp_manager,
-		   guint32 in_bytes,
-		   guint32 out_bytes,
-		   gpointer user_data)
+           guint32 in_bytes,
+           guint32 out_bytes,
+           gpointer user_data)
 {
 	NMModem *self = NM_MODEM (user_data);
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
@@ -464,7 +502,8 @@ ppp_stage3_ip_config_start (NMModem *self,
 	/* Check if ModemManager requested a specific IP timeout to be used. If 0 reported,
 	 * use the default one (30s) */
 	if (priv->mm_ip_timeout > 0) {
-		nm_log_info (LOGD_PPP, "using modem-specified IP timeout: %u seconds",
+		nm_log_info (LOGD_PPP, "(%s): using modem-specified IP timeout: %u seconds",
+		             nm_modem_get_uid (self),
 		             priv->mm_ip_timeout);
 		ip_timeout = priv->mm_ip_timeout;
 	}
@@ -486,7 +525,8 @@ ppp_stage3_ip_config_start (NMModem *self,
 
 		ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else {
-		nm_log_err (LOGD_PPP, "error starting PPP: (%d) %s",
+		nm_log_err (LOGD_PPP, "(%s): error starting PPP: (%d) %s",
+		            nm_modem_get_uid (self),
 		            error ? error->code : -1,
 		            error && error->message ? error->message : "(unknown)");
 		g_error_free (error);
@@ -705,7 +745,7 @@ modem_secrets_cb (NMActRequest *req,
 	priv->secrets_id = 0;
 
 	if (error)
-		nm_log_warn (LOGD_MB, "%s", error->message);
+		nm_log_warn (LOGD_MB, "(%s): %s", nm_modem_get_uid (self), error->message);
 
 	g_signal_emit (self, signals[AUTH_RESULT], 0, error);
 }
@@ -839,13 +879,12 @@ nm_modem_complete_connection (NMModem *self,
 /*****************************************************************************/
 
 static void
-deactivate (NMModem *self, NMDevice *device)
+deactivate_cleanup (NMModem *self, NMDevice *device)
 {
 	NMModemPrivate *priv;
 	int ifindex;
 
 	g_return_if_fail (NM_IS_MODEM (self));
-	g_return_if_fail (NM_IS_DEVICE (device));
 
 	priv = NM_MODEM_GET_PRIVATE (self);
 
@@ -864,15 +903,19 @@ deactivate (NMModem *self, NMDevice *device)
 		priv->ppp_manager = NULL;
 	}
 
-	if (priv->ip4_method == NM_MODEM_IP_METHOD_STATIC ||
-	    priv->ip4_method == NM_MODEM_IP_METHOD_AUTO ||
-	    priv->ip6_method == NM_MODEM_IP_METHOD_STATIC ||
-	    priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) {
-		ifindex = nm_device_get_ip_ifindex (device);
-		if (ifindex > 0) {
-			nm_platform_route_flush (ifindex);
-			nm_platform_address_flush (ifindex);
-			nm_platform_link_set_down (ifindex);
+	if (device) {
+		g_return_if_fail (NM_IS_DEVICE (device));
+
+		if (priv->ip4_method == NM_MODEM_IP_METHOD_STATIC ||
+		    priv->ip4_method == NM_MODEM_IP_METHOD_AUTO ||
+		    priv->ip6_method == NM_MODEM_IP_METHOD_STATIC ||
+		    priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) {
+			ifindex = nm_device_get_ip_ifindex (device);
+			if (ifindex > 0) {
+				nm_platform_route_flush (ifindex);
+				nm_platform_address_flush (ifindex);
+				nm_platform_link_set_down (ifindex);
+			}
 		}
 	}
 	priv->ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
@@ -884,10 +927,176 @@ deactivate (NMModem *self, NMDevice *device)
 
 /*****************************************************************************/
 
+typedef enum {
+	DEACTIVATE_CONTEXT_STEP_FIRST,
+	DEACTIVATE_CONTEXT_STEP_CLEANUP,
+	DEACTIVATE_CONTEXT_STEP_PPP_MANAGER_STOP,
+	DEACTIVATE_CONTEXT_STEP_MM_DISCONNECT,
+	DEACTIVATE_CONTEXT_STEP_LAST
+} DeactivateContextStep;
+
+typedef struct {
+	NMModem *self;
+	NMDevice *device;
+	GCancellable *cancellable;
+	GSimpleAsyncResult *result;
+	DeactivateContextStep step;
+	NMPPPManager *ppp_manager;
+} DeactivateContext;
+
+static void
+deactivate_context_complete (DeactivateContext *ctx)
+{
+	if (ctx->ppp_manager)
+		g_object_unref (ctx->ppp_manager);
+	if (ctx->cancellable)
+		g_object_unref (ctx->cancellable);
+	g_simple_async_result_complete_in_idle (ctx->result);
+	g_object_unref (ctx->result);
+	g_object_unref (ctx->device);
+	g_object_unref (ctx->self);
+	g_slice_free (DeactivateContext, ctx);
+}
+
+gboolean
+nm_modem_deactivate_async_finish (NMModem *self,
+                                  GAsyncResult *res,
+                                  GError **error)
+{
+	return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void deactivate_step (DeactivateContext *ctx);
+
+static void
+disconnect_ready (NMModem *self,
+                  GAsyncResult *res,
+                  DeactivateContext *ctx)
+{
+	GError *error = NULL;
+
+	if (!NM_MODEM_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
+		g_simple_async_result_take_error (ctx->result, error);
+		deactivate_context_complete (ctx);
+		return;
+	}
+
+	/* Go on */
+	ctx->step++;
+	deactivate_step (ctx);
+}
+
+static void
+ppp_manager_stop_ready (NMPPPManager *ppp_manager,
+                        GAsyncResult *res,
+                        DeactivateContext *ctx)
+{
+	GError *error = NULL;
+
+	if (!nm_ppp_manager_stop_finish (ppp_manager, res, &error)) {
+		nm_log_warn (LOGD_MB, "(%s): cannot stop PPP manager: %s",
+		             nm_modem_get_uid (ctx->self),
+		             error->message);
+		g_simple_async_result_take_error (ctx->result, error);
+		deactivate_context_complete (ctx);
+		return;
+	}
+
+	/* Go on */
+	ctx->step++;
+	deactivate_step (ctx);
+}
+
+static void
+deactivate_step (DeactivateContext *ctx)
+{
+	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (ctx->self);
+	GError *error = NULL;
+
+	/* Check cancellable in each step */
+	if (g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) {
+		g_simple_async_result_take_error (ctx->result, error);
+		deactivate_context_complete (ctx);
+		return;
+	}
+
+	switch (ctx->step) {
+	case DEACTIVATE_CONTEXT_STEP_FIRST:
+		ctx->step++;
+		/* Fall down */
+
+	case DEACTIVATE_CONTEXT_STEP_CLEANUP:
+		/* Make sure we keep a ref to the PPP manager if there is one */
+		if (priv->ppp_manager)
+			ctx->ppp_manager = g_object_ref (priv->ppp_manager);
+		/* Run cleanup */
+		NM_MODEM_GET_CLASS (ctx->self)->deactivate_cleanup (ctx->self, ctx->device);
+		ctx->step++;
+		/* Fall down */
+
+	case DEACTIVATE_CONTEXT_STEP_PPP_MANAGER_STOP:
+		/* If we have a PPP manager, stop it */
+		if (ctx->ppp_manager) {
+			nm_ppp_manager_stop (ctx->ppp_manager,
+			                     ctx->cancellable,
+			                     (GAsyncReadyCallback) ppp_manager_stop_ready,
+			                     ctx);
+			return;
+		}
+		ctx->step++;
+		/* Fall down */
+
+	case DEACTIVATE_CONTEXT_STEP_MM_DISCONNECT:
+		/* Disconnect asynchronously */
+		NM_MODEM_GET_CLASS (ctx->self)->disconnect (ctx->self,
+		                                            FALSE,
+		                                            ctx->cancellable,
+		                                            (GAsyncReadyCallback) disconnect_ready,
+		                                            ctx);
+		return;
+
+	case DEACTIVATE_CONTEXT_STEP_LAST:
+		nm_log_dbg (LOGD_MB, "(%s): modem deactivation finished",
+		            nm_modem_get_uid (ctx->self));
+		deactivate_context_complete (ctx);
+		return;
+	}
+
+	g_assert_not_reached ();
+}
+
+void
+nm_modem_deactivate_async (NMModem *self,
+                           NMDevice *device,
+                           GCancellable *cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer user_data)
+{
+	DeactivateContext *ctx;
+
+	ctx = g_slice_new0 (DeactivateContext);
+	ctx->self = g_object_ref (self);
+	ctx->device = g_object_ref (device);
+	ctx->result = g_simple_async_result_new (G_OBJECT (self),
+	                                         callback,
+	                                         user_data,
+	                                         nm_modem_deactivate_async);
+	ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+
+	/* Start */
+	ctx->step = DEACTIVATE_CONTEXT_STEP_FIRST;
+	deactivate_step (ctx);
+}
+
+/*****************************************************************************/
+
 void
 nm_modem_deactivate (NMModem *self, NMDevice *device)
 {
-	NM_MODEM_GET_CLASS (self)->deactivate (self, device);
+	/* First cleanup */
+	NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, device);
+	/* Then disconnect without waiting */
+	NM_MODEM_GET_CLASS (self)->disconnect (self, FALSE, NULL, NULL, NULL);
 }
 
 /*****************************************************************************/
@@ -912,7 +1121,6 @@ nm_modem_device_state_changed (NMModem *self,
 	switch (new_state) {
 	case NM_DEVICE_STATE_UNMANAGED:
 	case NM_DEVICE_STATE_UNAVAILABLE:
-	case NM_DEVICE_STATE_DISCONNECTED:
 	case NM_DEVICE_STATE_FAILED:
 		if (priv->act_request) {
 			cancel_get_secrets (self);
@@ -924,7 +1132,9 @@ nm_modem_device_state_changed (NMModem *self,
 			/* Don't bother warning on FAILED since the modem is already gone */
 			if (new_state == NM_DEVICE_STATE_FAILED)
 				warn = FALSE;
-			NM_MODEM_GET_CLASS (self)->disconnect (self, warn);
+			/* First cleanup */
+			NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, NULL);
+			NM_MODEM_GET_CLASS (self)->disconnect (self, warn, NULL, NULL, NULL);
 		}
 		break;
 	default:
@@ -1030,8 +1240,8 @@ nm_modem_init (NMModem *self)
 
 static GObject*
 constructor (GType type,
-			 guint n_construct_params,
-			 GObjectConstructParam *construct_params)
+             guint n_construct_params,
+             GObjectConstructParam *construct_params)
 {
 	GObject *object;
 	NMModemPrivate *priv;
@@ -1056,7 +1266,7 @@ constructor (GType type,
 
 	return object;
 
- err:
+err:
 	g_object_unref (object);
 	return NULL;
 }
@@ -1209,7 +1419,7 @@ nm_modem_class_init (NMModemClass *klass)
 
 	klass->act_stage1_prepare = act_stage1_prepare;
 	klass->stage3_ip6_config_request = stage3_ip6_config_request;
-	klass->deactivate = deactivate;
+	klass->deactivate_cleanup = deactivate_cleanup;
 
 	/* Properties */