bearer-3gpp: implement 3GPP-specific bearer

Implement basic functionality of a 3GPP based bearer, including connnection and
disconnection.
This commit is contained in:
Aleksander Morgado
2012-03-15 14:14:34 +01:00
parent c855e5be2d
commit 48f0061355
6 changed files with 1041 additions and 245 deletions
+2
View File
@@ -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 \
+869
View File
@@ -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]);
}
+63
View File
@@ -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 */
+79 -226
View File
@@ -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;
}
/* 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);
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);
key = va_arg (va_args, gchar *);
}
/* 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);
}
#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]);
}
+22 -13
View File
@@ -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 */
+6 -6
View File
@@ -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 */ \