diff options
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/mm-base-modem.c | 414 | ||||
-rw-r--r-- | src/mm-base-modem.h | 82 |
3 files changed, 498 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 562e3f56..951c675f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -110,6 +110,8 @@ modem_manager_SOURCES = \ mm-manager.h \ mm-plugin-manager.c \ mm-plugin-manager.h \ + mm-base-modem.h \ + mm-base-modem.c \ mm-modem.c \ mm-modem.h \ mm-serial-parsers.c \ diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c new file mode 100644 index 00000000..df4f4521 --- /dev/null +++ b/src/mm-base-modem.c @@ -0,0 +1,414 @@ +/* -*- 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 <ModemManager.h> + +#include <mm-errors-types.h> +#include <mm-gdbus-modem.h> + +#include "mm-base-modem.h" +#include "mm-errors.h" +#include "mm-log.h" +#include "mm-at-serial-port.h" +#include "mm-qcdm-serial-port.h" + +G_DEFINE_ABSTRACT_TYPE (MMBaseModem, mm_base_modem, MM_GDBUS_TYPE_OBJECT_SKELETON); + +enum { + PROP_0, + PROP_VALID, + PROP_MAX_TIMEOUTS, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +struct _MMBaseModemPrivate { + gboolean valid; + + guint max_timeouts; + guint set_invalid_unresponsive_modem_id; + + MMAuthProvider *authp; + + GHashTable *ports; +}; + +static gchar * +get_hash_key (const gchar *subsys, + const gchar *name) +{ + return g_strdup_printf ("%s%s", subsys, name); +} + +MMPort * +mm_base_modem_get_port (MMBaseModem *self, + const gchar *subsys, + const gchar *name) +{ + MMPort *port; + gchar *key; + + g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (subsys != NULL, NULL); + + /* Only 'net' or 'tty' should be given */ + g_return_val_if_fail (g_str_equal (subsys, "net") || + g_str_equal (subsys, "tty"), + NULL); + + key = get_hash_key (subsys, name); + port = g_hash_table_lookup (self->priv->ports, key); + g_free (key); + + return port; +} + +static gboolean +set_invalid_unresponsive_modem_cb (MMBaseModem *self) +{ + mm_base_modem_set_valid (self, FALSE); + self->priv->set_invalid_unresponsive_modem_id = 0; + return FALSE; +} + +static void +serial_port_timed_out_cb (MMSerialPort *port, + guint n_consecutive_timeouts, + gpointer user_data) +{ + MMBaseModem *self = (MM_BASE_MODEM (user_data)); + + if (self->priv->max_timeouts > 0 && + n_consecutive_timeouts >= self->priv->max_timeouts) { + mm_warn ("Modem %s: Port (%s/%s) timed out %u times, marking modem as disabled", + g_dbus_object_get_object_path (G_DBUS_OBJECT (self)), + mm_port_type_to_name (mm_port_get_port_type (MM_PORT (port))), + mm_port_get_device (MM_PORT (port)), + n_consecutive_timeouts); + + /* Only set action to invalidate modem if not already done */ + if (!self->priv->set_invalid_unresponsive_modem_id) + self->priv->set_invalid_unresponsive_modem_id = + g_idle_add ((GSourceFunc)set_invalid_unresponsive_modem_cb, self); + } +} + +static void +find_primary (gpointer key, gpointer data, gpointer user_data) +{ + MMPort **found = user_data; + MMPort *port = MM_PORT (data); + + if (!*found && (mm_port_get_port_type (port) == MM_PORT_TYPE_PRIMARY)) + *found = port; +} + +MMPort * +mm_base_modem_add_port (MMBaseModem *self, + const gchar *subsys, + const gchar *name, + MMPortType ptype) +{ + MMPort *port = NULL; + gchar *key, *device; + + g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL); + g_return_val_if_fail (subsys != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (ptype != MM_PORT_TYPE_UNKNOWN, NULL); + + /* Only 'net' or 'tty' should be given */ + g_return_val_if_fail (g_str_equal (subsys, "net") || + g_str_equal (subsys, "tty"), + NULL); + + key = get_hash_key (subsys, name); + port = g_hash_table_lookup (self->priv->ports, key); + + if (port) { + mm_warn ("cannot add port (%s/%s): already exists", + subsys, name); + g_free (key); + return NULL; + } + + if (ptype == MM_PORT_TYPE_PRIMARY) { + g_hash_table_foreach (self->priv->ports, find_primary, &port); + if (port) { + mm_warn ("cannot add port (%s/%s): primary port already exists", + subsys, name); + g_free (key); + return NULL; + } + } + + if (g_str_equal (subsys, "tty")) { + if (ptype == MM_PORT_TYPE_QCDM) + port = MM_PORT (mm_qcdm_serial_port_new (name, ptype)); + else + port = MM_PORT (mm_at_serial_port_new (name, ptype)); + + /* For serial ports, enable port timeout checks */ + if (port) + g_signal_connect (port, + "timed-out", + G_CALLBACK (serial_port_timed_out_cb), + self); + } else if (!strcmp (subsys, "net")) { + port = MM_PORT (g_object_new (MM_TYPE_PORT, + MM_PORT_DEVICE, name, + MM_PORT_SUBSYS, MM_PORT_SUBSYS_NET, + MM_PORT_TYPE, ptype, + NULL)); + } + + device = mm_modem_get_device (MM_MODEM (self)); + mm_dbg ("(%s/%s) type %s claimed by %s", + subsys, + name, + mm_port_type_to_name (ptype), + device); + g_free (device); + + g_hash_table_insert (self->priv->ports, key, port); + + return port; +} + +gboolean +mm_base_modem_remove_port (MMBaseModem *self, MMPort *port) +{ + gchar *device, *key, *name; + const gchar *type_name, *subsys; + gboolean removed; + + g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE); + g_return_val_if_fail (MM_IS_PORT (port), FALSE); + + name = g_strdup (mm_port_get_device (port)); + subsys = mm_port_subsys_to_name (mm_port_get_subsys (port)); + type_name = mm_port_type_to_name (mm_port_get_port_type (port)); + + key = get_hash_key (subsys, name); + removed = g_hash_table_remove (self->priv->ports, key); + if (removed) { + /* Port may have already been destroyed by removal from the hash */ + device = mm_modem_get_device (MM_MODEM (self)); + mm_dbg ("(%s/%s) type %s removed from %s", + subsys, + name, + type_name, + device); + g_free (device); + } + g_free (key); + g_free (name); + + return removed; +} + +void +mm_base_modem_set_valid (MMBaseModem *self, + gboolean new_valid) +{ + g_return_if_fail (MM_IS_BASE_MODEM (self)); + + if (self->priv->valid != new_valid) { + self->priv->valid = new_valid; + + /* TODO */ + /* /\* Modem starts off in disabled state, and jumps to disabled when */ + /* * it's no longer valid. */ + /* *\/ */ + /* mm_modem_set_state (MM_MODEM (self), */ + /* MM_MODEM_STATE_DISABLED, */ + /* MM_MODEM_STATE_REASON_NONE); */ + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALID]); + } +} + +gboolean +mm_base_modem_get_valid (MMBaseModem *self) +{ + g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE); + + return self->priv->valid; +} + +/*****************************************************************************/ + +gboolean +mm_base_modem_auth_request (MMBaseModem *self, + const gchar *authorization, + DBusGMethodInvocation *context, + MMAuthRequestCb callback, + gpointer callback_data, + GDestroyNotify notify, + GError **error) +{ + g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE); + + return !!mm_auth_provider_request_auth (self->priv->authp, + authorization, + G_OBJECT (self), + context, + callback, + callback_data, + notify, + error); +} + +gboolean +mm_base_modem_auth_finish (MMBaseModem *self, + MMAuthRequest *req, + GError **error) +{ + if (mm_auth_request_get_result (req) != MM_AUTH_RESULT_AUTHORIZED) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_UNAUTHORIZED, + "This request requires the '%s' authorization", + mm_auth_request_get_authorization (req)); + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +static void +mm_base_modem_init (MMBaseModem *self) +{ + /* Initialize private data */ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), + MM_TYPE_BASE_MODEM, + MMBaseModemPrivate); + + self->priv->authp = mm_auth_provider_get (); + + self->priv->ports = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); +} + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MMBaseModem *self = MM_BASE_MODEM (object); + + switch (prop_id) { + case PROP_VALID: + mm_base_modem_set_valid (self, g_value_get_boolean (value)); + break; + case PROP_MAX_TIMEOUTS: + self->priv->max_timeouts = g_value_get_uint (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) +{ + MMBaseModem *self = MM_BASE_MODEM (object); + + switch (prop_id) { + case PROP_VALID: + g_value_set_boolean (value, self->priv->valid); + break; + case PROP_MAX_TIMEOUTS: + g_value_set_uint (value, self->priv->max_timeouts); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + MMBaseModem *self = MM_BASE_MODEM (object); + + mm_auth_provider_cancel_for_owner (self->priv->authp, object); + + G_OBJECT_CLASS (mm_base_modem_parent_class)->finalize (object); +} + +static void +dispose (GObject *object) +{ + MMBaseModem *self = MM_BASE_MODEM (object); + + if (self->priv->ports) { + g_hash_table_destroy (self->priv->ports); + self->priv->ports = NULL; + } + + G_OBJECT_CLASS (mm_base_modem_parent_class)->dispose (object); +} + +static void +mm_base_modem_class_init (MMBaseModemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMBaseModemPrivate)); + + /* Virtual methods */ + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + object_class->dispose = dispose; + + properties[PROP_MAX_TIMEOUTS] = + g_param_spec_uint (MM_BASE_MODEM_MAX_TIMEOUTS, + "Max timeouts", + "Maximum number of consecutive timed out commands sent to " + "the modem before disabling it. If 0, this feature is disabled.", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_MAX_TIMEOUTS, properties[PROP_MAX_TIMEOUTS]); + + properties[PROP_VALID] = + g_param_spec_boolean (MM_BASE_MODEM_VALID, + "Valid", + "Whether the modem is to be considered valid or not.", + FALSE, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_VALID, properties[PROP_VALID]); +} + diff --git a/src/mm-base-modem.h b/src/mm-base-modem.h new file mode 100644 index 00000000..bd4f4bb8 --- /dev/null +++ b/src/mm-base-modem.h @@ -0,0 +1,82 @@ +/* -*- 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. + */ + +#ifndef MM_BASE_MODEM_H +#define MM_BASE_MODEM_H + +#include <glib.h> +#include <glib-object.h> + +#include <mm-gdbus-modem.h> + +#include "mm-port.h" +#include "mm-at-serial-port.h" +#include "mm-modem.h" + +#define MM_TYPE_BASE_MODEM (mm_base_modem_get_type ()) +#define MM_BASE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_MODEM, MMBaseModem)) +#define MM_BASE_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BASE_MODEM, MMBaseModemClass)) +#define MM_IS_BASE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BASE_MODEM)) +#define MM_IS_BASE_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BASE_MODEM)) +#define MM_BASE_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BASE_MODEM, MMBaseModemClass)) + +typedef struct _MMBaseModem MMBaseModem; +typedef struct _MMBaseModemClass MMBaseModemClass; +typedef struct _MMBaseModemPrivate MMBaseModemPrivate; + +#define MM_BASE_MODEM_MAX_TIMEOUTS "base-modem-max-timeouts" +#define MM_BASE_MODEM_VALID "base-modem-valid" + +struct _MMBaseModem { + MmGdbusObjectSkeleton parent; + MMBaseModemPrivate *priv; +}; + +struct _MMBaseModemClass { + MmGdbusObjectSkeletonClass parent; +}; + +GType mm_base_modem_get_type (void); + + +MMPort *mm_base_modem_get_port (MMBaseModem *self, + const gchar *subsys, + const gchar *name); +MMPort *mm_base_modem_add_port (MMBaseModem *self, + const gchar *subsys, + const gchar *name, + MMPortType ptype); +gboolean mm_base_modem_remove_port (MMBaseModem *self, + MMPort *port); + +void mm_base_modem_set_valid (MMBaseModem *self, + gboolean valid); +gboolean mm_base_modem_get_valid (MMBaseModem *self); + +gboolean mm_base_modem_auth_request (MMBaseModem *self, + const gchar *authorization, + DBusGMethodInvocation *context, + MMAuthRequestCb callback, + gpointer callback_data, + GDestroyNotify notify, + GError **error); +gboolean mm_base_modem_auth_finish (MMBaseModem *self, + MMAuthRequest *req, + GError **error); + +#endif /* MM_BASE_MODEM_H */ + |