diff options
Diffstat (limited to 'src/mm-broadband-bearer.c')
-rw-r--r-- | src/mm-broadband-bearer.c | 1691 |
1 files changed, 826 insertions, 865 deletions
diff --git a/src/mm-broadband-bearer.c b/src/mm-broadband-bearer.c index 98714ebc..c6234c12 100644 --- a/src/mm-broadband-bearer.c +++ b/src/mm-broadband-bearer.c @@ -30,11 +30,13 @@ #include "mm-broadband-bearer.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" +#include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-iface-modem-cdma.h" #include "mm-base-modem-at.h" -#include "mm-log.h" +#include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-port-enums-types.h" +#include "mm-helper-enums-types.h" static void async_initable_iface_init (GAsyncInitableIface *iface); @@ -48,6 +50,14 @@ typedef enum { CONNECTION_TYPE_CDMA, } ConnectionType; +enum { + PROP_0, + PROP_FLOW_CONTROL, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + struct _MMBroadbandBearerPrivate { /*-- Common stuff --*/ /* Data port used when modem is connected */ @@ -55,37 +65,26 @@ struct _MMBroadbandBearerPrivate { /* Current connection type */ ConnectionType connection_type; + /* PPP specific */ + MMFlowControl flow_control; + /*-- 3GPP specific --*/ /* CID of the PDP context */ - guint cid; + gint profile_id; }; /*****************************************************************************/ - -guint -mm_broadband_bearer_get_3gpp_cid (MMBroadbandBearer *self) -{ - return self->priv->cid; -} - -/*****************************************************************************/ /* Detailed connect context, used in both CDMA and 3GPP sequences */ typedef struct { - MMBroadbandBearer *self; MMBaseModem *modem; MMPortSerialAt *primary; MMPortSerialAt *secondary; - GCancellable *cancellable; - GSimpleAsyncResult *result; MMPort *data; gboolean close_data_on_exit; /* 3GPP-specific */ - guint cid; - guint max_cid; - gboolean use_existing_cid; MMBearerIpFamily ip_family; } DetailedConnectContext; @@ -94,18 +93,12 @@ detailed_connect_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) - return NULL; - - return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); + return g_task_propagate_pointer (G_TASK (res), error); } static void -detailed_connect_context_complete_and_free (DetailedConnectContext *ctx) +detailed_connect_context_free (DetailedConnectContext *ctx) { - g_simple_async_result_complete_in_idle (ctx->result); - g_object_unref (ctx->result); - g_object_unref (ctx->cancellable); g_object_unref (ctx->primary); if (ctx->secondary) g_object_unref (ctx->secondary); @@ -114,74 +107,26 @@ detailed_connect_context_complete_and_free (DetailedConnectContext *ctx) mm_port_serial_close (MM_PORT_SERIAL (ctx->data)); g_object_unref (ctx->data); } - g_object_unref (ctx->self); g_object_unref (ctx->modem); g_slice_free (DetailedConnectContext, ctx); } -static gboolean -detailed_connect_context_set_error_if_cancelled (DetailedConnectContext *ctx, - GError **error) -{ - if (!g_cancellable_is_cancelled (ctx->cancellable)) - return FALSE; - - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_CANCELLED, - "Connection setup operation has been cancelled"); - return TRUE; -} - -static gboolean -detailed_connect_context_complete_and_free_if_cancelled (DetailedConnectContext *ctx) -{ - GError *error = NULL; - - if (!detailed_connect_context_set_error_if_cancelled (ctx, &error)) - return FALSE; - - g_simple_async_result_take_error (ctx->result, error); - detailed_connect_context_complete_and_free (ctx); - return TRUE; -} - static DetailedConnectContext * detailed_connect_context_new (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, - MMPortSerialAt *secondary, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) + MMPortSerialAt *secondary) { DetailedConnectContext *ctx; ctx = g_slice_new0 (DetailedConnectContext); - ctx->self = g_object_ref (self); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); ctx->secondary = (secondary ? g_object_ref (secondary) : NULL); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - detailed_connect_context_new); ctx->ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); - if (ctx->ip_family == MM_BEARER_IP_FAMILY_NONE || - ctx->ip_family == MM_BEARER_IP_FAMILY_ANY) { - gchar *default_family; - - ctx->ip_family = mm_base_bearer_get_default_ip_family (MM_BASE_BEARER (self)); - default_family = mm_bearer_ip_family_build_string_from_mask (ctx->ip_family); - mm_dbg ("No specific IP family requested, defaulting to %s", default_family); - g_free (default_family); - } + mm_3gpp_normalize_ip_family (&ctx->ip_family); - /* NOTE: - * We don't currently support cancelling AT commands, so we'll just check - * whether the operation is to be cancelled at each step. */ - ctx->cancellable = g_object_ref (cancellable); return ctx; } @@ -189,8 +134,9 @@ detailed_connect_context_new (MMBroadbandBearer *self, /* Generic implementations (both 3GPP and CDMA) are always AT-port based */ static MMPortSerialAt * -common_get_at_data_port (MMBaseModem *modem, - GError **error) +common_get_at_data_port (MMBroadbandBearer *self, + MMBaseModem *modem, + GError **error) { MMPort *data; @@ -211,7 +157,7 @@ common_get_at_data_port (MMBaseModem *modem, return NULL; } - mm_dbg ("Connection through a plain serial AT port (%s)", mm_port_get_device (data)); + mm_obj_dbg (self, "connection through a plain serial AT port: %s", mm_port_get_device (data)); return MM_PORT_SERIAL_AT (g_object_ref (data)); } @@ -228,25 +174,47 @@ common_get_at_data_port (MMBaseModem *modem, */ static void -dial_cdma_ready (MMBaseModem *modem, +dial_cdma_ready (MMBaseModem *modem, GAsyncResult *res, - DetailedConnectContext *ctx) + GTask *task) { - GError *error = NULL; - MMBearerIpConfig *config; + MMBroadbandBearer *self; + DetailedConnectContext *ctx; + GError *error = NULL; + MMBearerIpConfig *config; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); /* DO NOT check for cancellable here. If we got here without errors, the * bearer is really connected and therefore we need to reflect that in * the state machine. */ mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { - mm_warn ("Couldn't connect: '%s'", error->message); - g_simple_async_result_take_error (ctx->result, error); - detailed_connect_context_complete_and_free (ctx); + mm_obj_warn (self, "couldn't connect: %s", error->message); + g_task_return_error (task, error); + g_object_unref (task); return; } - /* else... Yuhu! */ + /* Configure flow control to use while connected */ + if (self->priv->flow_control != MM_FLOW_CONTROL_NONE) { + gchar *flow_control_str; + + flow_control_str = mm_flow_control_build_string_from_mask (self->priv->flow_control); + mm_obj_dbg (self, "setting flow control in %s: %s", mm_port_get_device (ctx->data), flow_control_str); + g_free (flow_control_str); + + if (!mm_port_serial_set_flow_control (MM_PORT_SERIAL (ctx->data), self->priv->flow_control, &error)) { + mm_obj_warn (self, "couldn't set flow control settings in %s: %s", mm_port_get_device (ctx->data), error->message); + g_clear_error (&error); + } + } + + /* The ATD command has succeeded, and therefore the TTY is in data mode now. + * Instead of waiting for setting the port as connected later in + * connect_succeeded(), we do it right away so that we stop our polling. */ + mm_port_set_connected (ctx->data, TRUE); /* Keep port open during connection */ ctx->close_data_on_exit = FALSE; @@ -257,70 +225,62 @@ dial_cdma_ready (MMBaseModem *modem, mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_PPP); /* Assume only IPv4 is given */ - g_simple_async_result_set_op_res_gpointer ( - ctx->result, + g_task_return_pointer ( + task, mm_bearer_connect_result_new (ctx->data, config, NULL), (GDestroyNotify)mm_bearer_connect_result_unref); - detailed_connect_context_complete_and_free (ctx); + g_object_unref (task); g_object_unref (config); } static void -cdma_connect_context_dial (DetailedConnectContext *ctx) +cdma_connect_context_dial (GTask *task) { - gchar *command; - const gchar *number; - - number = mm_bearer_properties_get_number (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))); + DetailedConnectContext *ctx; - /* If a number was given when creating the bearer, use that one. - * Otherwise, use the default one, #777 - */ - if (number) - command = g_strconcat ("DT", number, NULL); - else - command = g_strdup ("DT#777"); + ctx = g_task_get_task_data (task); mm_base_modem_at_command_full (ctx->modem, MM_PORT_SERIAL_AT (ctx->data), - command, - 90, + "DT#777", + MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, FALSE, NULL, (GAsyncReadyCallback)dial_cdma_ready, - ctx); - g_free (command); + task); } static void set_rm_protocol_ready (MMBaseModem *self, GAsyncResult *res, - DetailedConnectContext *ctx) + GTask *task) { GError *error = NULL; /* If cancelled, complete */ - if (detailed_connect_context_complete_and_free_if_cancelled (ctx)) + if (g_task_return_error_if_cancelled (task)) { + g_object_unref (task); return; + } mm_base_modem_at_command_full_finish (self, res, &error); if (error) { - mm_warn ("Couldn't set RM protocol: '%s'", error->message); - g_simple_async_result_take_error (ctx->result, error); - detailed_connect_context_complete_and_free (ctx); + mm_obj_warn (self, "couldn't set RM protocol: %s", error->message); + g_task_return_error (task, error); + g_object_unref (task); return; } /* Nothing else needed, go on with dialing */ - cdma_connect_context_dial (ctx); + cdma_connect_context_dial (task); } static void current_rm_protocol_ready (MMBaseModem *self, GAsyncResult *res, - DetailedConnectContext *ctx) + GTask *task) { const gchar *result; GError *error = NULL; @@ -328,14 +288,16 @@ current_rm_protocol_ready (MMBaseModem *self, MMModemCdmaRmProtocol current_rm; /* If cancelled, complete */ - if (detailed_connect_context_complete_and_free_if_cancelled (ctx)) + if (g_task_return_error_if_cancelled (task)) { + g_object_unref (task); return; + } result = mm_base_modem_at_command_full_finish (self, res, &error); if (error) { - mm_warn ("Couldn't query current RM protocol: '%s'", error->message); - g_simple_async_result_take_error (ctx->result, error); - detailed_connect_context_complete_and_free (ctx); + mm_obj_warn (self, "couldn't query current RM protocol: %s", error->message); + g_task_return_error (task, error); + g_object_unref (task); return; } @@ -343,31 +305,30 @@ current_rm_protocol_ready (MMBaseModem *self, current_index = (guint) atoi (result); current_rm = mm_cdma_get_rm_protocol_from_index (current_index, &error); if (error) { - mm_warn ("Couldn't parse RM protocol reply (%s): '%s'", - result, - error->message); - g_simple_async_result_take_error (ctx->result, error); - detailed_connect_context_complete_and_free (ctx); + mm_obj_warn (self, "couldn't parse RM protocol reply (%s): %s", result, error->message); + g_task_return_error (task, error); + g_object_unref (task); return; } if (current_rm != mm_bearer_properties_get_rm_protocol (mm_base_bearer_peek_config (MM_BASE_BEARER (self)))) { + DetailedConnectContext *ctx; guint new_index; gchar *command; - mm_dbg ("Setting requested RM protocol..."); + mm_obj_dbg (self, "setting requested RM protocol..."); new_index = (mm_cdma_get_index_from_rm_protocol ( mm_bearer_properties_get_rm_protocol (mm_base_bearer_peek_config (MM_BASE_BEARER (self))), &error)); if (error) { - mm_warn ("Cannot set RM protocol: '%s'", - error->message); - g_simple_async_result_take_error (ctx->result, error); - detailed_connect_context_complete_and_free (ctx); + mm_obj_warn (self, "cannot set RM protocol: %s", error->message); + g_task_return_error (task, error); + g_object_unref (task); return; } + ctx = g_task_get_task_data (task); command = g_strdup_printf ("+CRM=%u", new_index); mm_base_modem_at_command_full (ctx->modem, ctx->primary, @@ -377,13 +338,13 @@ current_rm_protocol_ready (MMBaseModem *self, FALSE, NULL, (GAsyncReadyCallback)set_rm_protocol_ready, - ctx); + task); g_free (command); return; } /* Nothing else needed, go on with dialing */ - cdma_connect_context_dial (ctx); + cdma_connect_context_dial (task); } static void @@ -396,24 +357,22 @@ connect_cdma (MMBroadbandBearer *self, gpointer user_data) { DetailedConnectContext *ctx; + GTask *task; GError *error = NULL; g_assert (primary != NULL); - ctx = detailed_connect_context_new (self, - modem, - primary, - NULL, - cancellable, - callback, - user_data); + ctx = detailed_connect_context_new (self, modem, primary, NULL); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_connect_context_free); /* Grab dial port. This gets a reference to the dial port and OPENs it. * If we fail, we'll need to close it ourselves. */ - ctx->data = (MMPort *)common_get_at_data_port (ctx->modem, &error); + ctx->data = (MMPort *)common_get_at_data_port (self, ctx->modem, &error); if (!ctx->data) { - g_simple_async_result_take_error (ctx->result, error); - detailed_connect_context_complete_and_free (ctx); + g_task_return_error (task, error); + g_object_unref (task); return; } ctx->close_data_on_exit = TRUE; @@ -422,7 +381,7 @@ connect_cdma (MMBroadbandBearer *self, mm_base_bearer_peek_config (MM_BASE_BEARER (self))) != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) { /* Need to query current RM protocol */ - mm_dbg ("Querying current RM protocol set..."); + mm_obj_dbg (self, "querying current RM protocol set..."); mm_base_modem_at_command_full (ctx->modem, ctx->primary, "+CRM?", @@ -431,121 +390,95 @@ connect_cdma (MMBroadbandBearer *self, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)current_rm_protocol_ready, - ctx); + task); return; } /* Nothing else needed, go on with dialing */ - cdma_connect_context_dial (ctx); + cdma_connect_context_dial (task); } /*****************************************************************************/ /* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */ typedef struct { - MMBroadbandBearer *self; MMBaseModem *modem; MMPortSerialAt *primary; - GCancellable *cancellable; - GSimpleAsyncResult *result; GError *saved_error; - MMPortSerialAt *dial_port; gboolean close_dial_port_on_exit; } Dial3gppContext; static void -dial_3gpp_context_complete_and_free (Dial3gppContext *ctx) +dial_3gpp_context_free (Dial3gppContext *ctx) { if (ctx->saved_error) g_error_free (ctx->saved_error); if (ctx->dial_port) g_object_unref (ctx->dial_port); - g_object_unref (ctx->cancellable); - g_simple_async_result_complete (ctx->result); - g_object_unref (ctx->result); g_object_unref (ctx->primary); g_object_unref (ctx->modem); - g_object_unref (ctx->self); g_slice_free (Dial3gppContext, ctx); } -static gboolean -dial_3gpp_context_set_error_if_cancelled (Dial3gppContext *ctx, - GError **error) -{ - if (!g_cancellable_is_cancelled (ctx->cancellable)) - return FALSE; - - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_CANCELLED, - "Dial operation has been cancelled"); - return TRUE; -} - -static gboolean -dial_3gpp_context_complete_and_free_if_cancelled (Dial3gppContext *ctx) -{ - GError *error = NULL; - - if (!dial_3gpp_context_set_error_if_cancelled (ctx, &error)) - return FALSE; - - g_simple_async_result_take_error (ctx->result, error); - dial_3gpp_context_complete_and_free (ctx); - return TRUE; -} - static MMPort * dial_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) - return NULL; - - return MM_PORT (g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)))); + return g_task_propagate_pointer (G_TASK (res), error); } static void extended_error_ready (MMBaseModem *modem, GAsyncResult *res, - Dial3gppContext *ctx) + GTask *task) { + Dial3gppContext *ctx; const gchar *result; + GError *error = NULL; + + ctx = g_task_get_task_data (task); /* Close the dialling port as we got an error */ mm_port_serial_close (MM_PORT_SERIAL (ctx->dial_port)); /* If cancelled, complete */ - if (dial_3gpp_context_complete_and_free_if_cancelled (ctx)) + if (g_task_return_error_if_cancelled (task)) { + g_object_unref (task); return; + } result = mm_base_modem_at_command_full_finish (modem, res, NULL); if (result && g_str_has_prefix (result, "+CEER: ") && strlen (result) > 7) { - g_simple_async_result_set_error (ctx->result, - ctx->saved_error->domain, - ctx->saved_error->code, - "%s", &result[7]); + error = g_error_new (ctx->saved_error->domain, + ctx->saved_error->code, + "%s", + &result[7]); g_error_free (ctx->saved_error); } else - g_simple_async_result_take_error (ctx->result, - ctx->saved_error); + g_propagate_error (&error, ctx->saved_error); ctx->saved_error = NULL; /* Done with errors */ - dial_3gpp_context_complete_and_free (ctx); + g_task_return_error (task, error); + g_object_unref (task); } static void atd_ready (MMBaseModem *modem, GAsyncResult *res, - Dial3gppContext *ctx) + GTask *task) { + MMBroadbandBearer *self; + Dial3gppContext *ctx; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + /* DO NOT check for cancellable here. If we got here without errors, the * bearer is really connected and therefore we need to reflect that in * the state machine. */ @@ -561,14 +494,34 @@ atd_ready (MMBaseModem *modem, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)extended_error_ready, - ctx); + task); return; } - g_simple_async_result_set_op_res_gpointer (ctx->result, - g_object_ref (ctx->dial_port), - (GDestroyNotify)g_object_unref); - dial_3gpp_context_complete_and_free (ctx); + /* Configure flow control to use while connected */ + if (self->priv->flow_control != MM_FLOW_CONTROL_NONE) { + gchar *flow_control_str; + GError *error = NULL; + + flow_control_str = mm_flow_control_build_string_from_mask (self->priv->flow_control); + mm_obj_dbg (self, "setting flow control in %s: %s", mm_port_get_device (MM_PORT (ctx->dial_port)), flow_control_str); + g_free (flow_control_str); + + if (!mm_port_serial_set_flow_control (MM_PORT_SERIAL (ctx->dial_port), self->priv->flow_control, &error)) { + mm_obj_warn (self, "couldn't set flow control settings in %s: %s", mm_port_get_device (MM_PORT (ctx->dial_port)), error->message); + g_clear_error (&error); + } + } + + /* The ATD command has succeeded, and therefore the TTY is in data mode now. + * Instead of waiting for setting the port as connected later in + * connect_succeeded(), we do it right away so that we stop our polling. */ + mm_port_set_connected (MM_PORT (ctx->dial_port), TRUE); + + g_task_return_pointer (task, + g_object_ref (ctx->dial_port), + g_object_unref); + g_object_unref (task); } static void @@ -582,26 +535,24 @@ dial_3gpp (MMBroadbandBearer *self, { gchar *command; Dial3gppContext *ctx; + GTask *task; GError *error = NULL; g_assert (primary != NULL); ctx = g_slice_new0 (Dial3gppContext); - ctx->self = g_object_ref (self); ctx->modem = g_object_ref (modem); ctx->primary = g_object_ref (primary); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - dial_3gpp); - ctx->cancellable = g_object_ref (cancellable); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_task_data (task, ctx, (GDestroyNotify)dial_3gpp_context_free); /* Grab dial port. This gets a reference to the dial port and OPENs it. * If we fail, we'll need to close it ourselves. */ - ctx->dial_port = common_get_at_data_port (ctx->modem, &error); + ctx->dial_port = common_get_at_data_port (self, ctx->modem, &error); if (!ctx->dial_port) { - g_simple_async_result_take_error (ctx->result, error); - dial_3gpp_context_complete_and_free (ctx); + g_task_return_error (task, error); + g_object_unref (task); return; } @@ -610,16 +561,119 @@ dial_3gpp (MMBroadbandBearer *self, mm_base_modem_at_command_full (ctx->modem, ctx->dial_port, command, - 60, + MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)atd_ready, - ctx); + task); g_free (command); } /*****************************************************************************/ +/* 3GPP cid selection (sub-step of the 3GPP Connection sequence) */ + +typedef struct { + MMBaseModem *modem; + GCancellable *cancellable; + gint profile_id; +} SelectProfile3gppContext; + +static void +select_profile_3gpp_context_free (SelectProfile3gppContext *ctx) +{ + g_object_unref (ctx->modem); + g_object_unref (ctx->cancellable); + g_slice_free (SelectProfile3gppContext, ctx); +} + +static gint +select_profile_3gpp_finish (MMBroadbandBearer *self, + GAsyncResult *res, + GError **error) +{ + gint profile_id; + + /* returns -1 on failure */ + profile_id = (gint) g_task_propagate_int (G_TASK (res), error); + return (profile_id < 0) ? MM_3GPP_PROFILE_ID_UNKNOWN : profile_id; +} + +static void +select_profile_3gpp_set_profile_ready (MMIfaceModem3gppProfileManager *modem, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + g_autoptr(MM3gppProfile) profile = NULL; + + profile = mm_iface_modem_3gpp_profile_manager_set_profile_finish (modem, res, &error); + if (!profile) + g_task_return_error (task, error); + else + g_task_return_int (task, mm_3gpp_profile_get_profile_id (profile)); + g_object_unref (task); +} + +static void +select_profile_3gpp_get_profile_ready (MMIfaceModem3gppProfileManager *modem, + GAsyncResult *res, + GTask *task) +{ + SelectProfile3gppContext *ctx; + GError *error = NULL; + g_autoptr(MM3gppProfile) profile = NULL; + + ctx = g_task_get_task_data (task); + + profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (modem, res, &error); + if (!profile) + g_task_return_error (task, error); + else + g_task_return_int (task, ctx->profile_id); + g_object_unref (task); +} + +static void +select_profile_3gpp (MMBroadbandBearer *self, + MMBaseModem *modem, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + SelectProfile3gppContext *ctx; + MMBearerProperties *bearer_properties; + + task = g_task_new (self, cancellable, callback, user_data); + + ctx = g_slice_new0 (SelectProfile3gppContext); + ctx->modem = g_object_ref (modem); + ctx->cancellable = g_object_ref (cancellable); + + bearer_properties = mm_base_bearer_peek_config (MM_BASE_BEARER (self)); + ctx->profile_id = mm_bearer_properties_get_profile_id (bearer_properties); + + g_task_set_task_data (task, ctx, (GDestroyNotify)select_profile_3gpp_context_free); + + if (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) { + mm_iface_modem_3gpp_profile_manager_set_profile ( + MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (modem), + mm_bearer_properties_peek_3gpp_profile (bearer_properties), + FALSE, /* not strict! */ + (GAsyncReadyCallback)select_profile_3gpp_set_profile_ready, + task); + return; + } + + mm_iface_modem_3gpp_profile_manager_get_profile ( + MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (modem), + ctx->profile_id, + (GAsyncReadyCallback)select_profile_3gpp_get_profile_ready, + task); +} + +/*****************************************************************************/ /* 3GPP CONNECT * * 3GPP connection procedure of a bearer involves several steps: @@ -635,21 +689,23 @@ dial_3gpp (MMBroadbandBearer *self, */ static void -get_ip_config_3gpp_ready (MMBroadbandModem *modem, - GAsyncResult *res, - DetailedConnectContext *ctx) -{ - MMBearerIpConfig *ipv4_config = NULL; - MMBearerIpConfig *ipv6_config = NULL; - GError *error = NULL; - - if (!MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->get_ip_config_3gpp_finish (ctx->self, - res, - &ipv4_config, - &ipv6_config, - &error)) { - g_simple_async_result_take_error (ctx->result, error); - detailed_connect_context_complete_and_free (ctx); +get_ip_config_3gpp_ready (MMBroadbandBearer *self, + GAsyncResult *res, + GTask *task) +{ + DetailedConnectContext *ctx; + GError *error = NULL; + g_autoptr(MMBearerConnectResult) result = NULL; + g_autoptr(MMBearerIpConfig) ipv4_config = NULL; + g_autoptr(MMBearerIpConfig) ipv6_config = NULL; + + ctx = g_task_get_task_data (task); + + if (!MM_BROADBAND_BEARER_GET_CLASS (self)->get_ip_config_3gpp_finish (self, res, + &ipv4_config, &ipv6_config, + &error)) { + g_task_return_error (task, error); + g_object_unref (task); return; } @@ -657,34 +713,33 @@ get_ip_config_3gpp_ready (MMBroadbandModem *modem, if (MM_IS_PORT_SERIAL_AT (ctx->data)) ctx->close_data_on_exit = FALSE; - g_simple_async_result_set_op_res_gpointer ( - ctx->result, - mm_bearer_connect_result_new (ctx->data, ipv4_config, ipv6_config), - (GDestroyNotify)mm_bearer_connect_result_unref); - detailed_connect_context_complete_and_free (ctx); + result = mm_bearer_connect_result_new (ctx->data, ipv4_config, ipv6_config); + mm_bearer_connect_result_set_profile_id (result, self->priv->profile_id); - if (ipv4_config) - g_object_unref (ipv4_config); - if (ipv6_config) - g_object_unref (ipv6_config); + g_task_return_pointer (task, g_steal_pointer (&result), (GDestroyNotify)mm_bearer_connect_result_unref); + g_object_unref (task); } static void -dial_3gpp_ready (MMBroadbandModem *modem, - GAsyncResult *res, - DetailedConnectContext *ctx) +dial_3gpp_ready (MMBroadbandBearer *self, + GAsyncResult *res, + GTask *task) { - MMBearerIpMethod ip_method = MM_BEARER_IP_METHOD_UNKNOWN; - MMBearerIpConfig *ipv4_config = NULL; - MMBearerIpConfig *ipv6_config = NULL; - GError *error = NULL; + DetailedConnectContext *ctx; + MMBearerIpMethod ip_method = MM_BEARER_IP_METHOD_UNKNOWN; + GError *error = NULL; + g_autoptr(MMBearerConnectResult) result = NULL; + g_autoptr(MMBearerIpConfig) ipv4_config = NULL; + g_autoptr(MMBearerIpConfig) ipv6_config = NULL; + + ctx = g_task_get_task_data (task); - ctx->data = MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->dial_3gpp_finish (ctx->self, res, &error); + ctx->data = MM_BROADBAND_BEARER_GET_CLASS (self)->dial_3gpp_finish (self, res, &error); if (!ctx->data) { /* Clear CID when it failed to connect. */ - ctx->self->priv->cid = 0; - g_simple_async_result_take_error (ctx->result, error); - detailed_connect_context_complete_and_free (ctx); + self->priv->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN; + g_task_return_error (task, error); + g_object_unref (task); return; } @@ -693,19 +748,19 @@ dial_3gpp_ready (MMBroadbandModem *modem, if (MM_IS_PORT_SERIAL_AT (ctx->data)) ctx->close_data_on_exit = TRUE; - if (MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->get_ip_config_3gpp && - MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->get_ip_config_3gpp_finish) { + if (MM_BROADBAND_BEARER_GET_CLASS (self)->get_ip_config_3gpp && + MM_BROADBAND_BEARER_GET_CLASS (self)->get_ip_config_3gpp_finish) { /* Launch specific IP config retrieval */ - MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->get_ip_config_3gpp ( - ctx->self, + MM_BROADBAND_BEARER_GET_CLASS (self)->get_ip_config_3gpp ( + self, MM_BROADBAND_MODEM (ctx->modem), ctx->primary, ctx->secondary, ctx->data, - ctx->cid, + (guint)self->priv->profile_id, ctx->ip_family, (GAsyncReadyCallback)get_ip_config_3gpp_ready, - ctx); + task); return; } @@ -733,408 +788,140 @@ dial_3gpp_ready (MMBroadbandModem *modem, } g_assert (ipv4_config || ipv6_config); - g_simple_async_result_set_op_res_gpointer ( - ctx->result, - mm_bearer_connect_result_new (ctx->data, ipv4_config, ipv6_config), - (GDestroyNotify)mm_bearer_connect_result_unref); - detailed_connect_context_complete_and_free (ctx); - - g_clear_object (&ipv4_config); - g_clear_object (&ipv6_config); + result = mm_bearer_connect_result_new (ctx->data, ipv4_config, ipv6_config); + mm_bearer_connect_result_set_profile_id (result, self->priv->profile_id); + g_task_return_pointer (task, g_steal_pointer (&result), (GDestroyNotify)mm_bearer_connect_result_unref); + g_object_unref (task); } static void -start_3gpp_dial (DetailedConnectContext *ctx) +select_profile_3gpp_ready (MMBroadbandBearer *self, + GAsyncResult *res, + GTask *task) { - /* Keep CID around after initializing the PDP context in order to - * handle corresponding unsolicited PDP activation responses. */ - ctx->self->priv->cid = ctx->cid; - MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->dial_3gpp (ctx->self, - ctx->modem, - ctx->primary, - ctx->cid, - ctx->cancellable, - (GAsyncReadyCallback)dial_3gpp_ready, - ctx); -} + DetailedConnectContext *ctx; + GError *error = NULL; -static void -initialize_pdp_context_ready (MMBaseModem *modem, - GAsyncResult *res, - DetailedConnectContext *ctx) -{ - GError *error = NULL; + ctx = g_task_get_task_data (task); - /* If cancelled, complete */ - if (detailed_connect_context_complete_and_free_if_cancelled (ctx)) - return; - - mm_base_modem_at_command_full_finish (modem, res, &error); - if (error) { - mm_warn ("Couldn't initialize PDP context with our APN: '%s'", - error->message); - g_simple_async_result_take_error (ctx->result, error); - detailed_connect_context_complete_and_free (ctx); + /* Keep CID around after initializing the PDP context in order to + * handle corresponding unsolicited PDP activation responses. */ + self->priv->profile_id = select_profile_3gpp_finish (self, res, &error); + if (self->priv->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) { + g_task_return_error (task, error); + g_object_unref (task); return; } - start_3gpp_dial (ctx); + MM_BROADBAND_BEARER_GET_CLASS (self)->dial_3gpp (self, + ctx->modem, + ctx->primary, + (guint) self->priv->profile_id, + g_task_get_cancellable (task), + (GAsyncReadyCallback) dial_3gpp_ready, + task); } static void -find_cid_ready (MMBaseModem *modem, - GAsyncResult *res, - DetailedConnectContext *ctx) -{ - GVariant *result; - gchar *apn, *command; - GError *error = NULL; - const gchar *pdp_type; - - result = mm_base_modem_at_sequence_full_finish (modem, res, NULL, &error); - if (!result) { - mm_warn ("Couldn't find best CID to use: '%s'", error->message); - g_simple_async_result_take_error (ctx->result, error); - detailed_connect_context_complete_and_free (ctx); - return; - } - - /* If cancelled, complete. Normally, we would get the cancellation error - * already when finishing the sequence, but we may still get cancelled - * between last command result parsing in the sequence and the ready(). */ - if (detailed_connect_context_complete_and_free_if_cancelled (ctx)) - return; - - pdp_type = mm_3gpp_get_pdp_type_from_ip_family (ctx->ip_family); - if (!pdp_type) { - gchar * str; - - str = mm_bearer_ip_family_build_string_from_mask (ctx->ip_family); - g_simple_async_result_set_error (ctx->result, - MM_CORE_ERROR, - MM_CORE_ERROR_INVALID_ARGS, - "Unsupported IP type requested: '%s'", - str); - g_free (str); - detailed_connect_context_complete_and_free (ctx); - return; - } - ctx->cid = g_variant_get_uint32 (result); - - /* If there's already a PDP context defined, just use it */ - if (ctx->use_existing_cid) { - start_3gpp_dial (ctx); - return; - } - - /* Otherwise, initialize a new PDP context with our APN */ - apn = mm_port_serial_at_quote_string (mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)))); - command = g_strdup_printf ("+CGDCONT=%u,\"%s\",%s", - ctx->cid, - pdp_type, - apn); - g_free (apn); - mm_base_modem_at_command_full (ctx->modem, - ctx->primary, - command, - 3, - FALSE, - FALSE, /* raw */ - NULL, /* cancellable */ - (GAsyncReadyCallback)initialize_pdp_context_ready, - ctx); - g_free (command); -} - -static gboolean -parse_cid_range (MMBaseModem *modem, - DetailedConnectContext *ctx, - const gchar *command, - const gchar *response, - gboolean last_command, - const GError *error, - GVariant **result, - GError **result_error) -{ - GError *inner_error = NULL; - GList *formats, *l; - guint cid; - - /* If cancelled, set result error */ - if (detailed_connect_context_set_error_if_cancelled (ctx, result_error)) - return FALSE; - - if (error) { - mm_dbg ("Unexpected +CGDCONT error: '%s'", error->message); - mm_dbg ("Defaulting to CID=1"); - *result = g_variant_new_uint32 (1); - return TRUE; - } - - formats = mm_3gpp_parse_cgdcont_test_response (response, &inner_error); - if (inner_error) { - mm_dbg ("Error parsing +CGDCONT test response: '%s'", inner_error->message); - mm_dbg ("Defaulting to CID=1"); - g_error_free (inner_error); - *result = g_variant_new_uint32 (1); - return TRUE; - } - - cid = 0; - for (l = formats; l; l = g_list_next (l)) { - MM3gppPdpContextFormat *format = l->data; - - /* Found exact PDP type? */ - if (format->pdp_type == ctx->ip_family) { - if (ctx->max_cid < format->max_cid) - cid = ctx->max_cid + 1; - else - cid = ctx->max_cid; - break; - } - } - - mm_3gpp_pdp_context_format_list_free (formats); - - if (cid == 0) { - mm_dbg ("Defaulting to CID=1"); - cid = 1; - } else - mm_dbg ("Using CID %u", cid); - - *result = g_variant_new_uint32 (cid); - return TRUE; -} - -static gboolean -parse_pdp_list (MMBaseModem *modem, - DetailedConnectContext *ctx, - const gchar *command, - const gchar *response, - gboolean last_command, - const GError *error, - GVariant **result, - GError **result_error) +connect_3gpp (MMBroadbandBearer *self, + MMBroadbandModem *modem, + MMPortSerialAt *primary, + MMPortSerialAt *secondary, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - GError *inner_error = NULL; - GList *pdp_list; - GList *l; - guint cid; - - /* If cancelled, set result error */ - if (detailed_connect_context_set_error_if_cancelled (ctx, result_error)) - return FALSE; - - ctx->max_cid = 0; - - /* Some Android phones don't support querying existing PDP contexts, - * but will accept setting the APN. So if CGDCONT? isn't supported, - * just ignore that error and hope for the best. (bgo #637327) - */ - if (g_error_matches (error, - MM_MOBILE_EQUIPMENT_ERROR, - MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED)) { - mm_dbg ("Querying PDP context list is unsupported"); - return FALSE; - } - - if (error) { - mm_dbg ("Unexpected +CGDCONT? error: '%s'", error->message); - return FALSE; - } - - pdp_list = mm_3gpp_parse_cgdcont_read_response (response, &inner_error); - if (!pdp_list) { - if (inner_error) { - mm_dbg ("%s", inner_error->message); - g_error_free (inner_error); - } else { - /* No predefined PDP contexts found */ - mm_dbg ("No PDP contexts found"); - } - return FALSE; - } - - cid = 0; - - /* Show all found PDP contexts in debug log */ - mm_dbg ("Found '%u' PDP contexts", g_list_length (pdp_list)); - for (l = pdp_list; l; l = g_list_next (l)) { - MM3gppPdpContext *pdp = l->data; - gchar *ip_family_str; - - ip_family_str = mm_bearer_ip_family_build_string_from_mask (pdp->pdp_type); - mm_dbg (" PDP context [cid=%u] [type='%s'] [apn='%s']", - pdp->cid, - ip_family_str, - pdp->apn ? pdp->apn : ""); - g_free (ip_family_str); - } - - /* Look for the exact PDP context we want */ - for (l = pdp_list; l; l = g_list_next (l)) { - MM3gppPdpContext *pdp = l->data; - - if (pdp->pdp_type == ctx->ip_family) { - /* PDP with no APN set? we may use that one if not exact match found */ - if (!pdp->apn || !pdp->apn[0]) { - mm_dbg ("Found PDP context with CID %u and no APN", - pdp->cid); - cid = pdp->cid; - } else { - const gchar *apn; - - apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))); - if (apn && g_str_equal (pdp->apn, apn)) { - gchar *ip_family_str; - - /* Found a PDP context with the same CID and PDP type, we'll use it. */ - ip_family_str = mm_bearer_ip_family_build_string_from_mask (pdp->pdp_type); - mm_dbg ("Found PDP context with CID %u and PDP type %s for APN '%s'", - pdp->cid, ip_family_str, pdp->apn); - cid = pdp->cid; - ctx->use_existing_cid = TRUE; - g_free (ip_family_str); - /* In this case, stop searching */ - break; - } - } - } - - if (ctx->max_cid < pdp->cid) - ctx->max_cid = pdp->cid; - } - mm_3gpp_pdp_context_list_free (pdp_list); - - if (cid > 0) { - *result = g_variant_new_uint32 (cid); - return TRUE; - } + DetailedConnectContext *ctx; + GTask *task; - return FALSE; -} + g_assert (primary != NULL); -static const MMBaseModemAtCommand find_cid_sequence[] = { - { "+CGDCONT?", 3, FALSE, (MMBaseModemAtResponseProcessor)parse_pdp_list }, - { "+CGDCONT=?", 3, TRUE, (MMBaseModemAtResponseProcessor)parse_cid_range }, - { NULL } -}; + /* Clear CID on every connection attempt */ + self->priv->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN; -static void -connect_3gpp (MMBroadbandBearer *self, - MMBroadbandModem *modem, - MMPortSerialAt *primary, - MMPortSerialAt *secondary, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - DetailedConnectContext *ctx; + ctx = detailed_connect_context_new (self, modem, primary, secondary); - g_assert (primary != NULL); + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_connect_context_free); - ctx = detailed_connect_context_new (self, - modem, - primary, - secondary, - cancellable, - callback, - user_data); - - mm_dbg ("Looking for best CID..."); - mm_base_modem_at_sequence_full (ctx->modem, - ctx->primary, - find_cid_sequence, - ctx, /* also passed as response processor context */ - NULL, /* response_processor_context_free */ - NULL, /* cancellable */ - (GAsyncReadyCallback)find_cid_ready, - ctx); + select_profile_3gpp (self, + ctx->modem, + cancellable, + (GAsyncReadyCallback)select_profile_3gpp_ready, + task); } /*****************************************************************************/ /* CONNECT */ -typedef struct { - MMBroadbandBearer *self; - GSimpleAsyncResult *result; -} ConnectContext; - -static void -connect_context_complete_and_free (ConnectContext *ctx) -{ - g_simple_async_result_complete_in_idle (ctx->result); - g_object_unref (ctx->result); - g_object_unref (ctx->self); - g_slice_free (ConnectContext, ctx); -} - static MMBearerConnectResult * connect_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) - return NULL; - - return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); + return g_task_propagate_pointer (G_TASK (res), error); } static void -connect_succeeded (ConnectContext *ctx, +connect_succeeded (GTask *task, ConnectionType connection_type, MMBearerConnectResult *result) { + MMBroadbandBearer *self; + + self = g_task_get_source_object (task); + /* Keep connected port and type of connection */ - ctx->self->priv->port = g_object_ref (mm_bearer_connect_result_peek_data (result)); - ctx->self->priv->connection_type = connection_type; + self->priv->port = g_object_ref (mm_bearer_connect_result_peek_data (result)); + self->priv->connection_type = connection_type; - /* Port is connected; update the state */ - mm_port_set_connected (ctx->self->priv->port, TRUE); + /* Port is connected; update the state. For ATD based connections, the port + * may already be set as connected, but no big deal. */ + mm_port_set_connected (self->priv->port, TRUE); /* Set operation result */ - g_simple_async_result_set_op_res_gpointer (ctx->result, - result, - (GDestroyNotify)mm_bearer_connect_result_unref); - connect_context_complete_and_free (ctx); + g_task_return_pointer (task, + result, + (GDestroyNotify)mm_bearer_connect_result_unref); + g_object_unref (task); } static void connect_cdma_ready (MMBroadbandBearer *self, GAsyncResult *res, - ConnectContext *ctx) + GTask *task) { MMBearerConnectResult *result; GError *error = NULL; result = MM_BROADBAND_BEARER_GET_CLASS (self)->connect_cdma_finish (self, res, &error); if (!result) { - g_simple_async_result_take_error (ctx->result, error); - connect_context_complete_and_free (ctx); + g_task_return_error (task, error); + g_object_unref (task); return; } /* take result */ - connect_succeeded (ctx, CONNECTION_TYPE_CDMA, result); + connect_succeeded (task, CONNECTION_TYPE_CDMA, result); } static void connect_3gpp_ready (MMBroadbandBearer *self, GAsyncResult *res, - ConnectContext *ctx) + GTask *task) { MMBearerConnectResult *result; GError *error = NULL; result = MM_BROADBAND_BEARER_GET_CLASS (self)->connect_3gpp_finish (self, res, &error); if (!result) { - g_simple_async_result_take_error (ctx->result, error); - connect_context_complete_and_free (ctx); + g_task_return_error (task, error); + g_object_unref (task); return; } /* take result */ - connect_succeeded (ctx, CONNECTION_TYPE_3GPP, result); + connect_succeeded (task, CONNECTION_TYPE_3GPP, result); } static void @@ -1143,20 +930,20 @@ connect (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data) { - MMBaseModem *modem = NULL; - MMPortSerialAt *primary; - ConnectContext *ctx; - const gchar *apn; + MMPortSerialAt *primary; + const gchar *apn; + gint profile_id; + MMBearerMultiplexSupport multiplex; + GTask *task; + g_autoptr(MMBaseModem) modem = NULL; + + task = g_task_new (self, cancellable, callback, user_data); /* Don't try to connect if already connected */ if (MM_BROADBAND_BEARER (self)->priv->port) { - g_simple_async_report_error_in_idle ( - G_OBJECT (self), - callback, - user_data, - MM_CORE_ERROR, - MM_CORE_ERROR_CONNECTED, - "Couldn't connect: this bearer is already connected"); + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, + "Couldn't connect: this bearer is already connected"); + g_object_unref (task); return; } @@ -1169,89 +956,79 @@ connect (MMBaseBearer *self, /* We will launch the ATD call in the primary port... */ primary = mm_base_modem_peek_port_primary (modem); if (!primary) { - g_simple_async_report_error_in_idle ( - G_OBJECT (self), - callback, - user_data, - MM_CORE_ERROR, - MM_CORE_ERROR_CONNECTED, - "Couldn't connect: couldn't get primary port"); - g_object_unref (modem); + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, + "Couldn't connect: couldn't get primary port"); + g_object_unref (task); return; } /* ...only if not already connected */ if (mm_port_get_connected (MM_PORT (primary))) { - g_simple_async_report_error_in_idle ( - G_OBJECT (self), - callback, - user_data, - MM_CORE_ERROR, - MM_CORE_ERROR_CONNECTED, - "Couldn't connect: primary AT port is already connected"); - g_object_unref (modem); + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED, + "Couldn't connect: primary AT port is already connected"); + g_object_unref (task); return; } /* Default bearer connection logic * * 1) 3GPP-only modem: - * 1a) If no APN given, error. - * 1b) If APN given, run 3GPP connection logic. + * 1a) If no profile id or APN given, error. + * 1b) If APN or profile id given, run 3GPP connection logic. * 1c) If APN given, but empty (""), run 3GPP connection logic and try * to use default subscription APN. * * 2) 3GPP2-only modem: - * 2a) If no APN given, run CDMA connection logic. - * 2b) If APN given, error. + * 2a) If no APN or profile id given, run CDMA connection logic. + * 2b) If APN or profile id given, error. * * 3) 3GPP and 3GPP2 modem: - * 3a) If no APN given, run CDMA connection logic. + * 3a) If no profile id or APN given, run CDMA connection logic. * 3b) If APN given, run 3GPP connection logic. - * 1c) If APN given, but empty (""), run 3GPP connection logic and try + * 3c) If APN given, but empty (""), run 3GPP connection logic and try * to use default subscription APN. */ /* Check whether we have an APN */ apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); - - /* Is this a 3GPP only modem and no APN was given? If so, error */ - if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (modem)) && !apn) { - g_simple_async_report_error_in_idle ( - G_OBJECT (self), - callback, - user_data, - MM_CORE_ERROR, - MM_CORE_ERROR_INVALID_ARGS, - "3GPP connection logic requires APN setting"); - g_object_unref (modem); + profile_id = mm_bearer_properties_get_profile_id (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); + + /* Is this a 3GPP only modem and no APN or profile id was given? If so, error */ + if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (modem)) && + !apn && (profile_id == MM_3GPP_PROFILE_ID_UNKNOWN)) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, + "3GPP connection logic requires APN or profile id setting"); + g_object_unref (task); return; } - /* Is this a 3GPP2 only modem and APN was given? If so, error */ - if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (modem)) && apn) { - g_simple_async_report_error_in_idle ( - G_OBJECT (self), - callback, - user_data, - MM_CORE_ERROR, - MM_CORE_ERROR_INVALID_ARGS, - "3GPP2 doesn't support APN setting"); - g_object_unref (modem); + /* Is this a 3GPP2 only modem and APN or profile id was given? If so, error */ + if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (modem)) && + (apn || (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN))) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, + "3GPP2 doesn't support APN or profile id setting"); + g_object_unref (task); return; } - /* In this context, we only keep the stuff we'll need later */ - ctx = g_slice_new0 (ConnectContext); - ctx->self = g_object_ref (self); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - connect); + /* If no multiplex setting given by the user, assume none (which is the only + * supported mode anyway) */ + multiplex = mm_bearer_properties_get_multiplex (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); + if (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN) + multiplex = MM_BEARER_MULTIPLEX_SUPPORT_NONE; + + /* The generic broadband bearer doesn't support multiplexing */ + if (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "Multiplexing required but not supported"); + g_object_unref (task); + return; + } /* If the modem has 3GPP capabilities and an APN, launch 3GPP-based connection */ - if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem)) && apn) { - mm_dbg ("Launching 3GPP connection attempt with APN '%s'", apn); + if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem)) && + (apn || (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN))) { + mm_obj_dbg (self, "launching 3GPP connection attempt"); MM_BROADBAND_BEARER_GET_CLASS (self)->connect_3gpp ( MM_BROADBAND_BEARER (self), MM_BROADBAND_MODEM (modem), @@ -1259,14 +1036,14 @@ connect (MMBaseBearer *self, mm_base_modem_peek_port_secondary (modem), cancellable, (GAsyncReadyCallback) connect_3gpp_ready, - ctx); - g_object_unref (modem); + task); return; } /* Otherwise, launch CDMA-specific connection. */ - if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (modem)) && !apn) { - mm_dbg ("Launching 3GPP2 connection attempt"); + if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (modem)) && + !apn && (profile_id == MM_3GPP_PROFILE_ID_UNKNOWN)) { + mm_obj_dbg (self, "launching 3GPP2 connection attempt"); MM_BROADBAND_BEARER_GET_CLASS (self)->connect_cdma ( MM_BROADBAND_BEARER (self), MM_BROADBAND_MODEM (modem), @@ -1274,8 +1051,7 @@ connect (MMBaseBearer *self, mm_base_modem_peek_port_secondary (modem), cancellable, (GAsyncReadyCallback) connect_cdma_ready, - ctx); - g_object_unref (modem); + task); return; } @@ -1286,12 +1062,10 @@ connect (MMBaseBearer *self, /* Detailed disconnect context, used in both CDMA and 3GPP sequences */ typedef struct { - MMBroadbandBearer *self; MMBaseModem *modem; MMPortSerialAt *primary; MMPortSerialAt *secondary; MMPort *data; - GSimpleAsyncResult *result; /* 3GPP-specific */ gchar *cgact_command; @@ -1303,46 +1077,35 @@ detailed_disconnect_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); + return g_task_propagate_boolean (G_TASK (res), error); } static void -detailed_disconnect_context_complete_and_free (DetailedDisconnectContext *ctx) +detailed_disconnect_context_free (DetailedDisconnectContext *ctx) { - g_simple_async_result_complete_in_idle (ctx->result); - if (ctx->cgact_command) - g_free (ctx->cgact_command); - g_object_unref (ctx->result); + g_free (ctx->cgact_command); g_object_unref (ctx->data); g_object_unref (ctx->primary); if (ctx->secondary) g_object_unref (ctx->secondary); - g_object_unref (ctx->self); g_object_unref (ctx->modem); g_free (ctx); } static DetailedDisconnectContext * -detailed_disconnect_context_new (MMBroadbandBearer *self, - MMBroadbandModem *modem, +detailed_disconnect_context_new (MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, - MMPort *data, - GAsyncReadyCallback callback, - gpointer user_data) + MMPort *data) { DetailedDisconnectContext *ctx; ctx = g_new0 (DetailedDisconnectContext, 1); - ctx->self = g_object_ref (self); ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); ctx->primary = g_object_ref (primary); ctx->secondary = (secondary ? g_object_ref (secondary) : NULL); ctx->data = g_object_ref (data); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - detailed_disconnect_context_new); + return ctx; } @@ -1352,9 +1115,12 @@ detailed_disconnect_context_new (MMBroadbandBearer *self, static void data_flash_cdma_ready (MMPortSerial *data, GAsyncResult *res, - DetailedDisconnectContext *ctx) + GTask *task) { - GError *error = NULL; + MMBroadbandBearer *self; + GError *error = NULL; + + self = g_task_get_source_object (task); mm_port_serial_flash_finish (data, res, &error); @@ -1376,40 +1142,50 @@ data_flash_cdma_ready (MMPortSerial *data, MM_SERIAL_ERROR, MM_SERIAL_ERROR_FLASH_FAILED)) { /* Fatal */ - g_simple_async_result_take_error (ctx->result, error); - detailed_disconnect_context_complete_and_free (ctx); + g_task_return_error (task, error); + g_object_unref (task); return; } - mm_dbg ("Port flashing failed (not fatal): %s", error->message); + mm_obj_dbg (self, "port flashing failed (not fatal): %s", error->message); g_error_free (error); } - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - detailed_disconnect_context_complete_and_free (ctx); + /* Run init port sequence in the data port */ + mm_port_serial_at_run_init_sequence (MM_PORT_SERIAL_AT (data)); + + g_task_return_boolean (task, TRUE); + g_object_unref (task); } static void data_reopen_cdma_ready (MMPortSerial *data, GAsyncResult *res, - DetailedDisconnectContext *ctx) + GTask *task) { - GError *error = NULL; + MMBroadbandBearer *self; + DetailedDisconnectContext *ctx; + GError *error = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + g_object_set (data, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, TRUE, NULL); if (!mm_port_serial_reopen_finish (data, res, &error)) { /* Fatal */ - g_simple_async_result_take_error (ctx->result, error); - detailed_disconnect_context_complete_and_free (ctx); + g_task_return_error (task, error); + g_object_unref (task); return; } /* Just flash the data port */ - mm_dbg ("Flashing data port (%s)...", mm_port_get_device (MM_PORT (ctx->data))); + mm_obj_dbg (self, "flashing data port %s...", mm_port_get_device (MM_PORT (ctx->data))); mm_port_serial_flash (MM_PORT_SERIAL (ctx->data), 1000, TRUE, (GAsyncReadyCallback)data_flash_cdma_ready, - ctx); + task); } static void @@ -1422,56 +1198,65 @@ disconnect_cdma (MMBroadbandBearer *self, gpointer user_data) { DetailedDisconnectContext *ctx; + GTask *task; g_assert (primary != NULL); /* Generic CDMA plays only with SERIAL data ports */ g_assert (MM_IS_PORT_SERIAL (data)); - ctx = detailed_disconnect_context_new (self, - modem, - primary, - secondary, - data, - callback, - user_data); + ctx = detailed_disconnect_context_new (modem, primary, secondary, data); + + task = g_task_new (self, NULL, callback, user_data); + g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_disconnect_context_free); + + /* We don't want to run init sequence right away during the reopen, as we're + * going to flash afterwards. */ + g_object_set (data, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, FALSE, NULL); /* Fully reopen the port before flashing */ - mm_dbg ("Reopening data port (%s)...", mm_port_get_device (MM_PORT (ctx->data))); + mm_obj_dbg (self, "reopening data port %s...", mm_port_get_device (MM_PORT (ctx->data))); mm_port_serial_reopen (MM_PORT_SERIAL (ctx->data), 1000, (GAsyncReadyCallback)data_reopen_cdma_ready, - ctx); + task); } /*****************************************************************************/ /* 3GPP disconnect */ static void -cgact_data_ready (MMBaseModem *modem, +cgact_data_ready (MMBaseModem *modem, GAsyncResult *res, - DetailedDisconnectContext *ctx) + GTask *task) { + MMBroadbandBearer *self; + GError *error = NULL; - GError *error = NULL; + self = g_task_get_source_object (task); /* Ignore errors for now */ mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { - mm_dbg ("PDP context deactivation failed (not fatal): %s", error->message); + mm_obj_dbg (self, "PDP context deactivation failed (not fatal): %s", error->message); g_error_free (error); } - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - detailed_disconnect_context_complete_and_free (ctx); + g_task_return_boolean (task, TRUE); + g_object_unref (task); } static void data_flash_3gpp_ready (MMPortSerial *data, GAsyncResult *res, - DetailedDisconnectContext *ctx) + GTask *task) { - GError *error = NULL; + MMBroadbandBearer *self; + DetailedDisconnectContext *ctx; + GError *error = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); mm_port_serial_flash_finish (data, res, &error); @@ -1493,87 +1278,120 @@ data_flash_3gpp_ready (MMPortSerial *data, MM_SERIAL_ERROR, MM_SERIAL_ERROR_FLASH_FAILED)) { /* Fatal */ - g_simple_async_result_take_error (ctx->result, error); - detailed_disconnect_context_complete_and_free (ctx); + g_task_return_error (task, error); + g_object_unref (task); return; } - mm_dbg ("Port flashing failed (not fatal): %s", error->message); + mm_obj_dbg (self, "port flashing failed (not fatal): %s", error->message); g_error_free (error); } + /* Run init port sequence in the data port */ + mm_port_serial_at_run_init_sequence (MM_PORT_SERIAL_AT (data)); + /* Don't bother doing the CGACT again if it was already done on the * primary or secondary port */ if (ctx->cgact_sent) { - mm_dbg ("PDP disconnection already sent"); - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - detailed_disconnect_context_complete_and_free (ctx); + mm_obj_dbg (self, "PDP disconnection already sent"); + g_task_return_boolean (task, TRUE); + g_object_unref (task); return; } - /* Last resort, try to send CGACT in the data port itself */ - mm_dbg ("Sending PDP context deactivation in data port..."); + /* Send another CGACT on the primary port (also the data port when the modem + * only has one serial port) if the previous one failed. Some modems, like + * the Huawei E173 (fw 11.126.15.00.445) stop responding on their primary + * port when the CGACT is sent on the separte data port. + */ + if (MM_PORT_SERIAL (ctx->primary) == data) + mm_obj_dbg (self, "sending PDP context deactivation in primary/data port..."); + else + mm_obj_dbg (self, "sending PDP context deactivation in primary port again..."); + mm_base_modem_at_command_full (ctx->modem, - MM_PORT_SERIAL_AT (data), + ctx->primary, ctx->cgact_command, 10, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)cgact_data_ready, - ctx); + task); } static void data_reopen_3gpp_ready (MMPortSerial *data, GAsyncResult *res, - DetailedDisconnectContext *ctx) + GTask *task) { - GError *error = NULL; + MMBroadbandBearer *self; + DetailedDisconnectContext *ctx; + GError *error = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + g_object_set (data, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, TRUE, NULL); if (!mm_port_serial_reopen_finish (data, res, &error)) { /* Fatal */ - g_simple_async_result_take_error (ctx->result, error); - detailed_disconnect_context_complete_and_free (ctx); + g_task_return_error (task, error); + g_object_unref (task); return; } /* Just flash the data port */ - mm_dbg ("Flashing data port (%s)...", mm_port_get_device (MM_PORT (ctx->data))); + mm_obj_dbg (self, "flashing data port %s...", mm_port_get_device (MM_PORT (ctx->data))); mm_port_serial_flash (MM_PORT_SERIAL (ctx->data), 1000, TRUE, (GAsyncReadyCallback)data_flash_3gpp_ready, - ctx); + task); } static void -data_reopen_3gpp (DetailedDisconnectContext *ctx) +data_reopen_3gpp (GTask *task) { + MMBroadbandBearer *self; + DetailedDisconnectContext *ctx; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + /* We don't want to run init sequence right away during the reopen, as we're + * going to flash afterwards. */ + g_object_set (ctx->data, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, FALSE, NULL); + /* Fully reopen the port before flashing */ - mm_dbg ("Reopening data port (%s)...", mm_port_get_device (MM_PORT (ctx->data))); + mm_obj_dbg (self, "reopening data port %s...", mm_port_get_device (MM_PORT (ctx->data))); mm_port_serial_reopen (MM_PORT_SERIAL (ctx->data), 1000, (GAsyncReadyCallback)data_reopen_3gpp_ready, - ctx); + task); } static void -cgact_ready (MMBaseModem *modem, +cgact_ready (MMBaseModem *modem, GAsyncResult *res, - DetailedDisconnectContext *ctx) + GTask *task) { - GError *error = NULL; + MMBroadbandBearer *self; + DetailedDisconnectContext *ctx; + GError *error = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); mm_base_modem_at_command_full_finish (modem, res, &error); if (!error) ctx->cgact_sent = TRUE; else { - mm_dbg ("PDP context deactivation failed (not fatal): %s", error->message); + mm_obj_dbg (self, "PDP context deactivation failed (not fatal): %s", error->message); g_error_free (error); } - data_reopen_3gpp (ctx); + data_reopen_3gpp (task); } static void @@ -1587,38 +1405,36 @@ disconnect_3gpp (MMBroadbandBearer *self, gpointer user_data) { DetailedDisconnectContext *ctx; + GTask *task; g_assert (primary != NULL); /* Generic 3GPP plays only with SERIAL data ports */ g_assert (MM_IS_PORT_SERIAL (data)); - ctx = detailed_disconnect_context_new (self, - modem, - primary, - secondary, - data, - callback, - user_data); + ctx = detailed_disconnect_context_new (modem, primary, secondary, data); /* If no specific CID was used, disable all PDP contexts */ ctx->cgact_command = (cid > 0 ? g_strdup_printf ("+CGACT=0,%d", cid) : g_strdup_printf ("+CGACT=0")); + task = g_task_new (self, NULL, callback, user_data); + g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_disconnect_context_free); + /* If the primary port is NOT connected (doesn't have to be the data port), * we'll send CGACT there */ if (!mm_port_get_connected (MM_PORT (ctx->primary))) { - mm_dbg ("Sending PDP context deactivation in primary port..."); + mm_obj_dbg (self, "sending PDP context deactivation in primary port..."); mm_base_modem_at_command_full (ctx->modem, ctx->primary, ctx->cgact_command, - 10, + 45, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)cgact_ready, - ctx); + task); return; } @@ -1628,48 +1444,32 @@ disconnect_3gpp (MMBroadbandBearer *self, * driver doesn't support it). */ if (ctx->secondary) { - mm_dbg ("Sending PDP context deactivation in secondary port..."); + mm_obj_dbg (self, "sending PDP context deactivation in secondary port..."); mm_base_modem_at_command_full (ctx->modem, ctx->secondary, ctx->cgact_command, - 10, + 45, FALSE, FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)cgact_ready, - ctx); + task); return; } /* If no secondary port, go on to reopen & flash the data/primary port */ - data_reopen_3gpp (ctx); + data_reopen_3gpp (task); } /*****************************************************************************/ /* DISCONNECT */ -typedef struct { - MMBroadbandBearer *self; - GSimpleAsyncResult *result; - MMPort *data; -} DisconnectContext; - -static void -disconnect_context_complete_and_free (DisconnectContext *ctx) -{ - g_simple_async_result_complete_in_idle (ctx->result); - g_object_unref (ctx->result); - g_object_unref (ctx->data); - g_object_unref (ctx->self); - g_free (ctx); -} - static gboolean disconnect_finish (MMBaseBearer *self, GAsyncResult *res, GError **error) { - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); + return g_task_propagate_boolean (G_TASK (res), error); } static void @@ -1689,56 +1489,46 @@ reset_bearer_connection (MMBroadbandBearer *self) } static void -disconnect_succeeded (DisconnectContext *ctx) -{ - /* Cleanup all connection related data */ - reset_bearer_connection (ctx->self); - - /* Set operation result */ - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - disconnect_context_complete_and_free (ctx); -} - -static void -disconnect_failed (DisconnectContext *ctx, - GError *error) -{ - g_simple_async_result_take_error (ctx->result, error); - disconnect_context_complete_and_free (ctx); -} - -static void disconnect_cdma_ready (MMBroadbandBearer *self, GAsyncResult *res, - DisconnectContext *ctx) + GTask *task) { GError *error = NULL; if (!MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_cdma_finish (self, res, &error)) - disconnect_failed (ctx, error); - else - disconnect_succeeded (ctx); + g_task_return_error (task, error); + else { + /* Cleanup all connection related data */ + reset_bearer_connection (self); + + g_task_return_boolean (task, TRUE); + } + g_object_unref (task); } static void disconnect_3gpp_ready (MMBroadbandBearer *self, GAsyncResult *res, - DisconnectContext *ctx) + GTask *task) { GError *error = NULL; if (!MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp_finish (self, res, &error)) - disconnect_failed (ctx, error); + g_task_return_error (task, error); else { /* Clear CID if we got any set */ - if (ctx->self->priv->cid) - ctx->self->priv->cid = 0; - disconnect_succeeded (ctx); + self->priv->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN; + + /* Cleanup all connection related data */ + reset_bearer_connection (self); + + g_task_return_boolean (task, TRUE); } + g_object_unref (task); } static void @@ -1748,16 +1538,14 @@ disconnect (MMBaseBearer *self, { MMPortSerialAt *primary; MMBaseModem *modem = NULL; - DisconnectContext *ctx; + GTask *task; + + task = g_task_new (self, NULL, callback, user_data); if (!MM_BROADBAND_BEARER (self)->priv->port) { - g_simple_async_report_error_in_idle ( - G_OBJECT (self), - callback, - user_data, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't disconnect: this bearer is not connected"); + mm_obj_dbg (self, "no need to disconnect: bearer is already disconnected"); + g_task_return_boolean (task, TRUE); + g_object_unref (task); return; } @@ -1769,26 +1557,15 @@ disconnect (MMBaseBearer *self, /* We need the primary port to disconnect... */ primary = mm_base_modem_peek_port_primary (modem); if (!primary) { - g_simple_async_report_error_in_idle ( - G_OBJECT (self), - callback, - user_data, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't disconnect: couldn't get primary port"); + g_task_return_new_error (task, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't disconnect: couldn't get primary port"); + g_object_unref (task); g_object_unref (modem); return; } - /* In this context, we only keep the stuff we'll need later */ - ctx = g_new0 (DisconnectContext, 1); - ctx->self = g_object_ref (self); - ctx->data = g_object_ref (MM_BROADBAND_BEARER (self)->priv->port); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - disconnect); - switch (MM_BROADBAND_BEARER (self)->priv->connection_type) { case CONNECTION_TYPE_3GPP: MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp ( @@ -1797,9 +1574,9 @@ disconnect (MMBaseBearer *self, primary, mm_base_modem_peek_port_secondary (modem), MM_BROADBAND_BEARER (self)->priv->port, - MM_BROADBAND_BEARER (self)->priv->cid, + (guint)MM_BROADBAND_BEARER (self)->priv->profile_id, (GAsyncReadyCallback) disconnect_3gpp_ready, - ctx); + task); break; case CONNECTION_TYPE_CDMA: @@ -1810,10 +1587,11 @@ disconnect (MMBaseBearer *self, mm_base_modem_peek_port_secondary (modem), MM_BROADBAND_BEARER (self)->priv->port, (GAsyncReadyCallback) disconnect_cdma_ready, - ctx); + task); break; case CONNECTION_TYPE_NONE: + default: g_assert_not_reached (); } @@ -1821,10 +1599,132 @@ disconnect (MMBaseBearer *self, } /*****************************************************************************/ +/* Connection status monitoring */ + +static MMBearerConnectionStatus +load_connection_status_finish (MMBaseBearer *bearer, + GAsyncResult *res, + GError **error) +{ + GError *inner_error = NULL; + gssize value; + + value = g_task_propagate_int (G_TASK (res), &inner_error); + if (inner_error) { + g_propagate_error (error, inner_error); + return MM_BEARER_CONNECTION_STATUS_UNKNOWN; + } + return (MMBearerConnectionStatus)value; +} + +static void +cgact_periodic_query_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + MMBroadbandBearer *self; + const gchar *response; + GError *error = NULL; + GList *pdp_active_list = NULL; + GList *l; + MMBearerConnectionStatus status = MM_BEARER_CONNECTION_STATUS_UNKNOWN; + + self = MM_BROADBAND_BEARER (g_task_get_source_object (task)); + + response = mm_base_modem_at_command_finish (modem, res, &error); + if (response) + pdp_active_list = mm_3gpp_parse_cgact_read_response (response, &error); + + if (error) { + g_assert (!pdp_active_list); + g_prefix_error (&error, "Couldn't check current list of active PDP contexts: "); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + for (l = pdp_active_list; l; l = g_list_next (l)) { + MM3gppPdpContextActive *pdp_active; + + /* We look for he just assume the first active PDP context found is the one we're + * looking for. */ + pdp_active = (MM3gppPdpContextActive *)(l->data); + if (pdp_active->cid == (guint)self->priv->profile_id) { + status = (pdp_active->active ? MM_BEARER_CONNECTION_STATUS_CONNECTED : MM_BEARER_CONNECTION_STATUS_DISCONNECTED); + break; + } + } + mm_3gpp_pdp_context_active_list_free (pdp_active_list); + + /* PDP context not found? This shouldn't happen, error out */ + if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "PDP context not found in the known contexts list"); + else + g_task_return_int (task, (gssize) status); + g_object_unref (task); +} static void -report_connection_status (MMBaseBearer *self, - MMBearerConnectionStatus status) +load_connection_status (MMBaseBearer *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + MMBaseModem *modem = NULL; + MMPortSerialAt *port; + + task = g_task_new (self, NULL, callback, user_data); + + g_object_get (MM_BASE_BEARER (self), + MM_BASE_BEARER_MODEM, &modem, + NULL); + + /* No connection status checks on CDMA-only */ + if (MM_BROADBAND_BEARER (self)->priv->connection_type == CONNECTION_TYPE_CDMA) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "Couldn't load connection status: unsupported in CDMA"); + g_object_unref (task); + goto out; + } + + /* If CID not defined, error out */ + if (MM_BROADBAND_BEARER (self)->priv->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't load connection status: cid not defined"); + g_object_unref (task); + goto out; + } + + /* If no control port available, error out */ + port = mm_base_modem_peek_best_at_port (modem, NULL); + if (!port) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "Couldn't load connection status: no control port available"); + g_object_unref (task); + goto out; + } + + mm_base_modem_at_command_full (MM_BASE_MODEM (modem), + port, + "+CGACT?", + 3, + FALSE, /* allow cached */ + FALSE, /* raw */ + NULL, /* cancellable */ + (GAsyncReadyCallback) cgact_periodic_query_ready, + task); + +out: + g_clear_object (&modem); +} + +/*****************************************************************************/ + +static void +report_connection_status (MMBaseBearer *self, + MMBearerConnectionStatus status, + const GError *connection_error) { if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) /* Cleanup all connection related data */ @@ -1833,13 +1733,14 @@ report_connection_status (MMBaseBearer *self, /* Chain up parent's report_connection_status() */ MM_BASE_BEARER_CLASS (mm_broadband_bearer_parent_class)->report_connection_status ( self, - status); + status, + connection_error); } /*****************************************************************************/ typedef struct _InitAsyncContext InitAsyncContext; -static void interface_initialization_step (InitAsyncContext *ctx); +static void interface_initialization_step (GTask *task); typedef enum { INITIALIZATION_STEP_FIRST, @@ -1848,28 +1749,19 @@ typedef enum { } InitializationStep; struct _InitAsyncContext { - MMBroadbandBearer *self; - GSimpleAsyncResult *result; - GCancellable *cancellable; MMBaseModem *modem; InitializationStep step; MMPortSerialAt *port; }; static void -init_async_context_free (InitAsyncContext *ctx, - gboolean close_port) +init_async_context_free (InitAsyncContext *ctx) { if (ctx->port) { - if (close_port) - mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); + mm_port_serial_close (MM_PORT_SERIAL (ctx->port)); g_object_unref (ctx->port); } - g_object_unref (ctx->self); g_object_unref (ctx->modem); - g_object_unref (ctx->result); - if (ctx->cancellable) - g_object_unref (ctx->cancellable); g_free (ctx); } @@ -1898,22 +1790,26 @@ initable_init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) { - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); + return g_task_propagate_boolean (G_TASK (result), error); } static void crm_range_ready (MMBaseModem *modem, GAsyncResult *res, - InitAsyncContext *ctx) + GTask *task) { + MMBroadbandModem *self; + InitAsyncContext *ctx; GError *error = NULL; const gchar *response; + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + response = mm_base_modem_at_command_full_finish (modem, res, &error); if (error) { /* We should possibly take this error as fatal. If we were told to use a * specific Rm protocol, we must be able to check if it is supported. */ - g_simple_async_result_take_error (ctx->result, error); } else { MMModemCdmaRmProtocol min = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN; MMModemCdmaRmProtocol max = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN; @@ -1923,13 +1819,14 @@ crm_range_ready (MMBaseModem *modem, &error)) { MMModemCdmaRmProtocol current; - current = mm_bearer_properties_get_rm_protocol (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))); + current = mm_bearer_properties_get_rm_protocol (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); /* Check if value within the range */ if (current >= min && current <= max) { /* Fine, go on with next step */ ctx->step++; - interface_initialization_step (ctx); + interface_initialization_step (task); + return; } g_assert (error == NULL); @@ -1938,29 +1835,33 @@ crm_range_ready (MMBaseModem *modem, "Requested RM protocol '%s' is not supported", mm_modem_cdma_rm_protocol_get_string (current)); } - /* Failed, set as fatal as well */ - g_simple_async_result_take_error (ctx->result, error); } - g_simple_async_result_complete (ctx->result); - init_async_context_free (ctx, TRUE); + g_task_return_error (task, error); + g_object_unref (task); } static void -interface_initialization_step (InitAsyncContext *ctx) +interface_initialization_step (GTask *task) { + MMBroadbandModem *self; + InitAsyncContext *ctx; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + switch (ctx->step) { case INITIALIZATION_STEP_FIRST: - /* Fall down to next step */ ctx->step++; + /* Fall through */ case INITIALIZATION_STEP_CDMA_RM_PROTOCOL: /* If a specific RM protocol is given, we need to check whether it is * supported. */ if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->modem)) && mm_bearer_properties_get_rm_protocol ( - mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))) != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) { + mm_base_bearer_peek_config (MM_BASE_BEARER (self))) != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) { mm_base_modem_at_command_full (ctx->modem, ctx->port, "+CRM=?", @@ -1969,19 +1870,21 @@ interface_initialization_step (InitAsyncContext *ctx) FALSE, /* raw */ NULL, /* cancellable */ (GAsyncReadyCallback)crm_range_ready, - ctx); + task); return; } - /* Fall down to next step */ ctx->step++; + /* Fall through */ case INITIALIZATION_STEP_LAST: /* We are done without errors! */ - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - g_simple_async_result_complete_in_idle (ctx->result); - init_async_context_free (ctx, TRUE); + g_task_return_boolean (task, TRUE); + g_object_unref (task); return; + + default: + g_assert_not_reached (); } g_assert_not_reached (); @@ -1995,62 +1898,103 @@ initable_init_async (GAsyncInitable *initable, gpointer user_data) { InitAsyncContext *ctx; + GTask *task; GError *error = NULL; ctx = g_new0 (InitAsyncContext, 1); - ctx->self = g_object_ref (initable); - ctx->result = g_simple_async_result_new (G_OBJECT (initable), - callback, - user_data, - initable_init_async); - ctx->cancellable = (cancellable ? - g_object_ref (cancellable) : - NULL); - g_object_get (initable, MM_BASE_BEARER_MODEM, &ctx->modem, NULL); + task = g_task_new (MM_BROADBAND_BEARER (initable), + cancellable, + callback, + user_data); + g_task_set_task_data (task, ctx, (GDestroyNotify)init_async_context_free); + ctx->port = mm_base_modem_get_port_primary (ctx->modem); if (!ctx->port) { - g_simple_async_result_set_error (ctx->result, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't get primary port"); - g_simple_async_result_complete_in_idle (ctx->result); - init_async_context_free (ctx, FALSE); + g_task_return_new_error (task, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't get primary port"); + g_object_unref (task); return; } if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error)) { - g_simple_async_result_take_error (ctx->result, error); - g_simple_async_result_complete_in_idle (ctx->result); - init_async_context_free (ctx, FALSE); + g_clear_object (&ctx->port); + g_task_return_error (task, error); + g_object_unref (task); return; } - interface_initialization_step (ctx); + interface_initialization_step (task); } void mm_broadband_bearer_new (MMBroadbandModem *modem, - MMBearerProperties *properties, + MMBearerProperties *bearer_properties, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + MMFlowControl flow_control; + + /* Inherit flow control from modem object directly */ + g_object_get (modem, + MM_BROADBAND_MODEM_FLOW_CONTROL, &flow_control, + NULL); + g_async_initable_new_async ( MM_TYPE_BROADBAND_BEARER, G_PRIORITY_DEFAULT, cancellable, callback, user_data, - MM_BASE_BEARER_MODEM, modem, - MM_BASE_BEARER_CONFIG, properties, + MM_BASE_BEARER_MODEM, modem, + MM_BASE_BEARER_CONFIG, bearer_properties, + MM_BROADBAND_BEARER_FLOW_CONTROL, flow_control, NULL); } static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MMBroadbandBearer *self = MM_BROADBAND_BEARER (object); + + switch (prop_id) { + case PROP_FLOW_CONTROL: + self->priv->flow_control = g_value_get_flags (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MMBroadbandBearer *self = MM_BROADBAND_BEARER (object); + + switch (prop_id) { + case PROP_FLOW_CONTROL: + g_value_set_flags (value, self->priv->flow_control); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void mm_broadband_bearer_init (MMBroadbandBearer *self) { /* Initialize private data */ @@ -2060,6 +2004,7 @@ mm_broadband_bearer_init (MMBroadbandBearer *self) /* Set defaults */ self->priv->connection_type = CONNECTION_TYPE_NONE; + self->priv->flow_control = MM_FLOW_CONTROL_NONE; } static void @@ -2087,14 +2032,21 @@ mm_broadband_bearer_class_init (MMBroadbandBearerClass *klass) g_type_class_add_private (object_class, sizeof (MMBroadbandBearerPrivate)); - /* Virtual methods */ - object_class->dispose = dispose; + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; base_bearer_class->connect = connect; base_bearer_class->connect_finish = connect_finish; base_bearer_class->disconnect = disconnect; base_bearer_class->disconnect_finish = disconnect_finish; base_bearer_class->report_connection_status = report_connection_status; + base_bearer_class->load_connection_status = load_connection_status; + base_bearer_class->load_connection_status_finish = load_connection_status_finish; +#if defined WITH_SYSTEMD_SUSPEND_RESUME + base_bearer_class->reload_connection_status = load_connection_status; + base_bearer_class->reload_connection_status_finish = load_connection_status_finish; +#endif klass->connect_3gpp = connect_3gpp; klass->connect_3gpp_finish = detailed_connect_finish; @@ -2108,4 +2060,13 @@ mm_broadband_bearer_class_init (MMBroadbandBearerClass *klass) klass->disconnect_3gpp_finish = detailed_disconnect_finish; klass->disconnect_cdma = disconnect_cdma; klass->disconnect_cdma_finish = detailed_disconnect_finish; + + properties[PROP_FLOW_CONTROL] = + g_param_spec_flags (MM_BROADBAND_BEARER_FLOW_CONTROL, + "Flow control", + "Flow control settings to use during connection", + MM_TYPE_FLOW_CONTROL, + MM_FLOW_CONTROL_NONE, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_FLOW_CONTROL, properties[PROP_FLOW_CONTROL]); } |