aboutsummaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@lanedo.com>2012-03-16 10:09:57 +0100
committerAleksander Morgado <aleksander@lanedo.com>2012-03-16 14:53:26 +0100
commit7de8b588b630e25d54cf4f6b0a7323fe4478f3dc (patch)
tree64a2087c399da4605ff9be76b415eb3a4173534a /plugins
parentfe13d4cb66977e8641315683254e685b0b80584f (diff)
hso: setup connection sequence
Diffstat (limited to 'plugins')
-rw-r--r--plugins/option/mm-broadband-bearer-hso.c369
-rw-r--r--plugins/option/mm-broadband-bearer-hso.h10
2 files changed, 379 insertions, 0 deletions
diff --git a/plugins/option/mm-broadband-bearer-hso.c b/plugins/option/mm-broadband-bearer-hso.c
index 377a0954..37192540 100644
--- a/plugins/option/mm-broadband-bearer-hso.c
+++ b/plugins/option/mm-broadband-bearer-hso.c
@@ -46,8 +46,373 @@ static GParamSpec *properties[PROP_LAST];
struct _MMBroadbandBearerHsoPrivate {
gchar *user;
gchar *password;
+ guint auth_idx;
+
+ gpointer connect_pending;
+ guint connect_pending_id;
+ gulong connect_cancellable_id;
+};
+
+/*****************************************************************************/
+/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
+
+typedef struct {
+ MMBroadbandBearerHso *self;
+ MMBaseModem *modem;
+ MMAtSerialPort *primary;
+ guint cid;
+ GCancellable *cancellable;
+ GSimpleAsyncResult *result;
+ guint auth_idx;
+} Dial3gppContext;
+
+static Dial3gppContext *
+dial_3gpp_context_new (MMBroadbandBearerHso *self,
+ MMBaseModem *modem,
+ MMAtSerialPort *primary,
+ guint cid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Dial3gppContext *ctx;
+
+ ctx = g_new0 (Dial3gppContext, 1);
+ ctx->self = g_object_ref (self);
+ 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_context_new);
+ ctx->cancellable = g_object_ref (cancellable);
+
+ /* Always start with the index that worked last time
+ * (will be 0 the first time)*/
+ ctx->auth_idx = self->priv->auth_idx;
+
+ return ctx;
+}
+
+static void
+dial_3gpp_context_complete_and_free (Dial3gppContext *ctx)
+{
+ g_simple_async_result_complete (ctx->result);
+ 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_free (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 gboolean
+dial_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+void
+mm_broadband_bearer_hso_report_connection_status (MMBroadbandBearerHso *self,
+ MMBroadbandBearerHsoConnectionStatus status)
+{
+ Dial3gppContext *ctx;
+
+ /* Recover context (if any) and remove both cancellation and timeout (if any)*/
+ ctx = self->priv->connect_pending;
+ self->priv->connect_pending = NULL;
+
+ if (self->priv->connect_pending_id) {
+ g_source_remove (self->priv->connect_pending_id);
+ self->priv->connect_pending_id = 0;
+ }
+
+ if (self->priv->connect_cancellable_id) {
+ g_cancellable_disconnect (ctx->cancellable,
+ self->priv->connect_cancellable_id);
+ self->priv->connect_cancellable_id = 0;
+ }
+
+ switch (status) {
+ case MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_UNKNOWN:
+ g_warn_if_reached ();
+ break;
+
+ case MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_CONNECTED:
+ if (!ctx)
+ break;
+
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ dial_3gpp_context_complete_and_free (ctx);
+ return;
+
+ case MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_CONNECTION_FAILED:
+ if (!ctx)
+ break;
+
+ 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;
+
+ case MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_DISCONNECTED:
+ if (!ctx) {
+ 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);
+ } else {
+ /* Just ensure we mark ourselves as being disconnected... */
+ g_object_set (self,
+ MM_BEARER_STATUS, MM_BEARER_STATUS_DISCONNECTED,
+ NULL);
+ }
+ break;
+ }
+}
+
+static void
+connect_reset_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ Dial3gppContext *ctx)
+{
+ mm_base_modem_at_command_full_finish (modem, res, NULL);
+
+ /* error should have already been set in the simple async result */
+ dial_3gpp_context_complete_and_free (ctx);
+}
+
+static void
+connect_reset (Dial3gppContext *ctx)
+{
+ gchar *command;
+
+ /* Need to reset the connection attempt */
+ command = g_strdup_printf ("AT_OWANCALL=%d,0,1",
+ ctx->cid);
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)connect_reset_ready,
+ ctx);
+ g_free (command);
+}
+
+static gboolean
+connect_timed_out_cb (MMBroadbandBearerHso *self)
+{
+ Dial3gppContext *ctx;
+
+ /* Recover context and remove cancellation */
+ ctx = self->priv->connect_pending;
+
+ g_cancellable_disconnect (ctx->cancellable,
+ self->priv->connect_cancellable_id);
+
+ self->priv->connect_pending = NULL;
+ self->priv->connect_pending_id = 0;
+ self->priv->connect_cancellable_id = 0;
+
+ g_simple_async_result_set_error (ctx->result,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
+ "Connection attempt timed out");
+ connect_reset (ctx);
+
+ return FALSE;
+}
+
+static void
+connect_cancelled_cb (GCancellable *cancellable,
+ MMBroadbandBearerHso *self)
+{
+ GError *error = NULL;
+ Dial3gppContext *ctx;
+
+ /* Recover context and remove timeout */
+ ctx = self->priv->connect_pending;
+
+ g_source_remove (self->priv->connect_pending_id);
+
+ self->priv->connect_pending = NULL;
+ self->priv->connect_pending_id = 0;
+ self->priv->connect_cancellable_id = 0;
+
+ g_assert (dial_3gpp_context_set_error_if_cancelled (ctx, &error));
+
+ g_simple_async_result_take_error (ctx->result, error);
+ connect_reset (ctx);
+}
+
+static void
+activate_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ Dial3gppContext *ctx)
+{
+ GError *error = NULL;
+
+ /* 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);
+ g_simple_async_result_complete (ctx->result);
+ g_object_unref (ctx->result);
+ return;
+ }
+
+ /* We will now setup a timeout and keep the context in the bearer's private.
+ * Reports of modem being connected will arrive via unsolicited messages. */
+ g_assert (ctx->self->priv->connect_pending == NULL);
+ ctx->self->priv->connect_pending = ctx;
+ ctx->self->priv->connect_pending_id = g_timeout_add_seconds (30,
+ (GSourceFunc)connect_timed_out_cb,
+ ctx->self);
+ ctx->self->priv->connect_cancellable_id = g_cancellable_connect (ctx->cancellable,
+ G_CALLBACK (connect_cancelled_cb),
+ ctx->self,
+ NULL);
+}
+
+static void authenticate (Dial3gppContext *ctx);
+
+static void
+authenticate_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ Dial3gppContext *ctx)
+{
+ gchar *command;
+
+ /* If cancelled, complete */
+ if (dial_3gpp_context_complete_and_free_if_cancelled (ctx))
+ return;
+
+ if (!mm_base_modem_at_command_full_finish (modem, res, NULL)) {
+ /* Try the next auth command */
+ ctx->auth_idx++;
+ authenticate (ctx);
+ return;
+ }
+
+ /* Store which auth command worked, for next attempts */
+ ctx->self->priv->auth_idx = ctx->auth_idx;
+
+ /* Success, activate the PDP context and start the data session */
+ command = g_strdup_printf ("AT_OWANCALL=%d,1,1",
+ ctx->cid);
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)activate_ready,
+ ctx);
+ g_free (command);
+}
+
+const gchar *auth_commands[] = {
+ "$QCPDPP",
+ /* Icera-based devices (GI0322/Quicksilver, iCON 505) don't implement
+ * $QCPDPP, but instead use _OPDPP with the same arguments.
+ */
+ "_OPDPP",
+ NULL
};
+static void
+authenticate (Dial3gppContext *ctx)
+{
+ gchar *command;
+
+ if (!auth_commands[ctx->auth_idx]) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't run HSO authentication");
+ dial_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Both user and password are required; otherwise firmware returns an error */
+ if (!ctx->self->priv->user || !ctx->self->priv->password)
+ command = g_strdup_printf ("%s=%d,0",
+ auth_commands[ctx->auth_idx],
+ ctx->cid);
+ else
+ command = g_strdup_printf ("%s=%d,1,\"%s\",\"%s\"",
+ auth_commands[ctx->auth_idx],
+ ctx->cid,
+ ctx->self->priv->password,
+ ctx->self->priv->user);
+
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)authenticate_ready,
+ ctx);
+ g_free (command);
+}
+
+static void
+dial_3gpp (MMBroadbandBearer *self,
+ MMBaseModem *modem,
+ MMAtSerialPort *primary,
+ guint cid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_assert (primary != NULL);
+
+ authenticate (dial_3gpp_context_new (MM_BROADBAND_BEARER_HSO (self),
+ modem,
+ primary,
+ cid,
+ cancellable,
+ callback,
+ user_data));
+}
+
/*****************************************************************************/
static gboolean
@@ -199,6 +564,7 @@ mm_broadband_bearer_hso_class_init (MMBroadbandBearerHsoClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MMBearerClass *bearer_class = MM_BEARER_CLASS (klass);
+ MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
object_class->get_property = get_property;
object_class->set_property = set_property;
@@ -207,6 +573,9 @@ mm_broadband_bearer_hso_class_init (MMBroadbandBearerHsoClass *klass)
bearer_class->cmp_properties = cmp_properties;
bearer_class->expose_properties = expose_properties;
+ broadband_bearer_class->dial_3gpp = dial_3gpp;
+ broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
+
properties[PROP_USER] =
g_param_spec_string (MM_BROADBAND_BEARER_HSO_USER,
"User",
diff --git a/plugins/option/mm-broadband-bearer-hso.h b/plugins/option/mm-broadband-bearer-hso.h
index 7dc87cf9..4773d46d 100644
--- a/plugins/option/mm-broadband-bearer-hso.h
+++ b/plugins/option/mm-broadband-bearer-hso.h
@@ -34,6 +34,13 @@
#define MM_BROADBAND_BEARER_HSO_USER "broadband-bearer-hso-user"
#define MM_BROADBAND_BEARER_HSO_PASSWORD "broadband-bearer-hso-password"
+typedef enum {
+ MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_UNKNOWN,
+ MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_CONNECTED,
+ MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_CONNECTION_FAILED,
+ MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_DISCONNECTED
+} MMBroadbandBearerHsoConnectionStatus;
+
typedef struct _MMBroadbandBearerHso MMBroadbandBearerHso;
typedef struct _MMBroadbandBearerHsoClass MMBroadbandBearerHsoClass;
typedef struct _MMBroadbandBearerHsoPrivate MMBroadbandBearerHsoPrivate;
@@ -58,4 +65,7 @@ void mm_broadband_bearer_hso_new (MMBroadbandModemHso *modem,
MMBearer *mm_broadband_bearer_hso_new_finish (GAsyncResult *res,
GError **error);
+void mm_broadband_bearer_hso_report_connection_status (MMBroadbandBearerHso *self,
+ MMBroadbandBearerHsoConnectionStatus status);
+
#endif /* MM_BROADBAND_BEARER_HSO_H */