| /* -*- 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) 2012 Aleksander Morgado <aleksander@gnu.org> |
| */ |
| |
| #include <config.h> |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <arpa/inet.h> |
| |
| #include <ModemManager.h> |
| #include <libmm-common.h> |
| |
| #include "mm-base-modem-at.h" |
| #include "mm-broadband-bearer-hso.h" |
| #include "mm-log.h" |
| #include "mm-modem-helpers.h" |
| #include "mm-utils.h" |
| |
| G_DEFINE_TYPE (MMBroadbandBearerHso, mm_broadband_bearer_hso, MM_TYPE_BROADBAND_BEARER); |
| |
| struct _MMBroadbandBearerHsoPrivate { |
| guint auth_idx; |
| gpointer connect_pending; |
| guint connect_pending_id; |
| gulong connect_cancellable_id; |
| }; |
| |
| /*****************************************************************************/ |
| /* 3GPP IP config retrieval (sub-step of the 3GPP Connection sequence) */ |
| |
| typedef struct { |
| MMBroadbandBearerHso *self; |
| MMBaseModem *modem; |
| MMAtSerialPort *primary; |
| guint cid; |
| GSimpleAsyncResult *result; |
| } GetIpConfig3gppContext; |
| |
| static GetIpConfig3gppContext * |
| get_ip_config_3gpp_context_new (MMBroadbandBearerHso *self, |
| MMBaseModem *modem, |
| MMAtSerialPort *primary, |
| guint cid, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| GetIpConfig3gppContext *ctx; |
| |
| ctx = g_new0 (GetIpConfig3gppContext, 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, |
| get_ip_config_3gpp_context_new); |
| return ctx; |
| } |
| |
| static void |
| get_ip_config_context_complete_and_free (GetIpConfig3gppContext *ctx) |
| { |
| 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_free (ctx); |
| } |
| |
| static gboolean |
| get_ip_config_3gpp_finish (MMBroadbandBearer *self, |
| GAsyncResult *res, |
| MMBearerIpConfig **ipv4_config, |
| MMBearerIpConfig **ipv6_config, |
| GError **error) |
| { |
| MMBearerIpConfig *ip_config; |
| |
| if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) |
| return FALSE; |
| |
| /* No IPv6 for now */ |
| ip_config = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)); |
| *ipv4_config = g_object_ref (ip_config); |
| *ipv6_config = NULL; |
| return TRUE; |
| } |
| |
| #define OWANDATA_TAG "_OWANDATA: " |
| |
| static void |
| ip_config_ready (MMBaseModem *modem, |
| GAsyncResult *res, |
| GetIpConfig3gppContext *ctx) |
| { |
| MMBearerIpConfig *ip_config = NULL; |
| const gchar *response; |
| GError *error = NULL; |
| gchar **items; |
| gchar *dns[3] = { 0 }; |
| guint i; |
| guint dns_i; |
| guint32 tmp; |
| |
| response = mm_base_modem_at_command_full_finish (MM_BASE_MODEM (modem), res, &error); |
| if (error) { |
| g_simple_async_result_take_error (ctx->result, error); |
| get_ip_config_context_complete_and_free (ctx); |
| return; |
| } |
| |
| /* TODO: use a regex to parse this */ |
| |
| /* Check result */ |
| if (!g_str_has_prefix (response, OWANDATA_TAG)) { |
| g_simple_async_result_set_error (ctx->result, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_FAILED, |
| "Couldn't get IP config: invalid response '%s'", |
| response); |
| get_ip_config_context_complete_and_free (ctx); |
| return; |
| } |
| |
| response = mm_strip_tag (response, OWANDATA_TAG); |
| items = g_strsplit (response, ", ", 0); |
| |
| for (i = 0, dns_i = 0; items[i]; i++) { |
| if (i == 0) { /* CID */ |
| glong num; |
| |
| errno = 0; |
| num = strtol (items[i], NULL, 10); |
| if (errno != 0 || num < 0 || (gint) num != ctx->cid) { |
| error = g_error_new (MM_CORE_ERROR, |
| MM_CORE_ERROR_FAILED, |
| "Unknown CID in OWANDATA response (" |
| "got %d, expected %d)", (guint) num, ctx->cid); |
| break; |
| } |
| } else if (i == 1) { /* IP address */ |
| if (!inet_pton (AF_INET, items[i], &tmp)) |
| break; |
| |
| ip_config = mm_bearer_ip_config_new (); |
| mm_bearer_ip_config_set_method (ip_config, MM_BEARER_IP_METHOD_STATIC); |
| mm_bearer_ip_config_set_address (ip_config, items[i]); |
| } else if (i == 3 || i == 4) { /* DNS entries */ |
| if (!inet_pton (AF_INET, items[i], &tmp)) { |
| g_clear_object (&ip_config); |
| break; |
| } |
| |
| dns[dns_i++] = items[i]; |
| } |
| } |
| |
| if (!ip_config) { |
| if (error) |
| g_simple_async_result_take_error (ctx->result, error); |
| else |
| g_simple_async_result_set_error (ctx->result, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_FAILED, |
| "Couldn't get IP config: couldn't parse response '%s'", |
| response); |
| } else { |
| /* If we got DNS entries, set them in the IP config */ |
| if (dns[0]) |
| mm_bearer_ip_config_set_dns (ip_config, (const gchar **)dns); |
| |
| g_simple_async_result_set_op_res_gpointer (ctx->result, |
| ip_config, |
| (GDestroyNotify)g_object_unref); |
| } |
| |
| get_ip_config_context_complete_and_free (ctx); |
| g_strfreev (items); |
| } |
| |
| static void |
| get_ip_config_3gpp (MMBroadbandBearer *self, |
| MMBroadbandModem *modem, |
| MMAtSerialPort *primary, |
| MMAtSerialPort *secondary, |
| MMPort *data, |
| guint cid, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| gchar *command; |
| |
| command = g_strdup_printf ("AT_OWANDATA=%d", cid); |
| mm_base_modem_at_command_full (MM_BASE_MODEM (modem), |
| primary, |
| command, |
| 3, |
| FALSE, |
| NULL, /* cancellable */ |
| (GAsyncReadyCallback)ip_config_ready, |
| get_ip_config_3gpp_context_new (MM_BROADBAND_BEARER_HSO (self), |
| MM_BASE_MODEM (modem), |
| primary, |
| cid, |
| callback, |
| user_data)); |
| g_free (command); |
| } |
| |
| /*****************************************************************************/ |
| /* 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... */ |
| mm_bearer_report_disconnection (MM_BEARER (self)); |
| } |
| 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; |
| const gchar *user; |
| const gchar *password; |
| |
| 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; |
| } |
| |
| user = mm_bearer_properties_get_user (mm_bearer_peek_config (MM_BEARER (ctx->self))); |
| password = mm_bearer_properties_get_password (mm_bearer_peek_config (MM_BEARER (ctx->self))); |
| |
| /* Both user and password are required; otherwise firmware returns an error */ |
| if (!user || !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, |
| password, |
| 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)); |
| } |
| |
| /*****************************************************************************/ |
| /* 3GPP disconnect */ |
| |
| typedef struct { |
| MMBroadbandBearerHso *self; |
| MMBaseModem *modem; |
| MMAtSerialPort *primary; |
| GSimpleAsyncResult *result; |
| } DisconnectContext; |
| |
| static void |
| disconnect_context_complete_and_free (DisconnectContext *ctx) |
| { |
| g_simple_async_result_complete (ctx->result); |
| g_object_unref (ctx->result); |
| g_object_unref (ctx->primary); |
| g_object_unref (ctx->self); |
| g_object_unref (ctx->modem); |
| g_free (ctx); |
| } |
| |
| static gboolean |
| disconnect_3gpp_finish (MMBroadbandBearer *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); |
| } |
| |
| static void |
| disconnect_owancall_ready (MMBaseModem *modem, |
| GAsyncResult *res, |
| DisconnectContext *ctx) |
| { |
| GError *error = NULL; |
| |
| /* Ignore errors for now */ |
| mm_base_modem_at_command_full_finish (MM_BASE_MODEM (modem), res, &error); |
| if (error) { |
| mm_dbg ("Disconnection failed (not fatal): %s", error->message); |
| g_error_free (error); |
| } |
| |
| g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); |
| disconnect_context_complete_and_free (ctx); |
| } |
| |
| static void |
| disconnect_3gpp (MMBroadbandBearer *self, |
| MMBroadbandModem *modem, |
| MMAtSerialPort *primary, |
| MMAtSerialPort *secondary, |
| MMPort *data, |
| guint cid, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| gchar *command; |
| DisconnectContext *ctx; |
| |
| g_assert (primary != NULL); |
| |
| ctx = g_new0 (DisconnectContext, 1); |
| ctx->self = g_object_ref (self); |
| ctx->modem = MM_BASE_MODEM (g_object_ref (modem)); |
| ctx->primary = g_object_ref (primary); |
| ctx->result = g_simple_async_result_new (G_OBJECT (self), |
| callback, |
| user_data, |
| disconnect_3gpp); |
| |
| /* Use specific CID */ |
| command = g_strdup_printf ("AT_OWANCALL=%d,0,0", cid); |
| mm_base_modem_at_command_full (MM_BASE_MODEM (modem), |
| primary, |
| command, |
| 3, |
| FALSE, |
| NULL, /* cancellable */ |
| (GAsyncReadyCallback)disconnect_owancall_ready, |
| ctx); |
| g_free (command); |
| } |
| |
| /*****************************************************************************/ |
| |
| MMBearer * |
| mm_broadband_bearer_hso_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); |
| } |
| |
| void |
| mm_broadband_bearer_hso_new (MMBroadbandModemHso *modem, |
| MMBearerProperties *config, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| g_async_initable_new_async ( |
| MM_TYPE_BROADBAND_BEARER_HSO, |
| G_PRIORITY_DEFAULT, |
| cancellable, |
| callback, |
| user_data, |
| MM_BEARER_MODEM, modem, |
| MM_BEARER_CONFIG, config, |
| NULL); |
| } |
| |
| static void |
| mm_broadband_bearer_hso_init (MMBroadbandBearerHso *self) |
| { |
| /* Initialize private data */ |
| self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), |
| MM_TYPE_BROADBAND_BEARER_HSO, |
| MMBroadbandBearerHsoPrivate); |
| } |
| |
| static void |
| mm_broadband_bearer_hso_class_init (MMBroadbandBearerHsoClass *klass) |
| { |
| GObjectClass *object_class = G_OBJECT_CLASS (klass); |
| MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); |
| |
| g_type_class_add_private (object_class, sizeof (MMBroadbandBearerHsoPrivate)); |
| |
| broadband_bearer_class->dial_3gpp = dial_3gpp; |
| broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish; |
| broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp; |
| broadband_bearer_class->get_ip_config_3gpp_finish = get_ip_config_3gpp_finish; |
| broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; |
| broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish; |
| } |