summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/devices/wwan/nm-device-modem.c5
-rw-r--r--src/devices/wwan/nm-modem-broadband.c345
2 files changed, 234 insertions, 116 deletions
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index a79bc3a56e..bb8d49c231 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -417,10 +417,7 @@ check_connection_available (NMDevice *device,
return FALSE;
if (state == NM_MODEM_STATE_LOCKED) {
- NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (connection);
-
- /* Can't use a connection without a PIN if the modem is locked */
- if (!s_gsm || !nm_setting_gsm_get_pin (s_gsm))
+ if (!nm_connection_get_setting_gsm (connection))
return FALSE;
}
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index 2e810d21e3..1b4afda717 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -34,14 +34,38 @@
G_DEFINE_TYPE (NMModemBroadband, nm_modem_broadband, NM_TYPE_MODEM)
+typedef enum {
+ CONNECT_STEP_FIRST,
+ CONNECT_STEP_WAIT_FOR_SIM,
+ CONNECT_STEP_UNLOCK,
+ CONNECT_STEP_WAIT_FOR_READY,
+ CONNECT_STEP_CONNECT,
+ CONNECT_STEP_LAST
+} ConnectStep;
+
+typedef struct {
+ NMModemBroadband *self;
+ ConnectStep step;
+
+ MMModemCapability caps;
+ NMConnection *connection;
+ GCancellable *cancellable;
+ MMSimpleConnectProperties *connect_properties;
+ GArray *ip_types;
+ guint ip_types_i;
+ GError *first_error;
+} ConnectContext;
+
struct _NMModemBroadbandPrivate {
/* The modem object from dbus */
MMObject *modem_object;
/* Per-interface objects */
MMModem *modem_iface;
MMModemSimple *simple_iface;
+ MMSim *sim_iface;
/* Connection setup */
+ ConnectContext *ctx;
MMBearer *bearer;
MMBearerIpConfig *ipv4_config;
@@ -257,45 +281,45 @@ create_gsm_connect_properties (NMConnection *connection)
return properties;
}
-typedef struct {
- NMModemBroadband *self;
- MMModemCapability caps;
- MMSimpleConnectProperties *connect_properties;
- GArray *ip_types;
- guint ip_types_i;
- GError *first_error;
-} ActStageContext;
-
static void
-act_stage_context_free (ActStageContext *ctx)
+connect_context_clear (NMModemBroadband *self)
{
- g_clear_error (&ctx->first_error);
- g_clear_pointer (&ctx->ip_types, (GDestroyNotify) g_array_unref);
- g_clear_object (&ctx->connect_properties);
- g_object_unref (ctx->self);
- g_slice_free (ActStageContext, ctx);
+ if (self->priv->ctx) {
+ ConnectContext *ctx = self->priv->ctx;
+
+ g_clear_error (&ctx->first_error);
+ g_clear_pointer (&ctx->ip_types, (GDestroyNotify) g_array_unref);
+ g_clear_object (&ctx->cancellable);
+ g_clear_object (&ctx->connection);
+ g_clear_object (&ctx->connect_properties);
+ g_clear_object (&ctx->self);
+ g_slice_free (ConnectContext, ctx);
+ self->priv->ctx = NULL;
+ }
}
-static void act_stage_context_step (ActStageContext *ctx);
+static void connect_context_step (NMModemBroadband *self);
static void
connect_ready (MMModemSimple *simple_iface,
GAsyncResult *res,
- ActStageContext *ctx)
+ NMModemBroadband *self)
{
+ ConnectContext *ctx = self->priv->ctx;
GError *error = NULL;
NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN;
NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN;
- ctx->self->priv->bearer = mm_modem_simple_connect_finish (simple_iface, res, &error);
- if (!ctx->self->priv->bearer) {
+ self->priv->bearer = mm_modem_simple_connect_finish (simple_iface, res, &error);
+ if (!self->priv->bearer) {
if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) ||
(g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) &&
- mm_modem_get_unlock_required (ctx->self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) {
- /* Request PIN */
- ask_for_pin (ctx->self);
+ mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) {
g_error_free (error);
- act_stage_context_free (ctx);
+
+ /* Request PIN */
+ ask_for_pin (self);
+ connect_context_clear (self);
return;
}
@@ -312,79 +336,190 @@ connect_ready (MMModemSimple *simple_iface,
* retry with the next one, if any.
*/
ctx->ip_types_i++;
- act_stage_context_step (ctx);
+ connect_context_clear (self);
return;
}
/* Grab IP configurations */
- ctx->self->priv->ipv4_config = mm_bearer_get_ipv4_config (ctx->self->priv->bearer);
- if (ctx->self->priv->ipv4_config)
- ip4_method = get_bearer_ip_method (ctx->self->priv->ipv4_config);
+ self->priv->ipv4_config = mm_bearer_get_ipv4_config (self->priv->bearer);
+ if (self->priv->ipv4_config)
+ ip4_method = get_bearer_ip_method (self->priv->ipv4_config);
- ctx->self->priv->ipv6_config = mm_bearer_get_ipv6_config (ctx->self->priv->bearer);
- if (ctx->self->priv->ipv6_config)
- ip6_method = get_bearer_ip_method (ctx->self->priv->ipv6_config);
+ self->priv->ipv6_config = mm_bearer_get_ipv6_config (self->priv->bearer);
+ if (self->priv->ipv6_config)
+ ip6_method = get_bearer_ip_method (self->priv->ipv6_config);
if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN &&
ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) {
nm_log_warn (LOGD_MB, "(%s): failed to connect modem: invalid bearer IP configuration",
- nm_modem_get_uid (NM_MODEM (ctx->self)));
- g_signal_emit_by_name (ctx->self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
- act_stage_context_free (ctx);
+ nm_modem_get_uid (NM_MODEM (self)));
+ g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+ connect_context_clear (self);
return;
}
- g_object_set (ctx->self,
- NM_MODEM_DATA_PORT, mm_bearer_get_interface (ctx->self->priv->bearer),
+ g_object_set (self,
+ NM_MODEM_DATA_PORT, mm_bearer_get_interface (self->priv->bearer),
NM_MODEM_IP4_METHOD, ip4_method,
NM_MODEM_IP6_METHOD, ip6_method,
- NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (ctx->self->priv->bearer),
+ NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (self->priv->bearer),
NULL);
- g_signal_emit_by_name (ctx->self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE);
- act_stage_context_free (ctx);
+ ctx->step++;
+ connect_context_step (self);
}
static void
-act_stage_context_step (ActStageContext *ctx)
+send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self)
{
- if (ctx->ip_types_i < ctx->ip_types->len) {
- NMModemIPType current;
-
- current = g_array_index (ctx->ip_types, NMModemIPType, ctx->ip_types_i);
-
- if (current == NM_MODEM_IP_TYPE_IPV4)
- mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4);
- else if (current == NM_MODEM_IP_TYPE_IPV6)
- mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV6);
- else if (current == NM_MODEM_IP_TYPE_IPV4V6)
- mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4V6);
- else
- g_assert_not_reached ();
-
- nm_log_dbg (LOGD_MB, "(%s): launching connection with ip type '%s'",
- nm_modem_get_uid (NM_MODEM (ctx->self)),
- nm_modem_ip_type_to_string (current));
-
- mm_modem_simple_connect (ctx->self->priv->simple_iface,
- ctx->connect_properties,
- NULL,
- (GAsyncReadyCallback)connect_ready,
- ctx);
+ GError *error = NULL;
+
+ if (!mm_sim_send_pin_finish (sim, result, &error)) {
+ if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) ||
+ (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) &&
+ mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) {
+ ask_for_pin (self);
+ } else {
+ g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error));
+ }
+ g_error_free (error);
return;
+ }
+
+ self->priv->ctx->step++;
+ connect_context_step (self);
+}
+
+static void
+connect_context_step (NMModemBroadband *self)
+{
+ ConnectContext *ctx = self->priv->ctx;
+
+ switch (ctx->step) {
+ case CONNECT_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case CONNECT_STEP_WAIT_FOR_SIM:
+ if (MODEM_CAPS_3GPP (ctx->caps) && !self->priv->sim_iface) {
+ /* Have to wait for the SIM to show up */
+ break;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case CONNECT_STEP_UNLOCK:
+ if ( MODEM_CAPS_3GPP (ctx->caps)
+ && mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN) {
+ NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (ctx->connection);
+ const char *pin = nm_setting_gsm_get_pin (s_gsm);
+
+ /* If we have a PIN already, send it. If we don't, get it. */
+ if (pin) {
+ mm_sim_send_pin (self->priv->sim_iface,
+ pin,
+ ctx->cancellable,
+ (GAsyncReadyCallback) send_pin_ready,
+ self);
+ } else {
+ ask_for_pin (self);
+ }
+ break;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case CONNECT_STEP_WAIT_FOR_READY: {
+ GError *error = NULL;
+
+ if (mm_modem_get_state (self->priv->modem_iface) <= MM_MODEM_STATE_LOCKED)
+ break;
+
+ /* Create core connect properties based on the modem capabilities */
+ g_assert (!ctx->connect_properties);
+
+ if (MODEM_CAPS_3GPP (ctx->caps))
+ ctx->connect_properties = create_gsm_connect_properties (ctx->connection);
+ else if (MODEM_CAPS_3GPP2 (ctx->caps))
+ ctx->connect_properties = create_cdma_connect_properties (ctx->connection);
+ else {
+ nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': not a mobile broadband modem",
+ nm_modem_get_uid (NM_MODEM (self)),
+ nm_connection_get_id (ctx->connection));
+
+ g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
+ connect_context_clear (self);
+ break;
+ }
+ g_assert (ctx->connect_properties);
+
+ /* Build up list of IP types that we need to use in the retries */
+ ctx->ip_types = nm_modem_get_connection_ip_type (NM_MODEM (self), ctx->connection, &error);
+ if (!ctx->ip_types) {
+ nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': %s",
+ nm_modem_get_uid (NM_MODEM (self)),
+ nm_connection_get_id (ctx->connection),
+ error ? error->message : "unknown error");
+ g_clear_error (&error);
+
+ g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
+ connect_context_clear (self);
+ break;
+ }
+
+ ctx->step++;
+ /* fall through */
}
- /* If we have a saved error from a previous attempt, use it */
- if (!ctx->first_error)
- ctx->first_error = g_error_new_literal (NM_DEVICE_ERROR,
- NM_DEVICE_ERROR_INVALID_CONNECTION,
- "invalid bearer IP configuration");
-
- nm_log_warn (LOGD_MB, "(%s): failed to connect modem: %s",
- nm_modem_get_uid (NM_MODEM (ctx->self)),
- ctx->first_error->message);
- g_signal_emit_by_name (ctx->self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (ctx->first_error));
- act_stage_context_free (ctx);
+ case CONNECT_STEP_CONNECT:
+ if (ctx->ip_types_i < ctx->ip_types->len) {
+ NMModemIPType current;
+
+ current = g_array_index (ctx->ip_types, NMModemIPType, ctx->ip_types_i);
+
+ if (current == NM_MODEM_IP_TYPE_IPV4)
+ mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4);
+ else if (current == NM_MODEM_IP_TYPE_IPV6)
+ mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV6);
+ else if (current == NM_MODEM_IP_TYPE_IPV4V6)
+ mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4V6);
+ else
+ g_assert_not_reached ();
+
+ nm_log_dbg (LOGD_MB, "(%s): launching connection with ip type '%s'",
+ nm_modem_get_uid (NM_MODEM (self)),
+ nm_modem_ip_type_to_string (current));
+
+ mm_modem_simple_connect (self->priv->simple_iface,
+ ctx->connect_properties,
+ NULL,
+ (GAsyncReadyCallback) connect_ready,
+ self);
+ break;
+ }
+
+ ctx->step++;
+ /* fall through */
+
+ case CONNECT_STEP_LAST:
+ if (self->priv->ipv4_config || self->priv->ipv6_config) {
+ g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE);
+ } else {
+ /* If we have a saved error from a previous attempt, use it */
+ if (!ctx->first_error)
+ ctx->first_error = g_error_new_literal (NM_DEVICE_ERROR,
+ NM_DEVICE_ERROR_INVALID_CONNECTION,
+ "invalid bearer IP configuration");
+
+ nm_log_warn (LOGD_MB, "(%s): failed to connect modem: %s",
+ nm_modem_get_uid (NM_MODEM (self)),
+ ctx->first_error->message);
+ g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (ctx->first_error));
+ }
+
+ connect_context_clear (self);
+ break;
+ }
}
static NMActStageReturn
@@ -393,8 +528,6 @@ act_stage1_prepare (NMModem *_self,
NMDeviceStateReason *reason)
{
NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
- ActStageContext *ctx;
- GError *error = NULL;
/* Make sure we can get the Simple interface from the modem */
if (!self->priv->simple_iface) {
@@ -407,41 +540,16 @@ act_stage1_prepare (NMModem *_self,
}
}
- /* Allocate new context for this activation stage attempt */
- ctx = g_slice_new0 (ActStageContext);
- ctx->self = NM_MODEM_BROADBAND (g_object_ref (self));
- ctx->caps = mm_modem_get_current_capabilities (self->priv->modem_iface);
+ connect_context_clear (self);
- /* Create core connect properties based on the modem capabilities */
- if (MODEM_CAPS_3GPP (ctx->caps))
- ctx->connect_properties = create_gsm_connect_properties (connection);
- else if (MODEM_CAPS_3GPP2 (ctx->caps))
- ctx->connect_properties = create_cdma_connect_properties (connection);
- else {
- nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': not a mobile broadband modem",
- nm_modem_get_uid (NM_MODEM (self)),
- nm_connection_get_id (connection));
- act_stage_context_free (ctx);
- *reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
- return NM_ACT_STAGE_RETURN_FAILURE;
- }
- g_assert (ctx->connect_properties);
-
- /* Checkout list of IP types that we need to use in the retries */
- ctx->ip_types = nm_modem_get_connection_ip_type (NM_MODEM (self), connection, &error);
- if (!ctx->ip_types) {
- nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': %s",
- nm_modem_get_uid (NM_MODEM (self)),
- nm_connection_get_id (connection),
- error ? error->message : "unknown error");
- g_clear_error (&error);
- act_stage_context_free (ctx);
- *reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
- return NM_ACT_STAGE_RETURN_FAILURE;
- }
+ /* Allocate new context for this connect stage attempt */
+ self->priv->ctx = g_slice_new0 (ConnectContext);
+ self->priv->ctx->caps = mm_modem_get_current_capabilities (self->priv->modem_iface);
+ self->priv->ctx->cancellable = g_cancellable_new ();
+ self->priv->ctx->connection = g_object_ref (connection);
g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (self->priv->simple_iface), MODEM_CONNECT_TIMEOUT_SECS * 1000);
- act_stage_context_step (ctx);
+ connect_context_step (self);
return NM_ACT_STAGE_RETURN_POSTPONE;
}
@@ -1042,7 +1150,6 @@ modem_state_changed (MMModem *modem,
MMModemStateChangeReason reason,
NMModemBroadband *self)
{
-
/* After the SIM is unlocked MM1 will move the device to INITIALIZING which
* is an unavailable state. That makes state handling confusing here, so
* suppress this state change and let the modem move from LOCKED to DISABLED.
@@ -1053,6 +1160,9 @@ modem_state_changed (MMModem *modem,
nm_modem_set_state (NM_MODEM (self),
mm_state_to_nm (new_state),
mm_modem_state_change_reason_get_string (reason));
+
+ if (self->priv->ctx && self->priv->ctx->step == CONNECT_STEP_WAIT_FOR_READY)
+ connect_context_step (self);
}
/*****************************************************************************/
@@ -1114,13 +1224,22 @@ get_sim_ready (MMModem *modem,
GError *error = NULL;
MMSim *new_sim;
+
new_sim = mm_modem_get_sim_finish (modem, res, &error);
- if (new_sim) {
+ if (new_sim != self->priv->sim_iface) {
+ g_clear_object (&self->priv->sim_iface);
+ self->priv->sim_iface = new_sim;
+ }
+
+ if (self->priv->sim_iface) {
g_object_set (G_OBJECT (self),
- NM_MODEM_SIM_ID, mm_sim_get_identifier (new_sim),
- NM_MODEM_SIM_OPERATOR_ID, mm_sim_get_operator_identifier (new_sim),
+ NM_MODEM_SIM_ID, mm_sim_get_identifier (self->priv->sim_iface),
+ NM_MODEM_SIM_OPERATOR_ID, mm_sim_get_operator_identifier (self->priv->sim_iface),
NULL);
- g_object_unref (new_sim);
+
+ /* If we're waiting for the SIM during a connect, proceed with the connect */
+ if (self->priv->ctx && self->priv->ctx->step == CONNECT_STEP_WAIT_FOR_SIM)
+ connect_context_step (self);
} else {
nm_log_warn (LOGD_MB, "(%s): failed to retrieve SIM object: %s",
nm_modem_get_uid (NM_MODEM (self)),
@@ -1230,11 +1349,13 @@ dispose (GObject *object)
{
NMModemBroadband *self = NM_MODEM_BROADBAND (object);
+ connect_context_clear (self);
g_clear_object (&self->priv->ipv4_config);
g_clear_object (&self->priv->ipv6_config);
g_clear_object (&self->priv->bearer);
g_clear_object (&self->priv->modem_iface);
g_clear_object (&self->priv->simple_iface);
+ g_clear_object (&self->priv->sim_iface);
g_clear_object (&self->priv->modem_object);
G_OBJECT_CLASS (nm_modem_broadband_parent_class)->dispose (object);