[PATCH 5/5] mbm: port dial_3gpp() and disconnect_3gpp() to GTask
Aleksander Morgado
aleksander at aleksander.es
Tue Nov 21 18:24:40 UTC 2017
The two connection and disconnection methods are ported to GTask, and
are also updated so that the reception of the unsolicited message
reporting either connect/disconnection is able to right away complete
the pending connection/disconnection attempts, as done in other
plugins like the Icera or HSO ones.
---
plugins/mbm/mm-broadband-bearer-mbm.c | 692 +++++++++++++++++++---------------
1 file changed, 382 insertions(+), 310 deletions(-)
diff --git a/plugins/mbm/mm-broadband-bearer-mbm.c b/plugins/mbm/mm-broadband-bearer-mbm.c
index d26802f0..c4795aa1 100644
--- a/plugins/mbm/mm-broadband-bearer-mbm.c
+++ b/plugins/mbm/mm-broadband-bearer-mbm.c
@@ -13,13 +13,14 @@
* Copyright (C) 2008 - 2010 Ericsson AB
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Lanedo GmbH
+ * Copyright (C) 2017 Aleksander Morgado <aleksander at aleksander.es>
*
* Author: Per Hallsmark <per.hallsmark at ericsson.com>
* Bjorn Runaker <bjorn.runaker at ericsson.com>
* Torgny Johansson <torgny.johansson at ericsson.com>
* Jonas Sjöquist <jonas.sjoquist at ericsson.com>
* Dan Williams <dcbw at redhat.com>
- * Aleksander Morgado <aleksander at lanedo.com>
+ * Aleksander Morgado <aleksander at aleksander.es>
*/
#include <config.h>
@@ -42,229 +43,208 @@
#include "mm-modem-helpers-mbm.h"
#include "mm-daemon-enums-types.h"
-G_DEFINE_TYPE (MMBroadbandBearerMbm, mm_broadband_bearer_mbm, MM_TYPE_BROADBAND_BEARER);
+G_DEFINE_TYPE (MMBroadbandBearerMbm, mm_broadband_bearer_mbm, MM_TYPE_BROADBAND_BEARER)
struct _MMBroadbandBearerMbmPrivate {
- gpointer connect_pending;
- gpointer disconnect_pending;
+ GTask *connect_pending;
+ GTask *disconnect_pending;
};
-/*****************************************************************************/
-
-static void dial_3gpp_report_connection_status (gpointer data,
- MMBearerConnectionStatus status);
-static void disconnect_report_connection_status (gpointer data,
- MMBearerConnectionStatus status);
-
-static void
-report_connection_status (MMBaseBearer *bearer,
- MMBearerConnectionStatus status)
-{
- MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (bearer);
-
- g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED ||
- status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
-
- mm_dbg ("Received unsolicited *E2NAP (%s)",
- mm_bearer_connection_status_get_string (status));
-
- if (self->priv->connect_pending) {
- /* Save unsolicited status for the pending connection attempt */
- dial_3gpp_report_connection_status (self->priv->connect_pending, status);
- } else if (self->priv->disconnect_pending) {
- /* Save unsolicited status for the pending disconnection attempt */
- disconnect_report_connection_status (self->priv->disconnect_pending, status);
- } else {
- if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
- MM_BASE_BEARER_CLASS (mm_broadband_bearer_mbm_parent_class)->report_connection_status (
- bearer,
- status);
- }
- }
-}
-
/*****************************************************************************/
/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
typedef struct {
- MMBroadbandBearerMbm *self;
- MMBaseModem *modem;
+ MMBaseModem *modem;
MMPortSerialAt *primary;
- guint cid;
- GCancellable *cancellable;
- MMPort *data;
- GSimpleAsyncResult *result;
- guint poll_count;
- guint poll_id;
- MMBearerConnectionStatus e2nap_status;
+ guint cid;
+ MMPort *data;
+ guint poll_count;
+ guint poll_id;
+ GError *saved_error;
} Dial3gppContext;
static void
-dial_3gpp_context_complete_and_free (Dial3gppContext *ctx)
+dial_3gpp_context_free (Dial3gppContext *ctx)
{
- /* Clear bearer object pointer to this connect context */
- if (ctx->self->priv->connect_pending == ctx)
- ctx->self->priv->connect_pending = NULL;
-
- g_simple_async_result_complete_in_idle (ctx->result);
+ g_assert (!ctx->poll_id);
+ g_assert (!ctx->saved_error);
g_clear_object (&ctx->data);
- if (ctx->poll_id)
- g_source_remove (ctx->poll_id);
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->result);
- g_object_unref (ctx->primary);
- g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
+ g_clear_object (&ctx->primary);
+ g_clear_object (&ctx->modem);
g_slice_free (Dial3gppContext, ctx);
}
static MMPort *
-dial_3gpp_finish (MMBroadbandBearer *self,
- GAsyncResult *res,
- GError **error)
+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 MM_PORT (g_task_propagate_pointer (G_TASK (res), error));
}
static void
-dial_3gpp_report_connection_status (gpointer data,
- MMBearerConnectionStatus status)
+connect_reset_ready (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GTask *task)
{
- Dial3gppContext *ctx = data;
+ Dial3gppContext *ctx;
- g_assert (ctx);
- ctx->e2nap_status = status;
-}
+ ctx = g_task_get_task_data (task);
-static void
-connect_error_disconnect_ready (MMBroadbandBearer *self,
- GAsyncResult *res,
- Dial3gppContext *ctx)
-{
- MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp_finish (
- self,
- res,
- NULL);
- dial_3gpp_context_complete_and_free (ctx);
+ MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp_finish (self, res, NULL);
+
+ /* When reset is requested, it was either cancelled or an error was stored */
+ if (!g_task_return_error_if_cancelled (task)) {
+ g_assert (ctx->saved_error);
+ g_task_return_error (task, ctx->saved_error);
+ ctx->saved_error = NULL;
+ }
+
+ g_object_unref (task);
}
static void
-connect_error_disconnect_start (Dial3gppContext *ctx)
+connect_reset (GTask *task)
{
- /* We don't care about connect status anymore */
- if (ctx->self->priv->connect_pending == ctx)
- ctx->self->priv->connect_pending = NULL;
+ MMBroadbandBearerMbm *self;
+ Dial3gppContext *ctx;
- MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->disconnect_3gpp (
- MM_BROADBAND_BEARER (ctx->self),
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp (
+ MM_BROADBAND_BEARER (self),
MM_BROADBAND_MODEM (ctx->modem),
ctx->primary,
NULL,
ctx->data,
ctx->cid,
- (GAsyncReadyCallback) connect_error_disconnect_ready,
- ctx);
+ (GAsyncReadyCallback) connect_reset_ready,
+ task);
}
-static gboolean
-handle_e2nap_connect_status (Dial3gppContext *ctx)
+static void
+process_pending_connect_attempt (MMBroadbandBearerMbm *self,
+ MMBearerConnectionStatus status)
{
- switch (ctx->e2nap_status) {
- case MM_BEARER_CONNECTION_STATUS_CONNECTED:
- /* Reporting connected */
- mm_dbg ("Connected status indicated already by an unsolicited message");
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- g_object_ref (ctx->data),
- g_object_unref);
- dial_3gpp_context_complete_and_free (ctx);
- return TRUE;
- case MM_BEARER_CONNECTION_STATUS_DISCONNECTED:
- /* Reporting disconnected */
- mm_dbg ("Connection failure status indicated already by an unsolicited message");
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Call setup failed");
- dial_3gpp_context_complete_and_free (ctx);
- return TRUE;
- default:
- break;
+ GTask *task;
+ Dial3gppContext *ctx;
+
+ /* Recover connection task */
+ task = self->priv->connect_pending;
+ self->priv->connect_pending = NULL;
+ g_assert (task != NULL);
+
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->poll_id) {
+ g_source_remove (ctx->poll_id);
+ ctx->poll_id = 0;
}
- return FALSE;
+ /* Received 'CONNECTED' during a connection attempt? */
+ if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED) {
+ /* If we wanted to get cancelled before, do it now. */
+ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
+ connect_reset (task);
+ return;
+ }
+
+ g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref);
+ g_object_unref (task);
+ return;
+ }
+
+ /* If we wanted to get cancelled before and now we couldn't connect,
+ * use the cancelled error and return */
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
+ return;
+ }
+
+ /* Otherwise, received 'DISCONNECTED' during a connection attempt? */
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Call setup failed");
+ g_object_unref (task);
}
-static gboolean connect_poll_cb (Dial3gppContext *ctx);
+static gboolean connect_poll_cb (MMBroadbandBearerMbm *self);
static void
-connect_poll_ready (MMBaseModem *modem,
- GAsyncResult *res,
- Dial3gppContext *ctx)
+connect_poll_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ MMBroadbandBearerMbm *self)
{
- GError *error = NULL;
- const gchar *response;
- guint state;
+ GTask *task;
+ Dial3gppContext *ctx;
+ GError *error = NULL;
+ const gchar *response;
+ guint state;
+
+ task = self->priv->connect_pending;
+ self->priv->connect_pending = NULL;
+
+ if (!task) {
+ mm_dbg ("Connection context was finished already by an unsolicited message");
+ /* Run _finish() to finalize the async call, even if we don't care
+ * the result */
+ mm_base_modem_at_command_full_finish (modem, res, NULL);
+ return;
+ }
+
+ ctx = g_task_get_task_data (task);
response = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!response) {
- g_simple_async_result_take_error (ctx->result, error);
- connect_error_disconnect_start (ctx);
+ ctx->saved_error = error;
+ connect_reset (task);
return;
}
- if (sscanf (response, "*ENAP: %d", &state) == 1
- && state == 1) {
+ if (sscanf (response, "*ENAP: %d", &state) == 1 && state == 1) {
/* Success! Connected... */
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- g_object_ref (ctx->data),
- g_object_unref);
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref);
+ g_object_unref (task);
return;
}
- /* Process any unsolicited E2NAP disconnect notification */
- if (handle_e2nap_connect_status (ctx))
- return;
-
- /* Check again in one second */
+ /* Restore pending task and check again in one second */
+ self->priv->connect_pending = task;
g_assert (ctx->poll_id == 0);
- ctx->poll_id = g_timeout_add_seconds (1,
- (GSourceFunc)connect_poll_cb,
- ctx);
+ ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc) connect_poll_cb, self);
}
static gboolean
-connect_poll_cb (Dial3gppContext *ctx)
+connect_poll_cb (MMBroadbandBearerMbm *self)
{
+ GTask *task;
+ Dial3gppContext *ctx;
+
+ task = self->priv->connect_pending;
+ self->priv->connect_pending = NULL;
+
+ g_assert (task);
+ ctx = g_task_get_task_data (task);
+
ctx->poll_id = 0;
/* Complete if we were cancelled */
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Dial operation has been cancelled");
- connect_error_disconnect_start (ctx);
+ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
+ connect_reset (task);
return G_SOURCE_REMOVE;
}
- /* Process any unsolicited E2NAP status */
- if (handle_e2nap_connect_status (ctx))
- return G_SOURCE_REMOVE;
-
/* Too many retries... */
if (ctx->poll_count > 50) {
- g_simple_async_result_set_error (ctx->result,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
- "Connection attempt timed out");
- connect_error_disconnect_start (ctx);
+ g_assert (!ctx->saved_error);
+ ctx->saved_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
+ "Connection attempt timed out");
+ connect_reset (task);
return G_SOURCE_REMOVE;
}
+ /* Restore pending task and poll */
+ self->priv->connect_pending = task;
ctx->poll_count++;
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
@@ -272,49 +252,69 @@ connect_poll_cb (Dial3gppContext *ctx)
3,
FALSE,
FALSE, /* raw */
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)connect_poll_ready,
- ctx);
+ self);
return G_SOURCE_REMOVE;
}
static void
-activate_ready (MMBaseModem *modem,
- GAsyncResult *res,
- Dial3gppContext *ctx)
+activate_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ MMBroadbandBearerMbm *self)
{
- GError *error = NULL;
+ GTask *task;
+ Dial3gppContext *ctx;
+ GError *error = NULL;
+
+ /* Try to recover the connection context. If none found, it means the
+ * context was already completed and we have nothing else to do. */
+ task = self->priv->connect_pending;
+ self->priv->connect_pending = NULL;
+
+ if (!task) {
+ mm_dbg ("Connection context was finished already by an unsolicited message");
+ /* Run _finish() to finalize the async call, even if we don't care
+ * the result */
+ mm_base_modem_at_command_full_finish (modem, res, NULL);
+ goto out;
+ }
/* From now on, if we get cancelled, we'll need to run the connection
* reset ourselves just in case */
-
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- connect_error_disconnect_start (ctx);
- return;
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
}
- /* Process any unsolicited E2NAP status received before the ENAP OK */
- if (handle_e2nap_connect_status (ctx))
- return;
+ ctx = g_task_get_task_data (task);
/* No unsolicited E2NAP status yet; wait for it and periodically poll
* to handle very old F3507g/MD300 firmware that may not send E2NAP. */
- ctx->poll_id = g_timeout_add_seconds (1,
- (GSourceFunc)connect_poll_cb,
- ctx);
+ self->priv->connect_pending = task;
+ ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc)connect_poll_cb, self);
+
+ out:
+ /* Balance refcount with the extra ref we passed to command_full() */
+ g_object_unref (self);
}
static void
-activate (Dial3gppContext *ctx)
+activate (GTask *task)
{
- gchar *command;
+ MMBroadbandBearerMbm *self;
+ Dial3gppContext *ctx;
+ gchar *command;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
/* The unsolicited response to ENAP may come before the OK does.
* We will keep the connection context in the bearer private data so
* that it is accessible from the unsolicited message handler. */
- g_assert (ctx->self->priv->connect_pending == NULL);
- ctx->self->priv->connect_pending = ctx;
+ g_assert (self->priv->connect_pending == NULL);
+ self->priv->connect_pending = task;
/* Activate the PDP context and start the data session */
command = g_strdup_printf ("AT*ENAP=1,%d", ctx->cid);
@@ -324,36 +324,41 @@ activate (Dial3gppContext *ctx)
3,
FALSE,
FALSE, /* raw */
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)activate_ready,
- ctx);
+ g_object_ref (self)); /* we pass the bearer object! */
g_free (command);
}
static void
-authenticate_ready (MMBaseModem *modem,
+authenticate_ready (MMBaseModem *modem,
GAsyncResult *res,
- Dial3gppContext *ctx)
+ GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
- 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;
}
- activate (ctx);
+ activate (task);
}
static void
-authenticate (Dial3gppContext *ctx)
+authenticate (GTask *task)
{
- const gchar *user;
- const gchar *password;
+ MMBroadbandBearerMbm *self;
+ Dial3gppContext *ctx;
+ const gchar *user;
+ const gchar *password;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
- password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
+ user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
/* Both user and password are required; otherwise firmware returns an error */
if (user || password) {
@@ -379,55 +384,52 @@ authenticate (Dial3gppContext *ctx)
3,
FALSE,
FALSE, /* raw */
- ctx->cancellable,
- (GAsyncReadyCallback)authenticate_ready,
- ctx);
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) authenticate_ready,
+ task);
g_free (command);
return;
}
mm_dbg ("Authentication not needed");
- activate (ctx);
+ activate (task);
}
static void
-dial_3gpp (MMBroadbandBearer *self,
- MMBaseModem *modem,
- MMPortSerialAt *primary,
- guint cid,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+dial_3gpp (MMBroadbandBearer *_self,
+ MMBaseModem *modem,
+ MMPortSerialAt *primary,
+ guint cid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- Dial3gppContext *ctx;
+ MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (_self);
+ GTask *task;
+ Dial3gppContext *ctx;
g_assert (primary != NULL);
+ task = g_task_new (self, cancellable, callback, user_data);
+
ctx = g_slice_new0 (Dial3gppContext);
- ctx->self = g_object_ref (self);
- ctx->modem = g_object_ref (modem);
+ ctx->modem = g_object_ref (modem);
ctx->primary = g_object_ref (primary);
- ctx->cid = cid;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- dial_3gpp);
- ctx->cancellable = g_object_ref (cancellable);
- ctx->poll_count = 0;
+ ctx->cid = cid;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)dial_3gpp_context_free);
/* We need a net data port */
ctx->data = mm_base_modem_get_best_data_port (modem, MM_PORT_TYPE_NET);
if (!ctx->data) {
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_NOT_FOUND,
- "No valid data port found to launch connection");
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No valid data port found to launch connection");
+ g_object_unref (task);
return;
}
- authenticate (ctx);
+ authenticate (task);
}
/*****************************************************************************/
@@ -569,116 +571,135 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
/* 3GPP disconnect */
typedef struct {
- MMBroadbandBearerMbm *self;
- MMBaseModem *modem;
+ MMBaseModem *modem;
MMPortSerialAt *primary;
- GSimpleAsyncResult *result;
- guint poll_count;
- guint poll_id;
- MMBearerConnectionStatus e2nap_status;
+ guint poll_count;
+ guint poll_id;
} DisconnectContext;
static void
-disconnect_context_complete_and_free (DisconnectContext *ctx)
+disconnect_context_free (DisconnectContext *ctx)
{
- /* Clear bearer object pointer to this disconnect context */
- if (ctx->self->priv->disconnect_pending == ctx)
- ctx->self->priv->disconnect_pending = NULL;
-
- g_simple_async_result_complete_in_idle (ctx->result);
- if (ctx->poll_id)
- g_source_remove (ctx->poll_id);
- g_object_unref (ctx->result);
- g_object_unref (ctx->primary);
- g_object_unref (ctx->self);
- g_object_unref (ctx->modem);
+ g_assert (!ctx->poll_id);
+ g_clear_object (&ctx->primary);
+ g_clear_object (&ctx->modem);
g_free (ctx);
}
static gboolean
-disconnect_3gpp_finish (MMBroadbandBearer *self,
- GAsyncResult *res,
- GError **error)
+disconnect_3gpp_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
-disconnect_report_connection_status (gpointer data,
- MMBearerConnectionStatus status)
+process_pending_disconnect_attempt (MMBroadbandBearerMbm *self,
+ MMBearerConnectionStatus status)
{
- DisconnectContext *ctx = data;
+ GTask *task;
+ DisconnectContext *ctx;
- g_assert (ctx);
- ctx->e2nap_status = status;
-}
+ /* Recover disconnection task */
+ task = self->priv->disconnect_pending;
+ self->priv->disconnect_pending = NULL;
+ g_assert (task != NULL);
-static gboolean
-handle_e2nap_disconnect_status (DisconnectContext *ctx)
-{
- if (ctx->e2nap_status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
- /* Reporting disconnected */
- mm_dbg ("Connection disconnect indicated already by an unsolicited message");
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disconnect_context_complete_and_free (ctx);
- return TRUE;
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->poll_id) {
+ g_source_remove (ctx->poll_id);
+ ctx->poll_id = 0;
}
- return FALSE;
+ /* Received 'DISCONNECTED' during a disconnection attempt? */
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
+ mm_dbg ("Connection disconnect indicated by an unsolicited message");
+ g_task_return_boolean (task, TRUE);
+ } else {
+ /* Otherwise, report error */
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Disconnection failed");
+ }
+ g_object_unref (task);
}
-static gboolean disconnect_poll_cb (DisconnectContext *ctx);
+static gboolean disconnect_poll_cb (MMBroadbandBearerMbm *self);
static void
-disconnect_poll_ready (MMBaseModem *modem,
- GAsyncResult *res,
- DisconnectContext *ctx)
+disconnect_poll_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ MMBroadbandBearerMbm *self)
+
{
- GError *error = NULL;
- const gchar *response;
- guint state;
+ GTask *task;
+ DisconnectContext *ctx;
+ GError *error = NULL;
+ const gchar *response;
+ guint state;
+
+ task = self->priv->disconnect_pending;
+ self->priv->disconnect_pending = NULL;
+
+ if (!task) {
+ mm_dbg ("Disconnection context was finished already by an unsolicited message");
+ /* Run _finish() to finalize the async call, even if we don't care
+ * the result */
+ mm_base_modem_at_command_full_finish (modem, res, NULL);
+ goto out;
+ }
response = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!response) {
- g_simple_async_result_take_error (ctx->result, error);
- disconnect_context_complete_and_free (ctx);
- return;
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
}
- if (sscanf (response, "*ENAP: %d", &state) == 1
- && state == 0) {
+ if (sscanf (response, "*ENAP: %d", &state) == 1 && state == 0) {
/* Disconnected */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disconnect_context_complete_and_free (ctx);
- return;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ goto out;
}
- /* Check again in one second */
+ /* Restore pending task and check in 1s */
+ self->priv->disconnect_pending = task;
+ ctx = g_task_get_task_data (task);
g_assert (ctx->poll_id == 0);
- ctx->poll_id = g_timeout_add_seconds (1,
- (GSourceFunc)disconnect_poll_cb,
- ctx);
+ ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc) disconnect_poll_cb, self);
+
+ out:
+ /* Balance refcount with the extra ref we passed to command_full() */
+ g_object_unref (self);
}
static gboolean
-disconnect_poll_cb (DisconnectContext *ctx)
+disconnect_poll_cb (MMBroadbandBearerMbm *self)
{
- ctx->poll_id = 0;
+ GTask *task;
+ DisconnectContext *ctx;
- /* Process any unsolicited E2NAP status */
- if (handle_e2nap_disconnect_status (ctx))
- return G_SOURCE_REMOVE;
+ task = self->priv->disconnect_pending;
+ self->priv->disconnect_pending = NULL;
+
+ g_assert (task);
+ ctx = g_task_get_task_data (task);
+
+ ctx->poll_id = 0;
/* Too many retries... */
if (ctx->poll_count > 20) {
- g_simple_async_result_set_error (ctx->result,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
- "Disconnection attempt timed out");
- disconnect_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
+ "Disconnection attempt timed out");
+ g_object_unref (task);
return G_SOURCE_REMOVE;
}
+ /* Restore pending task and poll */
+ self->priv->disconnect_pending = task;
ctx->poll_count++;
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
@@ -687,17 +708,31 @@ disconnect_poll_cb (DisconnectContext *ctx)
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
- (GAsyncReadyCallback)disconnect_poll_ready,
- ctx);
+ (GAsyncReadyCallback) disconnect_poll_ready,
+ g_object_ref (self)); /* we pass the bearer object! */
return G_SOURCE_REMOVE;
}
static void
-disconnect_enap_ready (MMBaseModem *modem,
- GAsyncResult *res,
- DisconnectContext *ctx)
+disconnect_enap_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ MMBroadbandBearerMbm *self)
{
- GError *error = NULL;
+ DisconnectContext *ctx;
+ GTask *task;
+ GError *error = NULL;
+
+ task = self->priv->disconnect_pending;
+ self->priv->disconnect_pending = NULL;
+
+ /* Try to recover the disconnection context. If none found, it means the
+ * context was already completed and we have nothing else to do. */
+ if (!task) {
+ mm_base_modem_at_command_full_finish (modem, res, NULL);
+ goto out;
+ }
+
+ ctx = g_task_get_task_data (task);
/* Ignore errors for now */
mm_base_modem_at_command_full_finish (modem, res, &error);
@@ -706,45 +741,44 @@ disconnect_enap_ready (MMBaseModem *modem,
g_error_free (error);
}
- /* Process any unsolicited E2NAP status received before the ENAP OK */
- if (handle_e2nap_disconnect_status (ctx))
- return;
-
/* No unsolicited E2NAP status yet; wait for it and periodically poll
* to handle very old F3507g/MD300 firmware that may not send E2NAP. */
- ctx->poll_id = g_timeout_add_seconds (1,
- (GSourceFunc)disconnect_poll_cb,
- ctx);
+ self->priv->disconnect_pending = task;
+ ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc)disconnect_poll_cb, self);
+
+ out:
+ /* Balance refcount with the extra ref we passed to command_full() */
+ g_object_unref (self);
}
static void
-disconnect_3gpp (MMBroadbandBearer *self,
- MMBroadbandModem *modem,
- MMPortSerialAt *primary,
- MMPortSerialAt *secondary,
- MMPort *data,
- guint cid,
- GAsyncReadyCallback callback,
- gpointer user_data)
+disconnect_3gpp (MMBroadbandBearer *_self,
+ MMBroadbandModem *modem,
+ MMPortSerialAt *primary,
+ MMPortSerialAt *secondary,
+ MMPort *data,
+ guint cid,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- DisconnectContext *ctx;
+ MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (_self);
+ GTask *task;
+ DisconnectContext *ctx;
g_assert (primary != NULL);
+ task = g_task_new (self, NULL, callback, user_data);
+
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);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) disconnect_context_free);
/* The unsolicited response to ENAP may come before the OK does.
* We will keep the disconnection context in the bearer private data so
* that it is accessible from the unsolicited message handler. */
- g_assert (ctx->self->priv->disconnect_pending == NULL);
- ctx->self->priv->disconnect_pending = ctx;
+ g_assert (self->priv->disconnect_pending == NULL);
+ self->priv->disconnect_pending = task;
mm_base_modem_at_command_full (MM_BASE_MODEM (modem),
primary,
@@ -754,7 +788,45 @@ disconnect_3gpp (MMBroadbandBearer *self,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)disconnect_enap_ready,
- ctx);
+ g_object_ref (self)); /* we pass the bearer object! */
+}
+
+/*****************************************************************************/
+
+static void
+report_connection_status (MMBaseBearer *_self,
+ MMBearerConnectionStatus status)
+{
+ MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (_self);
+
+ g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED ||
+ status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED ||
+ status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+
+ /* Process pending connection attempt */
+ if (self->priv->connect_pending) {
+ process_pending_connect_attempt (self, status);
+ return;
+ }
+
+ /* Process pending disconnection attempt */
+ if (self->priv->disconnect_pending) {
+ process_pending_disconnect_attempt (self, status);
+ return;
+ }
+
+ mm_dbg ("Received spontaneous E2NAP (%s)",
+ mm_bearer_connection_status_get_string (status));
+
+ /* Received a random 'DISCONNECTED'...*/
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED ||
+ status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED) {
+ /* If no connection/disconnection attempt on-going, make sure we mark ourselves as
+ * disconnected. Make sure we only pass 'DISCONNECTED' to the parent */
+ MM_BASE_BEARER_CLASS (mm_broadband_bearer_mbm_parent_class)->report_connection_status (
+ _self,
+ MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ }
}
/*****************************************************************************/
--
2.15.0
More information about the ModemManager-devel
mailing list