static void _at_command (MMBaseModem *self, const gchar *command, guint timeout, gboolean allow_cached, gboolean is_raw, GAsyncReadyCallback callback, gpointer user_data) { MMAtSerialPort *port; GError *error = NULL; /* No port given, so we'll try to guess which is best */ port = mm_base_modem_peek_best_at_port (self, &error); if (!port) { g_assert (error != NULL); g_simple_async_report_take_gerror_in_idle (G_OBJECT (self), callback, user_data, error); return; } mm_base_modem_at_command_full (self, port, command, timeout, allow_cached, is_raw, NULL, callback, user_data); }
static void authenticate_ready (MMBaseModem *modem, GAsyncResult *res, Dial3gppContext *ctx) { gchar *command; /* If cancelled, complete */ if (dial_3gpp_context_complete_and_free_if_cancelled (ctx)) return; if (!mm_base_modem_at_command_full_finish (modem, res, NULL)) { /* Try the next auth command */ ctx->auth_idx++; authenticate (ctx); return; } /* Store which auth command worked, for next attempts */ ctx->self->priv->auth_idx = ctx->auth_idx; /* Success, activate the PDP context and start the data session */ command = g_strdup_printf ("AT_OWANCALL=%d,1,1", ctx->cid); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 3, FALSE, NULL, /* cancellable */ (GAsyncReadyCallback)activate_ready, ctx); g_free (command); }
static void get_ip_config_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMAtSerialPort *primary, MMAtSerialPort *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { gchar *command; command = g_strdup_printf ("AT_OWANDATA=%d", cid); mm_base_modem_at_command_full (MM_BASE_MODEM (modem), primary, command, 3, FALSE, NULL, /* cancellable */ (GAsyncReadyCallback)ip_config_ready, get_ip_config_3gpp_context_new (MM_BROADBAND_BEARER_HSO (self), MM_BASE_MODEM (modem), primary, cid, callback, user_data)); g_free (command); }
static void load_allowed_modes (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result; MMAtSerialPort *primary; result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, load_allowed_modes); if (!mm_iface_modem_is_3gpp (self)) { /* Cannot do this in CDMA modems */ g_simple_async_result_set_error (result, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot load allowed modes in CDMA modems"); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } /* Sierra secondary ports don't have full AT command interpreters */ primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); if (!primary || mm_port_get_connected (MM_PORT (primary))) { g_simple_async_result_set_error ( result, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "Cannot load allowed modes while connected"); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } mm_base_modem_at_command_full (MM_BASE_MODEM (self), primary, "!SELRAT?", 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)selrat_query_ready, result); }
static void connect_reset (Dial3gppContext *ctx) { gchar *command; /* Need to reset the connection attempt */ command = g_strdup_printf ("AT_OWANCALL=%d,0,1", ctx->cid); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 3, FALSE, NULL, /* cancellable */ (GAsyncReadyCallback)connect_reset_ready, ctx); g_free (command); }
static void disconnect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMAtSerialPort *primary, MMAtSerialPort *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { gchar *command; DisconnectContext *ctx; g_assert (primary != NULL); ctx = g_new0 (DisconnectContext, 1); ctx->self = g_object_ref (self); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); ctx->result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, disconnect_3gpp); /* Use specific CID */ command = g_strdup_printf ("AT_OWANCALL=%d,0,0", cid); mm_base_modem_at_command_full (MM_BASE_MODEM (modem), primary, command, 3, FALSE, NULL, /* cancellable */ (GAsyncReadyCallback)disconnect_owancall_ready, ctx); g_free (command); }
static void authenticate (Dial3gppContext *ctx) { gchar *command; if (!auth_commands[ctx->auth_idx]) { g_simple_async_result_set_error (ctx->result, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't run HSO authentication"); dial_3gpp_context_complete_and_free (ctx); return; } /* Both user and password are required; otherwise firmware returns an error */ if (!ctx->self->priv->user || !ctx->self->priv->password) command = g_strdup_printf ("%s=%d,0", auth_commands[ctx->auth_idx], ctx->cid); else command = g_strdup_printf ("%s=%d,1,\"%s\",\"%s\"", auth_commands[ctx->auth_idx], ctx->cid, ctx->self->priv->password, ctx->self->priv->user); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 3, FALSE, NULL, /* cancellable */ (GAsyncReadyCallback)authenticate_ready, ctx); g_free (command); }
static void disconnect_3gpp_context_step (Disconnect3gppContext *ctx) { switch (ctx->step) { case DISCONNECT_3GPP_CONTEXT_STEP_FIRST: /* Store the context */ ctx->self->priv->disconnect_pending = ctx; /* We ignore any pending network-initiated disconnection in order to prevent it * from interfering with the client-initiated disconnection, as we would like to * proceed with the latter anyway. */ if (ctx->self->priv->network_disconnect_pending_id != 0) { g_source_remove (ctx->self->priv->network_disconnect_pending_id); ctx->self->priv->network_disconnect_pending_id = 0; } ctx->step++; /* Fall down to the next step */ case DISCONNECT_3GPP_CONTEXT_STEP_NDISDUP: mm_base_modem_at_command_full (ctx->modem, ctx->primary, "^NDISDUP=1,0", 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)disconnect_ndisdup_ready, g_object_ref (ctx->self)); return; case DISCONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY: /* If too many retries (1s of wait between the retries), failed */ if (ctx->check_count > 60) { /* Clear context */ ctx->self->priv->disconnect_pending = NULL; g_simple_async_result_set_error (ctx->result, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "Disconnection attempt timed out"); disconnect_3gpp_context_complete_and_free (ctx); return; } /* Give up if too many unexpected responses to NIDSSTATQRY are encountered. */ if (ctx->failed_ndisstatqry_count > 10) { /* Clear context */ ctx->self->priv->disconnect_pending = NULL; g_simple_async_result_set_error (ctx->result, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, "Disconnection attempt not supported."); disconnect_3gpp_context_complete_and_free (ctx); return; } /* Check if disconnected */ ctx->check_count++; mm_base_modem_at_command_full (ctx->modem, ctx->primary, "^NDISSTATQRY?", 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)disconnect_ndisstatqry_check_ready, g_object_ref (ctx->self)); return; case DISCONNECT_3GPP_CONTEXT_STEP_LAST: /* Clear context */ ctx->self->priv->disconnect_pending = NULL; /* Set data port as result */ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); disconnect_3gpp_context_complete_and_free (ctx); return; } }
static void connect_3gpp_context_step (Connect3gppContext *ctx) { /* Check for cancellation */ if (g_cancellable_is_cancelled (ctx->cancellable)) { /* Clear context */ ctx->self->priv->connect_pending = NULL; /* If we already sent the connetion command, send the disconnection one */ if (ctx->step > CONNECT_3GPP_CONTEXT_STEP_NDISDUP) mm_base_modem_at_command_full (ctx->modem, ctx->primary, "^NDISDUP=1,0", 3, FALSE, FALSE, NULL, NULL, /* Do not care the AT response */ NULL); g_simple_async_result_set_error (ctx->result, MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, "Huawei connection operation has been cancelled"); connect_3gpp_context_complete_and_free (ctx); return; } /* Network-initiated disconnect should not be outstanding at this point, * because it interferes with the connect attempt. */ g_assert (ctx->self->priv->network_disconnect_pending_id == 0); switch (ctx->step) { case CONNECT_3GPP_CONTEXT_STEP_FIRST: { MMBearerIpFamily ip_family; ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))); if (ip_family == MM_BEARER_IP_FAMILY_NONE || ip_family == MM_BEARER_IP_FAMILY_ANY) { gchar *ip_family_str; ip_family = mm_base_bearer_get_default_ip_family (MM_BASE_BEARER (ctx->self)); ip_family_str = mm_bearer_ip_family_build_string_from_mask (ip_family); mm_dbg ("No specific IP family requested, defaulting to %s", ip_family_str); g_free (ip_family_str); } if (ip_family != MM_BEARER_IP_FAMILY_IPV4) { g_simple_async_result_set_error (ctx->result, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Only IPv4 is supported by this modem"); connect_3gpp_context_complete_and_free (ctx); return; } /* Store the context */ ctx->self->priv->connect_pending = ctx; ctx->step++; /* Fall down to the next step */ } case CONNECT_3GPP_CONTEXT_STEP_NDISDUP: { const gchar *apn; const gchar *user; const gchar *passwd; MMBearerAllowedAuth auth; gint encoded_auth = MM_BEARER_HUAWEI_AUTH_UNKNOWN; gchar *command; apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))); user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))); passwd = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))); auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))); encoded_auth = huawei_parse_auth_type (auth); /* Default to no authentication if not specified */ if ((encoded_auth = huawei_parse_auth_type (auth)) == MM_BEARER_HUAWEI_AUTH_UNKNOWN) encoded_auth = MM_BEARER_HUAWEI_AUTH_NONE; if (!user && !passwd) command = g_strdup_printf ("AT^NDISDUP=1,1,\"%s\"", apn == NULL ? "" : apn); else if (encoded_auth == MM_BEARER_HUAWEI_AUTH_NONE) command = g_strdup_printf ("AT^NDISDUP=1,1,\"%s\",\"%s\",\"%s\"", apn == NULL ? "" : apn, user == NULL ? "" : user, passwd == NULL ? "" : passwd); else command = g_strdup_printf ("AT^NDISDUP=1,1,\"%s\",\"%s\",\"%s\",%d", apn == NULL ? "" : apn, user == NULL ? "" : user, passwd == NULL ? "" : passwd, encoded_auth); mm_base_modem_at_command_full (ctx->modem, ctx->primary, command, 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)connect_ndisdup_ready, g_object_ref (ctx->self)); g_free (command); return; } case CONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY: /* Wait for dial up timeout, retries for 60 times * (1s between the retries, so it means 1 minute). * If too many retries, failed */ if (ctx->check_count > 60) { /* Clear context */ ctx->self->priv->connect_pending = NULL; g_simple_async_result_set_error (ctx->result, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "Connection attempt timed out"); connect_3gpp_context_complete_and_free (ctx); return; } /* Give up if too many unexpected responses to NIDSSTATQRY are encountered. */ if (ctx->failed_ndisstatqry_count > 10) { /* Clear context */ ctx->self->priv->connect_pending = NULL; g_simple_async_result_set_error (ctx->result, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, "Connection attempt not supported."); connect_3gpp_context_complete_and_free (ctx); return; } /* Check if connected */ ctx->check_count++; mm_base_modem_at_command_full (ctx->modem, ctx->primary, "^NDISSTATQRY?", 3, FALSE, FALSE, NULL, (GAsyncReadyCallback)connect_ndisstatqry_check_ready, g_object_ref (ctx->self)); return; case CONNECT_3GPP_CONTEXT_STEP_LAST: /* Clear context */ ctx->self->priv->connect_pending = NULL; /* Setup result */ { MMBearerIpConfig *ipv4_config; ipv4_config = mm_bearer_ip_config_new (); mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP); g_simple_async_result_set_op_res_gpointer ( ctx->result, mm_bearer_connect_result_new (ctx->data, ipv4_config, NULL), (GDestroyNotify)mm_bearer_connect_result_unref); g_object_unref (ipv4_config); } connect_3gpp_context_complete_and_free (ctx); return; } }
static void set_allowed_modes (MMIfaceModem *self, MMModemMode allowed, MMModemMode preferred, GAsyncReadyCallback callback, gpointer user_data) { GSimpleAsyncResult *result; MMAtSerialPort *primary; gint idx = -1; gchar *command; result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, load_allowed_modes); if (!mm_iface_modem_is_3gpp (self)) { /* Cannot do this in CDMA modems */ g_simple_async_result_set_error (result, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot set allowed modes in CDMA modems"); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } /* Sierra secondary ports don't have full AT command interpreters */ primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); if (!primary || mm_port_get_connected (MM_PORT (primary))) { g_simple_async_result_set_error ( result, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, "Cannot set allowed modes while connected"); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } if (allowed == MM_MODEM_MODE_3G) idx = 1; else if (allowed == MM_MODEM_MODE_2G) idx = 2; else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) { /* in Sierra LTE devices, modes 3 and 4 are automatic, including LTE, no preference */ if (mm_iface_modem_is_3gpp_lte (self)) { if (preferred == MM_MODEM_MODE_NONE) idx = 5; /* GSM and UMTS Only */ } else if (preferred == MM_MODEM_MODE_3G) idx = 3; else if (preferred == MM_MODEM_MODE_2G) idx = 4; else if (preferred == MM_MODEM_MODE_NONE) idx = 0; } else if (allowed == MM_MODEM_MODE_4G) idx = 6; else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G)) idx = 7; else if (allowed == MM_MODEM_MODE_ANY) idx = 0; if (idx < 0) { gchar *allowed_str; gchar *preferred_str; allowed_str = mm_modem_mode_build_string_from_mask (allowed); preferred_str = mm_modem_mode_build_string_from_mask (preferred); g_simple_async_result_set_error (result, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Requested mode (allowed: '%s', preferred: '%s') not " "supported by the modem.", allowed_str, preferred_str); g_free (allowed_str); g_free (preferred_str); g_simple_async_result_complete_in_idle (result); g_object_unref (result); return; } command = g_strdup_printf ("!SELRAT=%d", idx); mm_base_modem_at_command_full (MM_BASE_MODEM (self), primary, command, 3, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)selrat_set_ready, result); g_free (command); }