diff options
Diffstat (limited to 'src/devices/wwan/nm-modem.c')
| -rw-r--r-- | src/devices/wwan/nm-modem.c | 310 |
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 */ |