aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/mm-broadband-bearer.c1480
-rw-r--r--src/mm-broadband-bearer.h69
3 files changed, 1551 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index b7dc3b33..11f3b5eb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -132,6 +132,8 @@ modem_manager_SOURCES = \
mm-sim.c \
mm-bearer.h \
mm-bearer.c \
+ mm-broadband-bearer.h \
+ mm-broadband-bearer.c \
mm-bearer-3gpp.h \
mm-bearer-3gpp.c \
mm-bearer-cdma.h \
diff --git a/src/mm-broadband-bearer.c b/src/mm-broadband-bearer.c
new file mode 100644
index 00000000..884b0392
--- /dev/null
+++ b/src/mm-broadband-bearer.c
@@ -0,0 +1,1480 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2012 Red Hat, Inc.
+ * Copyright (C) 2011 - 2012 Google, Inc.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <ModemManager.h>
+#include <libmm-common.h>
+
+#include "mm-broadband-bearer.h"
+#include "mm-iface-modem.h"
+#include "mm-base-modem-at.h"
+#include "mm-utils.h"
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+
+static void async_initable_iface_init (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandBearer, mm_broadband_bearer, MM_TYPE_BEARER, 0,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+ async_initable_iface_init));
+
+enum {
+ PROP_0,
+ PROP_3GPP_APN,
+ PROP_CDMA_NUMBER,
+ PROP_CDMA_RM_PROTOCOL,
+ PROP_IP_TYPE,
+ PROP_ALLOW_ROAMING,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _MMBroadbandBearerPrivate {
+ /*-- Common stuff --*/
+ /* IP type */
+ gchar *ip_type;
+ /* Flag to allow/forbid connections while roaming */
+ gboolean allow_roaming;
+ /* Data port used when modem is connected */
+ MMPort *port;
+
+ /*-- 3GPP specific --*/
+ /* APN of the PDP context */
+ gchar *apn;
+ /* CID of the PDP context */
+ guint cid;
+
+ /*-- CDMA specific --*/
+ /* (Optional) Number to dial */
+ gchar *number;
+ /* Protocol of the Rm interface */
+ MMModemCdmaRmProtocol rm_protocol;
+};
+
+/*****************************************************************************/
+
+const gchar *
+mm_broadband_bearer_get_3gpp_apn (MMBroadbandBearer *self)
+{
+ return self->priv->apn;
+}
+
+MMModemCdmaRmProtocol
+mm_broadband_bearer_get_cdma_rm_protocol (MMBroadbandBearer *self)
+{
+ return self->priv->rm_protocol;
+}
+
+const gchar *
+mm_broadband_bearer_get_ip_type (MMBroadbandBearer *self)
+{
+ return self->priv->ip_type;
+}
+
+gboolean
+mm_broadband_bearer_get_allow_roaming (MMBroadbandBearer *self)
+{
+ return self->priv->allow_roaming;
+}
+
+/*****************************************************************************/
+/* CDMA CONNECT
+ *
+ * CDMA connection procedure of a bearer involves several steps:
+ * 1) Get data port from the modem. Default implementation will have only
+ * one single possible data port, but plugins may have more.
+ * 2) If requesting specific RM, load current.
+ * 2.1) If current RM different to the requested one, set the new one.
+ * 3) Initiate call.
+ */
+
+typedef struct {
+ MMBroadbandBearer *self;
+ MMBaseModem *modem;
+ MMAtSerialPort *primary;
+ MMPort *data;
+ GSimpleAsyncResult *result;
+ gchar *number;
+ GError *error;
+ GCancellable *cancellable;
+} ConnectContextCdma;
+
+static void
+connect_context_cdma_complete_and_free (ConnectContextCdma *ctx)
+{
+ if (ctx->error) {
+ /* On errors, close the data port */
+ if (MM_IS_AT_SERIAL_PORT (ctx->data))
+ mm_serial_port_close (MM_SERIAL_PORT (ctx->data));
+
+ g_simple_async_result_take_error (ctx->result, ctx->error);
+ } else {
+ GVariant *ip_config;
+ GVariantBuilder builder;
+ MMBearerIpMethod ip_method;
+
+ /* Port is connected; update the state */
+ mm_port_set_connected (ctx->data, TRUE);
+ mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (ctx->self),
+ TRUE);
+ mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (ctx->self),
+ mm_port_get_device (ctx->data));
+
+ /* If serial port, set PPP method */
+ ip_method = (MM_IS_AT_SERIAL_PORT (ctx->data) ?
+ MM_BEARER_IP_METHOD_PPP :
+ MM_BEARER_IP_METHOD_DHCP);
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&builder, "{sv}", "method", g_variant_new_uint32 (ip_method));
+ ip_config = g_variant_builder_end (&builder);
+ mm_gdbus_bearer_set_ip4_config (MM_GDBUS_BEARER (ctx->self), ip_config);
+ mm_gdbus_bearer_set_ip6_config (MM_GDBUS_BEARER (ctx->self), ip_config);
+
+ /* Keep data port around while connected */
+ ctx->self->priv->port = g_object_ref (ctx->data);
+
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ }
+
+ g_simple_async_result_complete_in_idle (ctx->result);
+
+ g_free (ctx->number);
+ g_object_unref (ctx->cancellable);
+ g_object_unref (ctx->data);
+ g_object_unref (ctx->primary);
+ g_object_unref (ctx->self);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->result);
+ g_free (ctx);
+}
+
+static gboolean
+connect_context_cdma_set_error_if_cancelled (ConnectContextCdma *ctx,
+ GError **error)
+{
+ if (!g_cancellable_is_cancelled (ctx->cancellable))
+ return FALSE;
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CANCELLED,
+ "CDMA connection setup operation has been cancelled");
+ return TRUE;
+}
+
+static void
+dial_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ ConnectContextCdma *ctx)
+{
+ /* 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_finish (modem, res, &(ctx->error));
+ if (ctx->error)
+ mm_warn ("Couldn't connect: '%s'", ctx->error->message);
+ /* else... Yuhu! */
+
+ connect_context_cdma_complete_and_free (ctx);
+}
+
+static void
+cdma_connect_context_dial (ConnectContextCdma *ctx)
+{
+ gchar *command;
+
+ /* Decide which number to dial, in the following order:
+ * (1) If a number given during Connect(), use it.
+ * (2) If a number given when creating the bearer, use that one. Wait, this is quite
+ * redundant, isn't it?
+ * (3) Otherwise, use the default one, #777
+ */
+ if (ctx->number)
+ command = g_strconcat ("DT", ctx->number, NULL);
+ else if (ctx->self->priv->number)
+ command = g_strconcat ("DT", ctx->self->priv->number, NULL);
+ else
+ command = g_strdup ("DT#777");
+ mm_base_modem_at_command_in_port (
+ ctx->modem,
+ ctx->primary,
+ command,
+ 90,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)dial_ready,
+ ctx);
+ g_free (command);
+}
+
+static void
+set_rm_protocol_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ ConnectContextCdma *ctx)
+{
+ /* If cancelled, complete */
+ if (connect_context_cdma_set_error_if_cancelled (ctx, &ctx->error)) {
+ connect_context_cdma_complete_and_free (ctx);
+ return;
+ }
+
+ mm_base_modem_at_command_finish (self, res, &(ctx->error));
+ if (ctx->error) {
+ mm_warn ("Couldn't set RM protocol: '%s'", ctx->error->message);
+ connect_context_cdma_complete_and_free (ctx);
+ return;
+ }
+
+ /* Nothing else needed, go on with dialing */
+ cdma_connect_context_dial (ctx);
+}
+
+static void
+current_rm_protocol_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ ConnectContextCdma *ctx)
+{
+ const gchar *result;
+ guint current_index;
+ MMModemCdmaRmProtocol current_rm;
+
+ /* If cancelled, complete */
+ if (connect_context_cdma_set_error_if_cancelled (ctx, &ctx->error)) {
+ connect_context_cdma_complete_and_free (ctx);
+ return;
+ }
+
+ result = mm_base_modem_at_command_finish (self, res, &(ctx->error));
+ if (ctx->error) {
+ mm_warn ("Couldn't query current RM protocol: '%s'", ctx->error->message);
+ connect_context_cdma_complete_and_free (ctx);
+ return;
+ }
+
+ result = mm_strip_tag (result, "+CRM:");
+ current_index = (guint) atoi (result);
+ current_rm = mm_cdma_get_rm_protocol_from_index (current_index, &ctx->error);
+ if (ctx->error) {
+ mm_warn ("Couldn't parse RM protocol reply (%s): '%s'",
+ result,
+ ctx->error->message);
+ connect_context_cdma_complete_and_free (ctx);
+ return;
+ }
+
+ if (current_rm != ctx->self->priv->rm_protocol) {
+ guint new_index;
+ gchar *command;
+
+ mm_dbg ("Setting requested RM protocol...");
+
+ new_index = (mm_cdma_get_index_from_rm_protocol (
+ ctx->self->priv->rm_protocol,
+ &ctx->error));
+ if (ctx->error) {
+ mm_warn ("Cannot set RM protocol: '%s'",
+ ctx->error->message);
+ connect_context_cdma_complete_and_free (ctx);
+ return;
+ }
+
+ command = g_strdup_printf ("+CRM=%u", new_index);
+ mm_base_modem_at_command_in_port (
+ ctx->modem,
+ ctx->primary,
+ command,
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)set_rm_protocol_ready,
+ ctx);
+ g_free (command);
+ return;
+ }
+
+ /* Nothing else needed, go on with dialing */
+ cdma_connect_context_dial (ctx);
+}
+
+static void
+connect_cdma (MMBroadbandBearer *self,
+ MMBaseModem *modem,
+ MMAtSerialPort *primary,
+ MMPort *data,
+ const gchar *number,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ConnectContextCdma *ctx;
+
+ ctx = g_new0 (ConnectContextCdma, 1);
+ ctx->primary = g_object_ref (primary);
+ ctx->data = g_object_ref (data);
+ ctx->self = g_object_ref (self);
+ ctx->number = g_strdup (number);
+ ctx->modem = g_object_ref (modem);
+
+ /* 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);
+
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ connect_cdma);
+
+ if (self->priv->rm_protocol != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) {
+ /* Need to query current RM protocol */
+ mm_dbg ("Querying current RM protocol set...");
+ mm_base_modem_at_command_in_port (
+ ctx->modem,
+ ctx->primary,
+ "+CRM?",
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)current_rm_protocol_ready,
+ ctx);
+ return;
+ }
+
+ /* Nothing else needed, go on with dialing */
+ cdma_connect_context_dial (ctx);
+}
+
+/*****************************************************************************/
+/* 3GPP CONNECT
+ *
+ * 3GPP connection procedure of a bearer involves several steps:
+ * 1) Get data port from the modem. Default implementation will have only
+ * one single possible data port, but plugins may have more.
+ * 2) Decide which PDP context to use
+ * 2.1) Look for an already existing PDP context with the same APN.
+ * 2.2) If none found with the same APN, try to find a PDP context without any
+ * predefined APN.
+ * 2.3) If none found, look for the highest available CID, and use that one.
+ * 3) Activate PDP context.
+ * 4) Initiate call.
+ */
+
+typedef struct {
+ MMBroadbandBearer *self;
+ MMBaseModem *modem;
+ MMAtSerialPort *primary;
+ MMPort *data;
+ guint cid;
+ guint max_cid;
+ GSimpleAsyncResult *result;
+ GError *error;
+ GCancellable *cancellable;
+} ConnectContext3gpp;
+
+static void
+connect_context_3gpp_complete_and_free (ConnectContext3gpp *ctx)
+{
+ if (ctx->error) {
+ /* On errors, close the data port */
+ if (MM_IS_AT_SERIAL_PORT (ctx->data))
+ mm_serial_port_close (MM_SERIAL_PORT (ctx->data));
+
+ g_simple_async_result_take_error (ctx->result, ctx->error);
+ } else {
+ GVariant *ip_config;
+ GVariantBuilder builder;
+ MMBearerIpMethod ip_method;
+
+ /* Port is connected; update the state */
+ mm_port_set_connected (ctx->data, TRUE);
+ mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (ctx->self),
+ TRUE);
+ mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (ctx->self),
+ mm_port_get_device (ctx->data));
+
+ /* If serial port, set PPP method */
+ ip_method = (MM_IS_AT_SERIAL_PORT (ctx->data) ?
+ MM_BEARER_IP_METHOD_PPP :
+ MM_BEARER_IP_METHOD_DHCP);
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&builder, "{sv}", "method", g_variant_new_uint32 (ip_method));
+ ip_config = g_variant_builder_end (&builder);
+ mm_gdbus_bearer_set_ip4_config (MM_GDBUS_BEARER (ctx->self), ip_config);
+ mm_gdbus_bearer_set_ip6_config (MM_GDBUS_BEARER (ctx->self), ip_config);
+
+ /* Keep data port and CID around while connected */
+ ctx->self->priv->cid = ctx->cid;
+ ctx->self->priv->port = g_object_ref (ctx->data);
+
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ }
+
+ g_simple_async_result_complete_in_idle (ctx->result);
+
+ g_object_unref (ctx->cancellable);
+ g_object_unref (ctx->data);
+ g_object_unref (ctx->primary);
+ g_object_unref (ctx->self);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->result);
+ g_free (ctx);
+}
+
+static gboolean
+connect_context_3gpp_set_error_if_cancelled (ConnectContext3gpp *ctx,
+ GError **error)
+{
+ if (!g_cancellable_is_cancelled (ctx->cancellable))
+ return FALSE;
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CANCELLED,
+ "3GPP connection setup operation has been cancelled");
+ return TRUE;
+}
+
+static void
+connect_report_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ ConnectContext3gpp *ctx)
+{
+ const gchar *result;
+
+ /* If cancelled, complete */
+ if (connect_context_3gpp_set_error_if_cancelled (ctx, &ctx->error)) {
+ connect_context_3gpp_complete_and_free (ctx);
+ return;
+ }
+
+ result = mm_base_modem_at_command_finish (modem, res, NULL);
+ if (result &&
+ g_str_has_prefix (result, "+CEER: ") &&
+ strlen (result) > 7) {
+ GError *rebuilt;
+
+ rebuilt = g_error_new (ctx->error->domain,
+ ctx->error->code,
+ "%s", &result[7]);
+ g_error_free (ctx->error);
+ ctx->error = rebuilt;
+ }
+
+ /* Done with errors */
+ connect_context_3gpp_complete_and_free (ctx);
+}
+
+static void
+connect_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ ConnectContext3gpp *ctx)
+{
+ /* 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_finish (modem, res, &(ctx->error));
+ if (ctx->error) {
+ /* Try to get more information why it failed */
+ mm_base_modem_at_command_in_port (
+ modem,
+ ctx->primary,
+ "+CEER",
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)connect_report_ready,
+ ctx);
+ return;
+ }
+
+ /* Yuhu! */
+ connect_context_3gpp_complete_and_free (ctx);
+}
+
+static void
+initialize_pdp_context_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ ConnectContext3gpp *ctx)
+{
+ gchar *command;
+
+ /* If cancelled, complete */
+ if (connect_context_3gpp_set_error_if_cancelled (ctx, &ctx->error)) {
+ connect_context_3gpp_complete_and_free (ctx);
+ return;
+ }
+
+ mm_base_modem_at_command_finish (self, res, &(ctx->error));
+ if (ctx->error) {
+ mm_warn ("Couldn't initialize PDP context with our APN: '%s'", ctx->error->message);
+ connect_context_3gpp_complete_and_free (ctx);
+ return;
+ }
+
+ /* Use default *99 to connect */
+ command = g_strdup_printf ("ATD*99***%d#", ctx->cid);
+ mm_base_modem_at_command_in_port (
+ ctx->modem,
+ ctx->primary,
+ command,
+ 60,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)connect_ready,
+ ctx);
+ g_free (command);
+}
+
+static void
+find_cid_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ ConnectContext3gpp *ctx)
+{
+ GVariant *result;
+ gchar *command;
+ GError *error = NULL;
+
+ result = mm_base_modem_at_sequence_finish (self, res, NULL, &error);
+ if (!result) {
+ mm_warn ("Couldn't find best CID to use: '%s'", error->message);
+ ctx->error = error;
+ connect_context_3gpp_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 (connect_context_3gpp_set_error_if_cancelled (ctx, &ctx->error)) {
+ connect_context_3gpp_complete_and_free (ctx);
+ return;
+ }
+
+ /* Initialize PDP context with our APN */
+ ctx->cid = g_variant_get_uint32 (result);
+ command = g_strdup_printf ("+CGDCONT=%u,\"IP\",\"%s\"",
+ ctx->cid,
+ ctx->self->priv->apn);
+ mm_base_modem_at_command_in_port (
+ ctx->modem,
+ ctx->primary,
+ command,
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)initialize_pdp_context_ready,
+ ctx);
+ g_free (command);
+}
+
+static gboolean
+parse_cid_range (MMBaseModem *self,
+ ConnectContext3gpp *ctx,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
+{
+ GError *inner_error = NULL;
+ GRegex *r;
+ GMatchInfo *match_info;
+ guint cid = 0;
+
+ /* If cancelled, set result error */
+ if (connect_context_3gpp_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;
+ }
+
+ if (!g_str_has_prefix (response, "+CGDCONT:")) {
+ mm_dbg ("Unexpected +CGDCONT response: '%s'", response);
+ mm_dbg ("Defaulting to CID=1");
+ *result = g_variant_new_uint32 (1);
+ return TRUE;
+ }
+
+ r = g_regex_new ("\\+CGDCONT:\\s*\\((\\d+)-(\\d+)\\),\\(?\"(\\S+)\"",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
+ 0, &inner_error);
+ if (r) {
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ cid = 0;
+ while (!inner_error &&
+ cid == 0 &&
+ g_match_info_matches (match_info)) {
+ gchar *pdp_type;
+
+ pdp_type = g_match_info_fetch (match_info, 3);
+
+ /* TODO: What about PDP contexts of type "IPV6"? */
+ if (g_str_equal (pdp_type, "IP")) {
+ gchar *max_cid_range_str;
+ guint max_cid_range;
+
+ max_cid_range_str = g_match_info_fetch (match_info, 2);
+ max_cid_range = (guint)atoi (max_cid_range_str);
+
+ if (ctx->max_cid < max_cid_range)
+ cid = ctx->max_cid + 1;
+ else
+ cid = ctx->max_cid;
+
+ g_free (max_cid_range_str);
+ }
+
+ g_free (pdp_type);
+ g_match_info_next (match_info, &inner_error);
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ }
+
+ if (inner_error) {
+ mm_dbg ("Unexpected error matching +CGDCONT response: '%s'", inner_error->message);
+ g_error_free (inner_error);
+ }
+
+ 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 *self,
+ ConnectContext3gpp *ctx,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
+{
+ GError *inner_error = NULL;
+ GList *pdp_list;
+ GList *l;
+ guint cid;
+
+ /* If cancelled, set result error */
+ if (connect_context_3gpp_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;
+ }
+
+ pdp_list = mm_3gpp_parse_pdp_query_response (response, &inner_error);
+ if (!pdp_list) {
+ /* No predefined PDP contexts found */
+ mm_dbg ("No PDP contexts found");
+ return FALSE;
+ }
+
+ cid = 0;
+ 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;
+
+ mm_dbg (" PDP context [cid=%u] [type='%s'] [apn='%s']",
+ pdp->cid,
+ pdp->pdp_type ? pdp->pdp_type : "",
+ pdp->apn ? pdp->apn : "");
+ if (g_str_equal (pdp->pdp_type, "IP")) {
+ /* 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 if (g_str_equal (pdp->apn, ctx->self->priv->apn)) {
+ /* Found a PDP context with the same CID, we'll use it. */
+ mm_dbg ("Found PDP context with CID %u for APN '%s'",
+ pdp->cid, pdp->apn);
+ cid = pdp->cid;
+ /* 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;
+ }
+
+ return FALSE;
+}
+
+static const MMBaseModemAtCommand find_cid_sequence[] = {
+ { "+CGDCONT?", 3, FALSE, (MMBaseModemAtResponseProcessor)parse_pdp_list },
+ { "+CGDCONT=?", 3, TRUE, (MMBaseModemAtResponseProcessor)parse_cid_range },
+ { NULL }
+};
+
+static void
+connect_3gpp (MMBroadbandBearer *self,
+ MMBaseModem *modem,
+ MMAtSerialPort *primary,
+ MMPort *data,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ConnectContext3gpp *ctx;
+
+ ctx = g_new0 (ConnectContext3gpp, 1);
+ ctx->primary = g_object_ref (primary);
+ ctx->data = g_object_ref (data);
+ ctx->self = g_object_ref (self);
+ ctx->modem = g_object_ref (modem);
+
+ /* 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);
+
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ connect_3gpp);
+
+ mm_dbg ("Looking for best CID...");
+ mm_base_modem_at_sequence_in_port (
+ 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);
+}
+
+/*****************************************************************************/
+/* CONNECT */
+
+static gboolean
+connect_finish (MMBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+connect (MMBearer *self,
+ const gchar *number,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMAtSerialPort *primary;
+ MMPort *data;
+ MMBaseModem *modem = NULL;
+
+ /* 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");
+ return;
+ }
+
+ /* Get the owner modem object */
+ g_object_get (self,
+ MM_BEARER_MODEM, &modem,
+ NULL);
+ g_assert (modem != NULL);
+
+ /* We will launch the ATD call in the primary port */
+ primary = mm_base_modem_get_port_primary (modem);
+ 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);
+ return;
+ }
+
+ /* Look for best data port, NULL if none available. */
+ data = mm_base_modem_get_best_data_port (modem);
+ if (!data) {
+ g_simple_async_report_error_in_idle (
+ G_OBJECT (self),
+ callback,
+ user_data,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CONNECTED,
+ "Couldn't connect: all available data ports already connected");
+ g_object_unref (modem);
+ return;
+ }
+
+ /* If data port is AT, we need to ensure it's open during the whole
+ * connection. For the case where the primary port is used as data port,
+ * which is actually always right now, this is already ensured because the
+ * primary port is kept open as long as the modem is enabled, but anyway
+ * there's no real problem in keeping an open count here as well. */
+ if (MM_IS_AT_SERIAL_PORT (data)) {
+ GError *error = NULL;
+
+ if (!mm_serial_port_open (MM_SERIAL_PORT (data), &error)) {
+ g_prefix_error (&error, "Couldn't connect: cannot keep data port open.");
+ g_simple_async_report_take_gerror_in_idle (
+ G_OBJECT (self),
+ callback,
+ user_data,
+ error);
+ g_object_unref (modem);
+ return;
+ }
+ }
+
+ /* If the modem has 3GPP capabilities, launch 3GPP-based connection */
+ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) {
+ if (number && number[0])
+ mm_warn ("Ignoring number to use when connecting 3GPP bearer: '%s'", number);
+
+ connect_3gpp (MM_BROADBAND_BEARER (self),
+ modem,
+ primary,
+ data,
+ cancellable,
+ callback,
+ user_data);
+ g_object_unref (modem);
+ return;
+ }
+
+ /* Otherwise, launch CDMA-specific connection */
+ connect_cdma (MM_BROADBAND_BEARER (self),
+ modem,
+ primary,
+ data,
+ number,
+ cancellable,
+ callback,
+ user_data);
+ g_object_unref (modem);
+}
+
+/*****************************************************************************/
+/* DISCONNECT (more or less the same for CDMA and 3GPP) */
+
+typedef struct {
+ MMBroadbandBearer *self;
+ MMBaseModem *modem;
+ MMAtSerialPort *primary;
+ MMAtSerialPort *secondary;
+ MMPort *data;
+ GSimpleAsyncResult *result;
+ GError *error;
+
+ gboolean cgact_needed;
+ gchar *cgact_command;
+ gboolean cgact_sent;
+} DisconnectContext;
+
+static void
+disconnect_context_complete_and_free (DisconnectContext *ctx)
+{
+ if (ctx->error) {
+ g_simple_async_result_take_error (ctx->result, ctx->error);
+ } else {
+ /* If properly disconnected, close the data port */
+ if (MM_IS_AT_SERIAL_PORT (ctx->data))
+ mm_serial_port_close (MM_SERIAL_PORT (ctx->data));
+
+ /* Port is disconnected; update the state */
+ mm_port_set_connected (ctx->data, FALSE);
+ mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (ctx->self), FALSE);
+ mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (ctx->self), NULL);
+ mm_gdbus_bearer_set_ip4_config (MM_GDBUS_BEARER (ctx->self), NULL);
+ mm_gdbus_bearer_set_ip6_config (MM_GDBUS_BEARER (ctx->self), NULL);
+ /* Clear data port and CID */
+ if (ctx->self->priv->cid)
+ ctx->self->priv->cid = 0;
+ g_clear_object (&ctx->self->priv->port);
+
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ }
+
+ g_simple_async_result_complete_in_idle (ctx->result);
+
+ 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_object_unref (ctx->result);
+ g_free (ctx->cgact_command);
+ g_free (ctx);
+}
+
+static gboolean
+disconnect_finish (MMBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+cgact_primary_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ DisconnectContext *ctx)
+{
+ /* Ignore errors for now */
+ mm_base_modem_at_command_finish (MM_BASE_MODEM (modem), res, NULL);
+
+ disconnect_context_complete_and_free (ctx);
+}
+
+static void
+primary_flash_ready (MMSerialPort *port,
+ GError *error,
+ DisconnectContext *ctx)
+{
+ if (error) {
+ /* Ignore "NO CARRIER" response when modem disconnects and any flash
+ * failures we might encounter. Other errors are hard errors.
+ */
+ if (!g_error_matches (error,
+ MM_CONNECTION_ERROR,
+ MM_CONNECTION_ERROR_NO_CARRIER) &&
+ !g_error_matches (error,
+ MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_FLASH_FAILED)) {
+ /* Fatal */
+ ctx->error = g_error_copy (error);
+ disconnect_context_complete_and_free (ctx);
+ return;
+ }
+
+ mm_dbg ("Port flashing failed (not fatal): %s", error->message);
+ }
+
+ /* Don't bother doing the CGACT again if it was done on a secondary port
+ * or if not needed */
+ if (!ctx->cgact_needed ||
+ !ctx->cgact_sent) {
+ disconnect_context_complete_and_free (ctx);
+ return;
+ }
+
+ mm_base_modem_at_command_in_port (
+ ctx->modem,
+ ctx->primary,
+ ctx->cgact_command,
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)cgact_primary_ready,
+ ctx);
+}
+
+static void
+cgact_secondary_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ DisconnectContext *ctx)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (MM_BASE_MODEM (modem), res, &error);
+ if (!error)
+ ctx->cgact_sent = TRUE;
+ else
+ g_error_free (error);
+
+ mm_serial_port_flash (MM_SERIAL_PORT (ctx->primary),
+ 1000,
+ TRUE,
+ (MMSerialFlashFn)primary_flash_ready,
+ ctx);
+}
+
+static void
+disconnect (MMBearer *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ DisconnectContext *ctx;
+ MMBaseModem *modem = NULL;
+
+ 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");
+ return;
+ }
+
+ g_object_get (self,
+ MM_BEARER_MODEM, &modem,
+ NULL);
+ g_assert (modem != NULL);
+
+ ctx = g_new0 (DisconnectContext, 1);
+ ctx->data = g_object_ref (MM_BROADBAND_BEARER (self)->priv->port);
+ ctx->primary = g_object_ref (mm_base_modem_get_port_primary (modem));
+ ctx->secondary = mm_base_modem_get_port_secondary (modem);
+ if (ctx->secondary)
+ g_object_ref (ctx->secondary);
+ ctx->self = g_object_ref (self);
+ ctx->modem = modem;
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ disconnect);
+
+ /* If the modem has 3GPP capabilities, send CGACT to disable contexts */
+ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) {
+ ctx->cgact_needed = TRUE;
+
+ /* If no specific CID was used, disable all PDP contexts */
+ ctx->cgact_command =
+ (MM_BROADBAND_BEARER (self)->priv->cid >= 0 ?
+ g_strdup_printf ("+CGACT=0,%d", MM_BROADBAND_BEARER (self)->priv->cid) :
+ g_strdup_printf ("+CGACT=0"));
+
+ /* If the primary port is connected (with PPP) then try sending the PDP
+ * context deactivation on the secondary port because not all modems will
+ * respond to flashing (since either the modem or the kernel's serial
+ * driver doesn't support it).
+ */
+ if (ctx->secondary &&
+ mm_port_get_connected (MM_PORT (ctx->primary))) {
+ mm_base_modem_at_command_in_port (
+ ctx->modem,
+ ctx->secondary,
+ ctx->cgact_command,
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)cgact_secondary_ready,
+ ctx);
+ return;
+ }
+ }
+
+ /* If CGACT not needed, or if no secondary port, go on to flash the primary port */
+ mm_serial_port_flash (MM_SERIAL_PORT (ctx->primary),
+ 1000,
+ TRUE,
+ (MMSerialFlashFn)primary_flash_ready,
+ ctx);
+}
+
+/*****************************************************************************/
+
+typedef struct _InitAsyncContext InitAsyncContext;
+static void interface_initialization_step (InitAsyncContext *ctx);
+
+typedef enum {
+ INITIALIZATION_STEP_FIRST,
+ INITIALIZATION_STEP_CDMA_RM_PROTOCOL,
+ INITIALIZATION_STEP_LAST
+} InitializationStep;
+
+struct _InitAsyncContext {
+ MMBroadbandBearer *self;
+ GSimpleAsyncResult *result;
+ GCancellable *cancellable;
+ MMBaseModem *modem;
+ InitializationStep step;
+ MMAtSerialPort *port;
+};
+
+static void
+init_async_context_free (InitAsyncContext *ctx,
+ gboolean close_port)
+{
+ if (close_port)
+ mm_serial_port_close (MM_SERIAL_PORT (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);
+}
+
+MMBearer *
+mm_broadband_bearer_new_finish (GAsyncResult *res,
+ GError **error)
+{
+ GObject *bearer;
+ GObject *source;
+
+ source = g_async_result_get_source_object (res);
+ bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);
+ g_object_unref (source);
+
+ if (!bearer)
+ return NULL;
+
+ /* Only export valid bearers */
+ mm_bearer_export (MM_BEARER (bearer));
+
+ return MM_BEARER (bearer);
+}
+
+static gboolean
+initable_init_finish (GAsyncInitable *initable,
+ GAsyncResult *result,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+static void
+crm_range_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ InitAsyncContext *ctx)
+{
+ GError *error = NULL;
+ const gchar *response;
+
+ response = mm_base_modem_at_command_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;
+
+ if (mm_cdma_parse_crm_range_response (response,
+ &min, &max,
+ &error)) {
+ /* Check if value within the range */
+ if (ctx->self->priv->rm_protocol >= min &&
+ ctx->self->priv->rm_protocol <= max) {
+ /* Fine, go on with next step */
+ ctx->step++;
+ interface_initialization_step (ctx);
+ }
+
+ g_assert (error == NULL);
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested RM protocol '%s' is not supported",
+ mm_modem_cdma_rm_protocol_get_string (
+ ctx->self->priv->rm_protocol));
+ }
+
+ /* 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);
+}
+
+static void
+interface_initialization_step (InitAsyncContext *ctx)
+{
+ switch (ctx->step) {
+ case INITIALIZATION_STEP_FIRST:
+ /* Fall down to next step */
+ ctx->step++;
+
+ 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)) &&
+ ctx->self->priv->rm_protocol != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) {
+ mm_base_modem_at_command_in_port (
+ ctx->modem,
+ ctx->port,
+ "+CRM=?",
+ 3,
+ TRUE, /* getting range, so reply can be cached */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)crm_range_ready,
+ ctx);
+ return;
+ }
+
+ /* Fall down to next step */
+ ctx->step++;
+
+ 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);
+ return;
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+initable_init_async (GAsyncInitable *initable,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InitAsyncContext *ctx;
+ 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_BEARER_MODEM, &ctx->modem,
+ NULL);
+
+ ctx->port = mm_base_modem_get_port_primary (ctx->modem);
+ if (!mm_serial_port_open (MM_SERIAL_PORT (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);
+ return;
+ }
+
+ interface_initialization_step (ctx);
+}
+
+void
+mm_broadband_bearer_new (MMBroadbandModem *modem,
+ MMCommonBearerProperties *properties,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_async_initable_new_async (
+ MM_TYPE_BROADBAND_BEARER,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ callback,
+ user_data,
+ MM_BEARER_MODEM, modem,
+ MM_BROADBAND_BEARER_3GPP_APN, mm_common_bearer_properties_get_apn (properties),
+ MM_BROADBAND_BEARER_CDMA_NUMBER, mm_common_bearer_properties_get_number (properties),
+ MM_BROADBAND_BEARER_CDMA_RM_PROTOCOL, mm_common_bearer_properties_get_rm_protocol (properties),
+ MM_BROADBAND_BEARER_IP_TYPE, mm_common_bearer_properties_get_ip_type (properties),
+ MM_BROADBAND_BEARER_ALLOW_ROAMING, mm_common_bearer_properties_get_allow_roaming (properties),
+ 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_3GPP_APN:
+ g_free (self->priv->apn);
+ self->priv->apn = g_value_dup_string (value);
+ break;
+ case PROP_CDMA_NUMBER:
+ g_free (self->priv->number);
+ self->priv->number = g_value_dup_string (value);
+ break;
+ case PROP_CDMA_RM_PROTOCOL:
+ self->priv->rm_protocol = g_value_get_enum (value);
+ break;
+ case PROP_IP_TYPE:
+ g_free (self->priv->ip_type);
+ self->priv->ip_type = g_value_dup_string (value);
+ break;
+ case PROP_ALLOW_ROAMING:
+ self->priv->allow_roaming = g_value_get_boolean (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_3GPP_APN:
+ g_value_set_string (value, self->priv->apn);
+ break;
+ case PROP_CDMA_NUMBER:
+ g_value_set_string (value, self->priv->number);
+ break;
+ case PROP_CDMA_RM_PROTOCOL:
+ g_value_set_enum (value, self->priv->rm_protocol);
+ break;
+ case PROP_IP_TYPE:
+ g_value_set_string (value, self->priv->ip_type);
+ break;
+ case PROP_ALLOW_ROAMING:
+ g_value_set_boolean (value, self->priv->allow_roaming);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+mm_broadband_bearer_init (MMBroadbandBearer *self)
+{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+ MM_TYPE_BROADBAND_BEARER,
+ MMBroadbandBearerPrivate);
+
+ /* Set defaults */
+ self->priv->allow_roaming = TRUE;
+ self->priv->rm_protocol = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN;
+}
+
+static void
+finalize (GObject *object)
+{
+ MMBroadbandBearer *self = MM_BROADBAND_BEARER (object);
+
+ g_free (self->priv->apn);
+ g_free (self->priv->ip_type);
+
+ G_OBJECT_CLASS (mm_broadband_bearer_parent_class)->finalize (object);
+}
+
+static void
+async_initable_iface_init (GAsyncInitableIface *iface)
+{
+ iface->init_async = initable_init_async;
+ iface->init_finish = initable_init_finish;
+}
+
+static void
+mm_broadband_bearer_class_init (MMBroadbandBearerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMBearerClass *bearer_class = MM_BEARER_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMBroadbandBearerPrivate));
+
+ /* Virtual methods */
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->finalize = finalize;
+ bearer_class->connect = connect;
+ bearer_class->connect_finish = connect_finish;
+ bearer_class->disconnect = disconnect;
+ bearer_class->disconnect_finish = disconnect_finish;
+
+ properties[PROP_3GPP_APN] =
+ g_param_spec_string (MM_BROADBAND_BEARER_3GPP_APN,
+ "3GPP APN",
+ "Access Point Name to use in the connection",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_3GPP_APN, properties[PROP_3GPP_APN]);
+
+ properties[PROP_CDMA_NUMBER] =
+ g_param_spec_string (MM_BROADBAND_BEARER_CDMA_NUMBER,
+ "Number to dial",
+ "Number to dial when launching the CDMA connection",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_CDMA_NUMBER, properties[PROP_CDMA_NUMBER]);
+
+ properties[PROP_CDMA_RM_PROTOCOL] =
+ g_param_spec_enum (MM_BROADBAND_BEARER_CDMA_RM_PROTOCOL,
+ "Rm Protocol",
+ "Protocol to use in the CDMA Rm interface",
+ MM_TYPE_MODEM_CDMA_RM_PROTOCOL,
+ MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_CDMA_RM_PROTOCOL, properties[PROP_CDMA_RM_PROTOCOL]);
+
+ properties[PROP_IP_TYPE] =
+ g_param_spec_string (MM_BROADBAND_BEARER_IP_TYPE,
+ "IP type",
+ "IP setup to use in the connection",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_IP_TYPE, properties[PROP_IP_TYPE]);
+
+ properties[PROP_ALLOW_ROAMING] =
+ g_param_spec_boolean (MM_BEARER_ALLOW_ROAMING,
+ "Allow roaming",
+ "Whether connections are allowed when roaming",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_ALLOW_ROAMING, properties[PROP_ALLOW_ROAMING]);
+}
diff --git a/src/mm-broadband-bearer.h b/src/mm-broadband-bearer.h
new file mode 100644
index 00000000..31f60565
--- /dev/null
+++ b/src/mm-broadband-bearer.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Author: Aleksander Morgado <aleksander@lanedo.com>
+ *
+ * Copyright (C) 2011 - 2012 Google, Inc.
+ */
+
+#ifndef MM_BROADBAND_BEARER_H
+#define MM_BROADBAND_BEARER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mm-bearer.h"
+#include "mm-broadband-modem.h"
+
+#define MM_TYPE_BROADBAND_BEARER (mm_broadband_bearer_get_type ())
+#define MM_BROADBAND_BEARER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER, MMBroadbandBearer))
+#define MM_BROADBAND_BEARER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER, MMBroadbandBearerClass))
+#define MM_IS_BROADBAND_BEARER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER))
+#define MM_IS_BROADBAND_BEARER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER))
+#define MM_BROADBAND_BEARER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER, MMBroadbandBearerClass))
+
+#define MM_BROADBAND_BEARER_3GPP_APN "broadband-bearer-3gpp-apn"
+#define MM_BROADBAND_BEARER_CDMA_NUMBER "broadband-bearer-cdma-number"
+#define MM_BROADBAND_BEARER_CDMA_RM_PROTOCOL "broadband-bearer-cdma-rm-protocol"
+#define MM_BROADBAND_BEARER_IP_TYPE "broadband-bearer-ip-type"
+#define MM_BROADBAND_BEARER_ALLOW_ROAMING "broadband-bearer-allow-roaming"
+
+typedef struct _MMBroadbandBearer MMBroadbandBearer;
+typedef struct _MMBroadbandBearerClass MMBroadbandBearerClass;
+typedef struct _MMBroadbandBearerPrivate MMBroadbandBearerPrivate;
+
+struct _MMBroadbandBearer {
+ MMBearer parent;
+ MMBroadbandBearerPrivate *priv;
+};
+
+struct _MMBroadbandBearerClass {
+ MMBearerClass parent;
+};
+
+GType mm_broadband_bearer_get_type (void);
+
+/* Default 3GPP bearer creation implementation */
+void mm_broadband_bearer_new (MMBroadbandModem *modem,
+ MMCommonBearerProperties *properties,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMBearer *mm_broadband_bearer_new_finish (GAsyncResult *res,
+ GError **error);
+
+const gchar *mm_broadband_bearer_get_3gpp_apn (MMBroadbandBearer *self);
+guint mm_broadband_bearer_get_cdma_rm_protocol (MMBroadbandBearer *self);
+const gchar *mm_broadband_bearer_get_ip_type (MMBroadbandBearer *self);
+gboolean mm_broadband_bearer_get_allow_roaming (MMBroadbandBearer *self);
+
+#endif /* MM_BROADBAND_BEARER_H */