diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/mm-bearer-3gpp.c | 869 | ||||
-rw-r--r-- | src/mm-bearer-3gpp.h | 63 | ||||
-rw-r--r-- | src/mm-bearer.c | 305 | ||||
-rw-r--r-- | src/mm-bearer.h | 35 | ||||
-rw-r--r-- | src/mm-broadband-modem.c | 12 |
6 files changed, 1041 insertions, 245 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 99b091a1..6bdb9d6b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -112,6 +112,8 @@ modem_manager_SOURCES = \ mm-sim.c \ mm-bearer.h \ mm-bearer.c \ + mm-bearer-3gpp.h \ + mm-bearer-3gpp.c \ mm-bearer-list.h \ mm-bearer-list.c \ mm-base-modem-at.h \ diff --git a/src/mm-bearer-3gpp.c b/src/mm-bearer-3gpp.c new file mode 100644 index 00000000..11e3e3ee --- /dev/null +++ b/src/mm-bearer-3gpp.c @@ -0,0 +1,869 @@ +/* -*- 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 - 2011 Red Hat, Inc. + * Copyright (C) 2011 Google, Inc. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> + +#include <ModemManager.h> + +#include <mm-enums-types.h> +#include <mm-errors-types.h> + +#include "mm-bearer-3gpp.h" +#include "mm-base-modem-at.h" +#include "mm-utils.h" +#include "mm-log.h" +#include "mm-modem-helpers.h" + +G_DEFINE_TYPE (MMBearer3gpp, mm_bearer_3gpp, MM_TYPE_BEARER); + +enum { + PROP_0, + PROP_APN, + PROP_IP_TYPE, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +struct _MMBearer3gppPrivate { + /* APN of the PDP context */ + gchar *apn; + /* IP type of the PDP context */ + gchar *ip_type; + + /* Data port used when modem is connected */ + MMPort *port; + /* CID of the PDP context */ + guint cid; +}; + +/*****************************************************************************/ +/* CONNECT + * + * Connection procedure of a 3GPP 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 { + MMBearer *bearer; + MMBaseModem *modem; + MMAtSerialPort *primary; + MMPort *data; + guint cid; + guint max_cid; + GSimpleAsyncResult *result; + GError *error; +} ConnectContext; + +static void +connect_context_complete_and_free (ConnectContext *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 { + /* Port is connected; update the state */ + mm_port_set_connected (ctx->data, TRUE); + mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (ctx->bearer), + TRUE); + mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (ctx->bearer), + mm_port_get_device (ctx->data)); + /* Keep data port and CID around while connected */ + MM_BEARER_3GPP (ctx->bearer)->priv->cid = ctx->cid; + MM_BEARER_3GPP (ctx->bearer)->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->data); + g_object_unref (ctx->primary); + g_object_unref (ctx->bearer); + g_object_unref (ctx->modem); + g_object_unref (ctx->result); + g_free (ctx); +} + +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_report_ready (MMBaseModem *modem, + GAsyncResult *res, + ConnectContext *ctx) +{ + const gchar *result; + + 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_complete_and_free (ctx); +} + +static void +connect_ready (MMBaseModem *modem, + GAsyncResult *res, + ConnectContext *ctx) +{ + 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_complete_and_free (ctx); +} + +static void +initialize_pdp_context_ready (MMBaseModem *self, + GAsyncResult *res, + ConnectContext *ctx) +{ + gchar *command; + + 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_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, + ConnectContext *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_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, + MM_BEARER_3GPP (ctx->bearer)->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, + ConnectContext *ctx, + const gchar *command, + const gchar *response, + const GError *error, + GVariant **result, + GError **result_error) +{ + GError *inner_error = NULL; + GRegex *r; + GMatchInfo *match_info; + guint cid = 0; + + 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, + ConnectContext *ctx, + const gchar *command, + const gchar *response, + const GError *error, + GVariant **result, + GError **result_error) +{ + GError *inner_error = NULL; + GList *pdp_list; + GList *l; + guint cid; + + 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, MM_BEARER_3GPP (ctx->bearer)->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 (MMBearer *self, + const gchar *number, + GAsyncReadyCallback callback, + gpointer user_data) +{ + ConnectContext *ctx; + MMAtSerialPort *primary; + MMPort *data; + MMBaseModem *modem = NULL; + + if (number && number[0]) + mm_warn ("Ignoring number to use when connecting 3GPP bearer: '%s'", number); + + if (MM_BEARER_3GPP (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; + } + + 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); + g_object_unref (data); + return; + } + } + + ctx = g_new0 (ConnectContext, 1); + ctx->primary = g_object_ref (primary); + ctx->data = g_object_ref (data); + ctx->bearer = g_object_ref (self); + ctx->modem = modem; + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + connect); + + 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); +} + +/*****************************************************************************/ +/* DISCONNECT */ + +typedef struct { + MMBearer *bearer; + MMBaseModem *modem; + MMAtSerialPort *primary; + MMAtSerialPort *secondary; + MMPort *data; + gchar *cgact_command; + gboolean cgact_sent; + GSimpleAsyncResult *result; + GError *error; +} 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->bearer), FALSE); + mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (ctx->bearer), NULL); + /* Clear data port and CID */ + MM_BEARER_3GPP (ctx->bearer)->priv->cid = 0; + g_clear_object (&(MM_BEARER_3GPP (ctx->bearer)->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->bearer); + 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 */ + if (!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_BEARER_3GPP (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_BEARER_3GPP (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->bearer = g_object_ref (self); + ctx->modem = modem; + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + connect); + + /* If no specific CID was used, disable all PDP contexts */ + ctx->cgact_command = + (MM_BEARER_3GPP (self)->priv->cid >= 0 ? + g_strdup_printf ("+CGACT=0,%d", MM_BEARER_3GPP (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; + } + + mm_serial_port_flash (MM_SERIAL_PORT (ctx->primary), + 1000, + TRUE, + (MMSerialFlashFn)primary_flash_ready, + ctx); +} + +/*****************************************************************************/ + +MMBearer * +mm_bearer_3gpp_new_from_properties (MMBaseModem *modem, + GVariant *properties, + GError **error) +{ + GVariantIter iter; + const gchar *key; + const gchar *value; + gchar *apn = NULL; + gchar *ip_type = NULL; + + mm_dbg ("Creating 3GPP bearer with properties..."); + g_variant_iter_init (&iter, properties); + while (g_variant_iter_loop (&iter, "{ss}", &key, &value)) { + mm_dbg (" Property '%s', value '%s'", key, value); + if (g_str_equal (key, "apn")) { + if (apn) + mm_warn ("Duplicate 'apn' property found, ignoring value '%s'", value); + else + apn = g_strdup (value); + } else if (g_str_equal (key, "ip-type")) { + if (ip_type) + mm_warn ("Duplicate 'ip-type' property found, ignoring value '%s'", value); + else + ip_type = g_strdup (value); + } + else + mm_dbg ("Ignoring property '%s' in 3GPP bearer", key); + } + + /* Check mandatory properties */ + if (!apn) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Invalid input properties: 3GPP bearer requires 'apn'"); + g_free (ip_type); + return NULL; + } + + return mm_bearer_3gpp_new (modem, + apn, + ip_type); +} + +MMBearer * +mm_bearer_3gpp_new (MMBaseModem *modem, + const gchar *apn, + const gchar *ip_type) +{ + static guint id = 0; + MMBearer3gpp *bearer; + gchar *path; + + /* Create the object */ + bearer = g_object_new (MM_TYPE_BEARER_3GPP, + MM_BEARER_3GPP_APN, apn, + MM_BEARER_3GPP_IP_TYPE, ip_type, + NULL); + + /* Build dict with all properties */ + mm_bearer_expose_properties (MM_BEARER (bearer), + "apn", apn, + "ip-type", ip_type, + NULL); + + /* Set modem and path ONLY after having checked input properties, so that + * we don't export invalid bearers. */ + path = g_strdup_printf (MM_DBUS_BEARER_3GPP_PREFIX "/%d", id++); + g_object_set (bearer, + MM_BEARER_PATH, path, + MM_BEARER_MODEM, modem, + NULL); + g_free (path); + + return MM_BEARER (bearer); +} + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MMBearer3gpp *self = MM_BEARER_3GPP (object); + + switch (prop_id) { + case PROP_APN: + g_free (self->priv->apn); + self->priv->apn = g_value_dup_string (value); + break; + case PROP_IP_TYPE: + g_free (self->priv->ip_type); + self->priv->ip_type = g_value_dup_string (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) +{ + MMBearer3gpp *self = MM_BEARER_3GPP (object); + + switch (prop_id) { + case PROP_APN: + g_value_set_string (value, self->priv->apn); + break; + case PROP_IP_TYPE: + g_value_set_string (value, self->priv->ip_type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +mm_bearer_3gpp_init (MMBearer3gpp *self) +{ + /* Initialize private data */ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), + MM_TYPE_BEARER_3GPP, + MMBearer3gppPrivate); +} + +static void +finalize (GObject *object) +{ + MMBearer3gpp *self = MM_BEARER_3GPP (object); + + g_free (self->priv->apn); + g_free (self->priv->ip_type); + + G_OBJECT_CLASS (mm_bearer_3gpp_parent_class)->finalize (object); +} + +static void +mm_bearer_3gpp_class_init (MMBearer3gppClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMBearerClass *bearer_class = MM_BEARER_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMBearer3gppPrivate)); + + /* 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_APN] = + g_param_spec_string (MM_BEARER_3GPP_APN, + "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_APN, properties[PROP_APN]); + + properties[PROP_IP_TYPE] = + g_param_spec_string (MM_BEARER_3GPP_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]); +} diff --git a/src/mm-bearer-3gpp.h b/src/mm-bearer-3gpp.h new file mode 100644 index 00000000..de75e673 --- /dev/null +++ b/src/mm-bearer-3gpp.h @@ -0,0 +1,63 @@ +/* -*- 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 Google, Inc. + */ + +#ifndef MM_BEARER_3GPP_H +#define MM_BEARER_3GPP_H + +#include <glib.h> +#include <glib-object.h> + +#include "mm-bearer.h" +#include "mm-base-modem.h" + +#define MM_TYPE_BEARER_3GPP (mm_bearer_3gpp_get_type ()) +#define MM_BEARER_3GPP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER_3GPP, MMBearer3gpp)) +#define MM_BEARER_3GPP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BEARER_3GPP, MMBearer3gppClass)) +#define MM_IS_BEARER_3GPP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER_3GPP)) +#define MM_IS_BEARER_3GPP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_3GPP)) +#define MM_BEARER_3GPP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_3GPP, MMBearer3gppClass)) + +#define MM_BEARER_3GPP_CID "bearer-3gpp-cid" +#define MM_BEARER_3GPP_APN "bearer-3gpp-apn" +#define MM_BEARER_3GPP_IP_TYPE "bearer-3gpp-ip-type" + +/* Prefix for all 3GPP bearer object paths */ +#define MM_DBUS_BEARER_3GPP_PREFIX MM_DBUS_BEARER_PREFIX "/3GPP" + +typedef struct _MMBearer3gpp MMBearer3gpp; +typedef struct _MMBearer3gppClass MMBearer3gppClass; +typedef struct _MMBearer3gppPrivate MMBearer3gppPrivate; + +struct _MMBearer3gpp { + MMBearer parent; + MMBearer3gppPrivate *priv; +}; + +struct _MMBearer3gppClass { + MMBearerClass parent; +}; + +GType mm_bearer_3gpp_get_type (void); + +MMBearer *mm_bearer_3gpp_new (MMBaseModem *modem, + const gchar *apn, + const gchar *ip_type); +MMBearer *mm_bearer_3gpp_new_from_properties (MMBaseModem *modem, + GVariant *properties, + GError **error); + +#endif /* MM_BEARER_3GPP_H */ diff --git a/src/mm-bearer.c b/src/mm-bearer.c index 14f57b8d..459377c1 100644 --- a/src/mm-bearer.c +++ b/src/mm-bearer.c @@ -43,12 +43,6 @@ enum { PROP_PATH, PROP_CONNECTION, PROP_MODEM, - PROP_CAPABILITY, - PROP_CONNECTION_APN, - PROP_CONNECTION_IP_TYPE, - PROP_CONNECTION_USER, - PROP_CONNECTION_PASSWORD, - PROP_CONNECTION_NUMBER, PROP_LAST }; @@ -61,36 +55,75 @@ struct _MMBearerPrivate { MMBaseModem *modem; /* The path where the BEARER object is exported */ gchar *path; - /* Capability of this bearer */ - MMModemCapability capability; - - /* Input properties configured */ - gchar *connection_apn; - gchar *connection_ip_type; - gchar *connection_user; - gchar *connection_password; - gchar *connection_number; }; /*****************************************************************************/ /* CONNECT */ +static void +handle_connect_ready (MMBearer *self, + GAsyncResult *res, + GDBusMethodInvocation *invocation) +{ + GError *error = NULL; + + if (!MM_BEARER_GET_CLASS (self)->connect_finish (self, res, &error)) + g_dbus_method_invocation_take_error (invocation, error); + else + mm_gdbus_bearer_complete_connect (MM_GDBUS_BEARER (self), invocation); + + g_object_unref (invocation); +} + static gboolean handle_connect (MMBearer *self, GDBusMethodInvocation *invocation, - const gchar *arg_number) + const gchar *number) { + if (MM_BEARER_GET_CLASS (self)->connect != NULL && + MM_BEARER_GET_CLASS (self)->connect_finish != NULL) { + MM_BEARER_GET_CLASS (self)->connect ( + self, + number, + (GAsyncReadyCallback)handle_connect_ready, + g_object_ref (invocation)); + return TRUE; + } + return FALSE; } /*****************************************************************************/ /* DISCONNECT */ +static void +handle_disconnect_ready (MMBearer *self, + GAsyncResult *res, + GDBusMethodInvocation *invocation) +{ + GError *error = NULL; + + if (!MM_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) + g_dbus_method_invocation_take_error (invocation, error); + else + mm_gdbus_bearer_complete_disconnect (MM_GDBUS_BEARER (self), invocation); + + g_object_unref (invocation); +} + static gboolean handle_disconnect (MMBearer *self, - GDBusMethodInvocation *invocation, - const gchar *arg_number) + GDBusMethodInvocation *invocation) { + if (MM_BEARER_GET_CLASS (self)->disconnect != NULL && + MM_BEARER_GET_CLASS (self)->disconnect_finish != NULL) { + MM_BEARER_GET_CLASS (self)->disconnect ( + self, + (GAsyncReadyCallback)handle_disconnect_ready, + g_object_ref (invocation)); + return TRUE; + } + return FALSE; } @@ -145,126 +178,34 @@ mm_bearer_get_path (MMBearer *self) /*****************************************************************************/ -static gboolean -parse_input_properties (MMBearer *bearer, - MMModemCapability bearer_capability, - GVariant *properties, - GError **error) +void +mm_bearer_expose_properties (MMBearer *bearer, + const gchar *first_property_name, + ...) { - GVariantIter iter; - gchar *key; - gchar *value; + va_list va_args; + const gchar *key; + GVariantBuilder builder; - g_variant_iter_init (&iter, properties); - while (g_variant_iter_loop (&iter, "{ss}", &key, &value)) { - gchar *previous = NULL; + va_start (va_args, first_property_name); - g_object_get (G_OBJECT (bearer), - key, &previous, - NULL); + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ss}")); + key = first_property_name; + while (key) { + const gchar *value; - if (previous) { - if (g_str_equal (previous, value)) { - /* no big deal */ - g_free (previous); - continue; - } - - g_free (previous); - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_INVALID_ARGS, - "Invalid input properties: duplicated key '%s'", - key); - return FALSE; - } - - g_object_set (G_OBJECT (bearer), - key, value, - NULL); - } + /* If a key with NULL value is given, just ignore it. */ + value = va_arg (va_args, gchar *); + if (value) + g_variant_builder_add (&builder, "{ss}", key, value); - /* Check mandatory properties for each capability */ -#define CHECK_MANDATORY_PROPERTY(NAME, PROPERTY) do \ - { \ - gchar *value; \ - \ - g_object_get (G_OBJECT (bearer), \ - PROPERTY, &value, \ - NULL); \ - if (!value) { \ - g_set_error (error, \ - MM_CORE_ERROR, \ - MM_CORE_ERROR_INVALID_ARGS, \ - "Invalid input properties: %s bearer requires '%s'", \ - NAME, \ - PROPERTY); \ - return FALSE; \ - } \ - g_free (value); \ - } while (0) - - /* POTS bearer? */ - if (bearer_capability & MM_MODEM_CAPABILITY_POTS) { - CHECK_MANDATORY_PROPERTY ("POTS", MM_BEARER_CONNECTION_NUMBER); - } - /* CDMA bearer? */ - else if (bearer_capability & MM_MODEM_CAPABILITY_CDMA_EVDO) { - /* No mandatory properties here */ - } - /* 3GPP bearer? */ - else { - CHECK_MANDATORY_PROPERTY ("3GPP", MM_BEARER_CONNECTION_APN); + key = va_arg (va_args, gchar *); } - -#undef CHECK_MANDATORY_PROPERTY + va_end (va_args); /* Keep the whole list of properties in the interface */ - mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (bearer), properties); - return TRUE; -} - -MMBearer * -mm_bearer_new (MMBaseModem *modem, - GVariant *properties, - MMModemCapability capability, - GError **error) -{ - static guint32 id = 0; - gchar *path; - MMBearer *bearer; - - /* Ensure only one capability is set */ - g_assert_cmpuint (mm_count_bits_set (capability), ==, 1); - - /* Create the object */ - bearer = g_object_new (MM_TYPE_BEARER, - MM_BEARER_CAPABILITY, capability, - NULL); - - /* Parse and set input properties */ - if (!parse_input_properties (bearer, capability, properties, error)) { - g_object_unref (bearer); - return NULL; - } - - /* Set defaults */ - mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (bearer), NULL); - mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (bearer), FALSE); - mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (bearer), FALSE); - mm_gdbus_bearer_set_ip4_config (MM_GDBUS_BEARER (bearer), NULL); - mm_gdbus_bearer_set_ip6_config (MM_GDBUS_BEARER (bearer), NULL); - - /* Set modem and path ONLY after having checked input properties, so that - * we don't export invalid bearers. */ - path = g_strdup_printf (MM_DBUS_BEARER_PREFIX "%d", id++); - g_object_set (bearer, - MM_BEARER_PATH, path, - MM_BEARER_MODEM, modem, - NULL); - g_free (path); - - return bearer; + mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (bearer), + g_variant_builder_end (&builder)); } static void @@ -302,29 +243,6 @@ set_property (GObject *object, self, MM_BEARER_CONNECTION, G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); break; - case PROP_CAPABILITY: - self->priv->capability = g_value_get_flags (value); - break; - case PROP_CONNECTION_APN: - g_free (self->priv->connection_apn); - self->priv->connection_apn = g_value_dup_string (value); - break; - case PROP_CONNECTION_IP_TYPE: - g_free (self->priv->connection_ip_type); - self->priv->connection_ip_type = g_value_dup_string (value); - break; - case PROP_CONNECTION_USER: - g_free (self->priv->connection_user); - self->priv->connection_user = g_value_dup_string (value); - break; - case PROP_CONNECTION_PASSWORD: - g_free (self->priv->connection_password); - self->priv->connection_password = g_value_dup_string (value); - break; - case PROP_CONNECTION_NUMBER: - g_free (self->priv->connection_number); - self->priv->connection_number = g_value_dup_string (value); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -349,24 +267,6 @@ get_property (GObject *object, case PROP_MODEM: g_value_set_object (value, self->priv->modem); break; - case PROP_CAPABILITY: - g_value_set_flags (value, self->priv->capability); - break; - case PROP_CONNECTION_APN: - g_value_set_string (value, self->priv->connection_apn); - break; - case PROP_CONNECTION_IP_TYPE: - g_value_set_string (value, self->priv->connection_ip_type); - break; - case PROP_CONNECTION_USER: - g_value_set_string (value, self->priv->connection_user); - break; - case PROP_CONNECTION_PASSWORD: - g_value_set_string (value, self->priv->connection_password); - break; - case PROP_CONNECTION_NUMBER: - g_value_set_string (value, self->priv->connection_number); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -380,6 +280,14 @@ mm_bearer_init (MMBearer *self) self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), MM_TYPE_BEARER, MMBearerPrivate); + + /* Set defaults */ + mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), NULL); + mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), FALSE); + mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE); + mm_gdbus_bearer_set_ip4_config (MM_GDBUS_BEARER (self), NULL); + mm_gdbus_bearer_set_ip6_config (MM_GDBUS_BEARER (self), NULL); + mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (self), NULL); } static void @@ -389,12 +297,6 @@ finalize (GObject *object) g_free (self->priv->path); - g_free (self->priv->connection_apn); - g_free (self->priv->connection_ip_type); - g_free (self->priv->connection_user); - g_free (self->priv->connection_password); - g_free (self->priv->connection_number); - G_OBJECT_CLASS (mm_bearer_parent_class)->finalize (object); } @@ -450,53 +352,4 @@ mm_bearer_class_init (MMBearerClass *klass) MM_TYPE_BASE_MODEM, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]); - - properties[PROP_CAPABILITY] = - g_param_spec_flags (MM_BEARER_CAPABILITY, - "Capability", - "The Capability supported by this Bearer", - MM_TYPE_MODEM_CAPABILITY, - MM_MODEM_CAPABILITY_NONE, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_CAPABILITY, properties[PROP_CAPABILITY]); - - properties[PROP_CONNECTION_APN] = - g_param_spec_string (MM_BEARER_CONNECTION_APN, - "Connection APN", - "Access Point Name to use in the connection", - NULL, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_CONNECTION_APN, properties[PROP_CONNECTION_APN]); - - properties[PROP_CONNECTION_IP_TYPE] = - g_param_spec_string (MM_BEARER_CONNECTION_IP_TYPE, - "Connection IP type", - "IP setup to use in the connection", - NULL, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_CONNECTION_IP_TYPE, properties[PROP_CONNECTION_IP_TYPE]); - - properties[PROP_CONNECTION_USER] = - g_param_spec_string (MM_BEARER_CONNECTION_USER, - "Connection User", - "User to use in the connection", - NULL, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_CONNECTION_USER, properties[PROP_CONNECTION_USER]); - - properties[PROP_CONNECTION_PASSWORD] = - g_param_spec_string (MM_BEARER_CONNECTION_PASSWORD, - "Connection Password", - "Password to use in the connection", - NULL, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_CONNECTION_PASSWORD, properties[PROP_CONNECTION_PASSWORD]); - - properties[PROP_CONNECTION_NUMBER] = - g_param_spec_string (MM_BEARER_CONNECTION_NUMBER, - "Connection Number", - "Number to use in the connection", - NULL, - G_PARAM_READWRITE); - g_object_class_install_property (object_class, PROP_CONNECTION_NUMBER, properties[PROP_CONNECTION_NUMBER]); } diff --git a/src/mm-bearer.h b/src/mm-bearer.h index a1778a7f..8a73caa3 100644 --- a/src/mm-bearer.h +++ b/src/mm-bearer.h @@ -38,16 +38,9 @@ typedef struct _MMBearerPrivate MMBearerPrivate; #define MM_BEARER_PATH "bearer-path" #define MM_BEARER_CONNECTION "bearer-connection" #define MM_BEARER_MODEM "bearer-modem" -#define MM_BEARER_CAPABILITY "bearer-capability" -/* same names as the ones used in DBus properties */ -#define MM_BEARER_CONNECTION_APN "apn" -#define MM_BEARER_CONNECTION_IP_TYPE "ip-type" -#define MM_BEARER_CONNECTION_USER "user" -#define MM_BEARER_CONNECTION_PASSWORD "password" -#define MM_BEARER_CONNECTION_NUMBER "number" /* Prefix for all bearer object paths */ -#define MM_DBUS_BEARER_PREFIX MM_DBUS_PATH "/Bearers/" +#define MM_DBUS_BEARER_PREFIX MM_DBUS_PATH "/Bearers" struct _MMBearer { MmGdbusBearerSkeleton parent; @@ -56,15 +49,31 @@ struct _MMBearer { struct _MMBearerClass { MmGdbusBearerSkeletonClass parent; + + /* Connect this bearer */ + void (* connect) (MMBearer *bearer, + const gchar *number, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* connect_finish) (MMBearer *bearer, + GAsyncResult *res, + GError **error); + + /* Disconnect this bearer */ + void (* disconnect) (MMBearer *bearer, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* disconnect_finish) (MMBearer *bearer, + GAsyncResult *res, + GError **error); }; GType mm_bearer_get_type (void); -MMBearer *mm_bearer_new (MMBaseModem *modem, - GVariant *properties, - MMModemCapability capability, - GError **error); - const gchar *mm_bearer_get_path (MMBearer *bearer); +void mm_bearer_expose_properties (MMBearer *bearer, + const gchar *first_property_name, + ...); + #endif /* MM_BEARER_H */ diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index 2af4db6b..bbc4f882 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -30,7 +30,7 @@ #include "mm-broadband-modem.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" -#include "mm-bearer.h" +#include "mm-bearer-3gpp.h" #include "mm-bearer-list.h" #include "mm-sim.h" #include "mm-log.h" @@ -113,11 +113,10 @@ modem_create_bearer (MMIfaceModem *self, /* TODO: We'll need to guess the capability of the bearer, based on the * current capabilities that we handle, and the specific allowed modes - * configured in the modem. Use GSM_UMTS for testing now */ - bearer = mm_bearer_new (MM_BASE_MODEM (self), - properties, - MM_MODEM_CAPABILITY_GSM_UMTS, - &error); + * configured in the modem. Use 3GPP for testing now */ + bearer = mm_bearer_3gpp_new_from_properties (MM_BASE_MODEM (self), + properties, + &error); if (!bearer) { g_simple_async_report_take_gerror_in_idle (G_OBJECT (self), callback, @@ -2309,6 +2308,7 @@ initialize_finish (MMBaseModem *self, &error)) { \ g_simple_async_result_take_error (G_SIMPLE_ASYNC_RESULT (ctx->result), error); \ initialize_context_complete_and_free (ctx); \ + return; \ } \ \ /* Go on to next step */ \ |