diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2012-06-05 15:25:38 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2012-08-06 20:06:44 +0200 |
commit | 815693661c7f6a4225d271d2cec67e59bad8e070 (patch) | |
tree | 4bd354e4713a5eb11a95d50e56b6ed2e2afe30e3 /src | |
parent | 4add521a98e59c9fbaaf30d965771ca01f748242 (diff) |
core: compile all ports before creating the modem object
Before this, we only exported the modem to DBus when all ports were organized,
in order to make sure that we select as primary port the one we really want and
not the first AT port grabbed. Given that to get all the ports organized we also
needed to wait to get all the ports grabbed, we can now also defer the creation
of the modem object until all the ports get grabbed. This allows us to create
different types of objects based on the ports available (e.g. we can now create
QMI-supported modem objects if we see a QMI port around).
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/mm-device.c | 413 | ||||
-rw-r--r-- | src/mm-device.h | 74 | ||||
-rw-r--r-- | src/mm-manager.c | 486 | ||||
-rw-r--r-- | src/mm-plugin-base.c | 98 | ||||
-rw-r--r-- | src/mm-plugin-base.h | 17 | ||||
-rw-r--r-- | src/mm-plugin.c | 15 | ||||
-rw-r--r-- | src/mm-plugin.h | 23 |
8 files changed, 758 insertions, 370 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 53b77b76..34dde87c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -148,6 +148,8 @@ ModemManager_SOURCES = \ mm-auth-provider.c \ mm-manager.c \ mm-manager.h \ + mm-device.c \ + mm-device.h \ mm-plugin-manager.c \ mm-plugin-manager.h \ mm-sim.h \ diff --git a/src/mm-device.c b/src/mm-device.c new file mode 100644 index 00000000..3f6859a0 --- /dev/null +++ b/src/mm-device.c @@ -0,0 +1,413 @@ +/* -*- 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) 2012 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-device.h" + +#include "mm-log.h" + +G_DEFINE_TYPE (MMDevice, mm_device, G_TYPE_OBJECT); + +enum { + PROP_0, + PROP_UDEV_DEVICE, + PROP_PLUGIN, + PROP_MODEM, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +struct _MMDevicePrivate { + /* Parent UDev device */ + GUdevDevice *udev_device; + + /* Best plugin to manage this device */ + MMPlugin *plugin; + + /* List of ports in the device */ + GList *udev_ports; + + /* The Modem object for this device */ + MMBaseModem *modem; + + /* When exported, a reference to the object manager */ + GDBusObjectManagerServer *object_manager; +}; + +/*****************************************************************************/ + +static gint +udev_port_cmp (GUdevDevice *a, + GUdevDevice *b) +{ + return strcmp (g_udev_device_get_sysfs_path (a), + g_udev_device_get_sysfs_path (b)); +} + +gboolean +mm_device_owns_port (MMDevice *self, + GUdevDevice *udev_port) +{ + return !!g_list_find_custom (self->priv->udev_ports, + udev_port, + (GCompareFunc)udev_port_cmp); +} + +void +mm_device_grab_port (MMDevice *self, + GUdevDevice *udev_port) +{ + if (!g_list_find_custom (self->priv->udev_ports, + udev_port, + (GCompareFunc)udev_port_cmp)) { + self->priv->udev_ports = g_list_prepend (self->priv->udev_ports, + g_object_ref (udev_port)); + } +} + +void +mm_device_release_port (MMDevice *self, + GUdevDevice *udev_port) +{ + GList *found; + + found = g_list_find_custom (self->priv->udev_ports, + udev_port, + (GCompareFunc)udev_port_cmp); + if (found) { + g_object_unref (found->data); + self->priv->udev_ports = g_list_delete_link (self->priv->udev_ports, found); + } +} + +/*****************************************************************************/ + +static void +unexport_modem (MMDevice *self) +{ + gchar *path; + + g_assert (MM_IS_BASE_MODEM (self->priv->modem)); + g_assert (G_IS_DBUS_OBJECT_MANAGER (self->priv->object_manager)); + + path = g_strdup (g_dbus_object_get_object_path (G_DBUS_OBJECT (self->priv->modem))); + if (path != NULL) { + g_dbus_object_manager_server_unexport (self->priv->object_manager, path); + g_object_set (self->priv->modem, + MM_BASE_MODEM_CONNECTION, NULL, + NULL); + + mm_dbg ("Unexported modem '%s' from path '%s'", + g_udev_device_get_sysfs_path (self->priv->udev_device), + path); + g_free (path); + } +} + +/*****************************************************************************/ + +static void +export_modem (MMDevice *self) +{ + GDBusConnection *connection = NULL; + static guint32 id = 0; + gchar *path; + + g_assert (MM_IS_BASE_MODEM (self->priv->modem)); + g_assert (G_IS_DBUS_OBJECT_MANAGER (self->priv->object_manager)); + + /* If modem not yet valid (not fully initialized), don't export it */ + if (!mm_base_modem_get_valid (self->priv->modem)) { + mm_dbg ("Modem '%s' not yet fully initialized", + g_udev_device_get_sysfs_path (self->priv->udev_device)); + return; + } + + /* Don't export already exported modems */ + g_object_get (self->priv->modem, + "g-object-path", &path, + NULL); + if (path) { + g_free (path); + mm_dbg ("Modem '%s' already exported", + g_udev_device_get_sysfs_path (self->priv->udev_device)); + return; + } + + /* No outstanding port tasks, so if the modem is valid we can export it */ + + path = g_strdup_printf (MM_DBUS_MODEM_PREFIX "/%d", id++); + g_object_get (self->priv->object_manager, + "connection", &connection, + NULL); + g_object_set (self->priv->modem, + "g-object-path", path, + MM_BASE_MODEM_CONNECTION, connection, + NULL); + g_object_unref (connection); + + g_dbus_object_manager_server_export (self->priv->object_manager, + G_DBUS_OBJECT_SKELETON (self->priv->modem)); + + mm_dbg ("Exported modem '%s' at path '%s'", + g_udev_device_get_sysfs_path (self->priv->udev_device), + path); + + /* Once connected, dump additional debug info about the modem */ + mm_dbg ("(%s): '%s' modem, VID 0x%04X PID 0x%04X (%s)", + path, + mm_base_modem_get_plugin (self->priv->modem), + (mm_base_modem_get_vendor_id (self->priv->modem) & 0xFFFF), + (mm_base_modem_get_product_id (self->priv->modem) & 0xFFFF), + g_udev_device_get_subsystem (self->priv->udev_device)); + + g_free (path); +} + +/*****************************************************************************/ + +void +mm_device_remove_modem (MMDevice *self) +{ + if (!self->priv->modem) + return; + + unexport_modem (self); + + /* Run dispose before unref-ing, in order to cleanup the SIM object, + * if any (which also holds a reference to the modem object) */ + g_object_run_dispose (G_OBJECT (self->priv->modem)); + g_clear_object (&(self->priv->modem)); + g_clear_object (&(self->priv->object_manager)); +} + +/*****************************************************************************/ + +static void +modem_valid (MMBaseModem *modem, + GParamSpec *pspec, + MMDevice *self) +{ + if (!mm_base_modem_get_valid (modem)) { + /* Modem no longer valid */ + mm_device_remove_modem (self); + } else { + /* Modem now valid, export it */ + export_modem (self); + } +} + +gboolean +mm_device_create_modem (MMDevice *self, + GDBusObjectManagerServer *object_manager, + GError **error) +{ + g_assert (self->priv->modem == NULL); + g_assert (self->priv->object_manager == NULL); + g_assert (self->priv->udev_ports != NULL); + + mm_info ("Creating modem with plugin '%s' and '%u' ports", + mm_plugin_get_name (self->priv->plugin), + g_list_length (self->priv->udev_ports)); + + self->priv->modem = mm_plugin_create_modem (self->priv->plugin, + self->priv->udev_ports, + error); + if (self->priv->modem) { + /* Keep the object manager */ + self->priv->object_manager = g_object_ref (object_manager); + + /* We want to get notified when the modem becomes valid/invalid */ + g_signal_connect (self->priv->modem, + "notify::" MM_BASE_MODEM_VALID, + G_CALLBACK (modem_valid), + self); + } + + return !!self->priv->modem; +} + +/*****************************************************************************/ + +GUdevDevice * +mm_device_peek_udev_device (MMDevice *self) +{ + return self->priv->udev_device; +} + +GUdevDevice * +mm_device_get_udev_device (MMDevice *self) +{ + return G_UDEV_DEVICE (g_object_ref (self->priv->udev_device)); +} + +MMPlugin * +mm_device_peek_plugin (MMDevice *self) +{ + return self->priv->plugin; +} + +MMPlugin * +mm_device_get_plugin (MMDevice *self) +{ + return MM_PLUGIN (g_object_ref (self->priv->plugin)); +} + +MMBaseModem * +mm_device_peek_modem (MMDevice *self) +{ + return (self->priv->modem ? + MM_BASE_MODEM (self->priv->modem) : + NULL); +} + +MMBaseModem * +mm_device_get_modem (MMDevice *self) +{ + return (self->priv->modem ? + MM_BASE_MODEM (g_object_ref (self->priv->modem)) : + NULL); +} + +/*****************************************************************************/ + +MMDevice * +mm_device_new (GUdevDevice *udev_device, + MMPlugin *plugin) +{ + return MM_DEVICE (g_object_new (MM_TYPE_DEVICE, + MM_DEVICE_UDEV_DEVICE, udev_device, + MM_DEVICE_PLUGIN, plugin, + NULL)); +} + +static void +mm_device_init (MMDevice *self) +{ + /* Initialize private data */ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), + MM_TYPE_DEVICE, + MMDevicePrivate); +} + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MMDevice *self = MM_DEVICE (object); + + switch (prop_id) { + case PROP_UDEV_DEVICE: + /* construct only */ + self->priv->udev_device = g_value_dup_object (value); + break; + case PROP_PLUGIN: + /* construct only */ + self->priv->plugin = g_value_dup_object (value); + break; + case PROP_MODEM: + g_clear_object (&(self->priv->modem)); + self->priv->modem = g_value_dup_object (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) +{ + MMDevice *self = MM_DEVICE (object); + + switch (prop_id) { + case PROP_UDEV_DEVICE: + g_value_set_object (value, self->priv->udev_device); + break; + case PROP_PLUGIN: + g_value_set_object (value, self->priv->plugin); + break; + case PROP_MODEM: + g_value_set_object (value, self->priv->modem); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ + MMDevice *self = MM_DEVICE (object); + + g_clear_object (&(self->priv->udev_device)); + g_clear_object (&(self->priv->plugin)); + g_list_free_full (self->priv->udev_ports, (GDestroyNotify)g_object_unref); + g_clear_object (&(self->priv->modem)); + + G_OBJECT_CLASS (mm_device_parent_class)->dispose (object); +} + +static void +mm_device_class_init (MMDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMDevicePrivate)); + + /* Virtual methods */ + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->dispose = dispose; + + properties[PROP_UDEV_DEVICE] = + g_param_spec_object (MM_DEVICE_UDEV_DEVICE, + "UDev Device", + "UDev device object", + G_UDEV_TYPE_DEVICE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_UDEV_DEVICE, properties[PROP_UDEV_DEVICE]); + + properties[PROP_PLUGIN] = + g_param_spec_object (MM_DEVICE_PLUGIN, + "Plugin", + "Best plugin to manage this device", + MM_TYPE_PLUGIN, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_PLUGIN, properties[PROP_PLUGIN]); + + properties[PROP_MODEM] = + g_param_spec_object (MM_DEVICE_MODEM, + "Modem", + "The modem object", + MM_TYPE_BASE_MODEM, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]); +} diff --git a/src/mm-device.h b/src/mm-device.h new file mode 100644 index 00000000..dab11b7e --- /dev/null +++ b/src/mm-device.h @@ -0,0 +1,74 @@ +/* -*- 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) 2012 Google, Inc. + */ + +#ifndef MM_DEVICE_H +#define MM_DEVICE_H + +#include <glib.h> +#include <glib-object.h> + +#include <gudev/gudev.h> + +#include "mm-plugin.h" + +#define MM_TYPE_DEVICE (mm_device_get_type ()) +#define MM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_DEVICE, MMDevice)) +#define MM_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_DEVICE, MMDeviceClass)) +#define MM_IS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_DEVICE)) +#define MM_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_DEVICE)) +#define MM_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_DEVICE, MMDeviceClass)) + +typedef struct _MMDevice MMDevice; +typedef struct _MMDeviceClass MMDeviceClass; +typedef struct _MMDevicePrivate MMDevicePrivate; + +#define MM_DEVICE_UDEV_DEVICE "udev-device" +#define MM_DEVICE_PLUGIN "plugin" +#define MM_DEVICE_MODEM "modem" + +struct _MMDevice { + GObject parent; + MMDevicePrivate *priv; +}; + +struct _MMDeviceClass { + GObjectClass parent; +}; + +GType mm_device_get_type (void); + +MMDevice *mm_device_new (GUdevDevice *udev_device, + MMPlugin *plugin); + +void mm_device_grab_port (MMDevice *self, + GUdevDevice *udev_port); +void mm_device_release_port (MMDevice *self, + GUdevDevice *udev_port); +gboolean mm_device_owns_port (MMDevice *self, + GUdevDevice *udev_port); + +gboolean mm_device_create_modem (MMDevice *self, + GDBusObjectManagerServer *object_manager, + GError **error); +void mm_device_remove_modem (MMDevice *self); + +GUdevDevice *mm_device_peek_udev_device (MMDevice *self); +GUdevDevice *mm_device_get_udev_device (MMDevice *self); +MMPlugin *mm_device_peek_plugin (MMDevice *self); +MMPlugin *mm_device_get_plugin (MMDevice *self); +MMBaseModem *mm_device_peek_modem (MMDevice *self); +MMBaseModem *mm_device_get_modem (MMDevice *self); + +#endif /* MM_DEVICE_H */ diff --git a/src/mm-manager.c b/src/mm-manager.c index 7000f038..fc1e4292 100644 --- a/src/mm-manager.c +++ b/src/mm-manager.c @@ -27,17 +27,13 @@ #include <mm-gdbus-manager.h> #include "mm-manager.h" +#include "mm-device.h" #include "mm-plugin-manager.h" #include "mm-auth.h" #include "mm-plugin.h" #include "mm-log.h" #include "mm-port-probe-cache.h" -static void grab_port (MMManager *manager, - MMPlugin *plugin, - GUdevDevice *device, - GUdevDevice *physical_device); - static void initable_iface_init (GInitableIface *iface); G_DEFINE_TYPE_EXTENDED (MMManager, mm_manager, MM_GDBUS_TYPE_ORG_FREEDESKTOP_MODEM_MANAGER1_SKELETON, 0, @@ -60,9 +56,9 @@ struct _MMManagerPrivate { GCancellable *authp_cancellable; /* The Plugin Manager object */ MMPluginManager *plugin_manager; - /* The container of currently available modems */ - GHashTable *modems; - /* DBus The Object Manager server */ + /* The container of devices being prepared */ + GHashTable *devices; + /* The Object Manager server */ GDBusObjectManagerServer *object_manager; }; @@ -95,306 +91,145 @@ find_port_support_context_free (FindPortSupportContext *ctx) g_free (ctx); } -static void -remove_modem (MMManager *manager, - MMBaseModem *modem) -{ - gchar *path; - gchar *device; - - device = g_strdup (mm_base_modem_get_device (modem)); - path = g_strdup (g_dbus_object_get_object_path (G_DBUS_OBJECT (modem))); - - /* If we get DBus object path, modem was exported */ - if (path) { - g_dbus_object_manager_server_unexport (manager->priv->object_manager, path); - g_object_set (modem, - MM_BASE_MODEM_CONNECTION, NULL, - NULL); - - mm_dbg ("Unexported modem '%s' from path '%s'", device, path); - g_free (path); - } else { - mm_dbg ("Removing modem '%s', which wasn't exported yet", device); - } - - /* Run dispose before unref-ing, in order to cleanup the SIM object, - * if any (which also holds a reference to the modem object) */ - g_object_run_dispose (G_OBJECT (modem)); - g_hash_table_remove (manager->priv->modems, device); - g_free (device); -} - -static void -debug_modem_info (MMManager *self, - MMBaseModem *modem, - const gchar *path) -{ - GUdevDevice *physdev; - const gchar *subsys; - - physdev = g_udev_client_query_by_sysfs_path (self->priv->udev, - mm_base_modem_get_device (modem)); - subsys = (physdev ? - g_udev_device_get_subsystem (physdev) : - NULL); - - mm_dbg ("(%s): '%s' modem, VID 0x%04X PID 0x%04X (%s)", - path, - mm_base_modem_get_plugin (modem), - (mm_base_modem_get_vendor_id (modem) & 0xFFFF), - (mm_base_modem_get_product_id (modem) & 0xFFFF), - subsys ? subsys : "unknown"); - - if (physdev) - g_object_unref (physdev); -} - -static void -check_export_modem (MMManager *self, - MMBaseModem *modem) -{ - GError *error = NULL; - static guint32 id = 0; - const gchar *modem_physdev; - const gchar *name; - const gchar *subsys; - gchar *path; - - /* A modem is only exported to D-Bus when both of the following are true: - * - * 1) the modem is valid - * 2) all ports the modem provides have either been grabbed or are - * unsupported by any plugin - * - * This ensures that all the modem's ports are completely ready before - * any clients can do anything with it. - * - * FIXME: if udev or the kernel are really slow giving us ports, there's a - * chance that a port could show up after the modem is already created and - * all other ports are already handled. That chance is very small though. - */ - - modem_physdev = mm_base_modem_get_device (modem); - g_assert (modem_physdev); - - /* Check for ports that are in the process of being interrogated by plugins */ - if (mm_plugin_manager_is_finding_device_support (self->priv->plugin_manager, - modem_physdev, - &subsys, - &name)) { - mm_dbg ("(%s/%s): outstanding support task prevents export of '%s'", - subsys, name, modem_physdev); - return; - } - - /* Plugin manager is not trying to find more ports supported by this device, - * so we can organize the ports now (if not done already). */ - if (!mm_base_modem_organize_ports (modem, &error)) { - /* If the ports were not properly organized, the modem will be marked as - * invalid and therefore removed */ - mm_err ("Failed to organize modem ports: '%s'", - error->message); - g_error_free (error); - remove_modem (self, modem); - return; - } - - /* If modem not yet valid (not fully initialized), don't export it */ - if (!mm_base_modem_get_valid (modem)) - return; - - /* Don't export already exported modems */ - g_object_get (modem, - "g-object-path", &path, - NULL); - if (path) { - g_free (path); - mm_dbg ("Modem '%s' already exported", modem_physdev); - return; - } - - /* No outstanding port tasks, so if the modem is valid we can export it */ - path = g_strdup_printf (MM_DBUS_MODEM_PREFIX "/%d", id++); - g_object_set (modem, - "g-object-path", path, - MM_BASE_MODEM_CONNECTION, self->priv->connection, - NULL); - g_dbus_object_manager_server_export (self->priv->object_manager, - G_DBUS_OBJECT_SKELETON (modem)); - mm_dbg ("Exported modem '%s' at path '%s'", modem_physdev, path); - - /* Once connected, dump additional debug info about the modem */ - debug_modem_info (self, modem, path); - g_free (path); -} - -static void -modem_valid (MMBaseModem *modem, - GParamSpec *pspec, - gpointer user_data) -{ - MMManager *manager = MM_MANAGER (user_data); - - if (mm_base_modem_get_valid (modem)) - check_export_modem (manager, modem); - else - remove_modem (manager, modem); -} - -#define MANAGER_PLUGIN_TAG "manager-plugin" - -static void -add_modem (MMManager *manager, - MMBaseModem *modem, - MMPlugin *plugin) -{ - const gchar *device; - - device = mm_base_modem_get_device (modem); - if (!g_hash_table_lookup (manager->priv->modems, device)) { - mm_dbg ("Added modem %s", device); - g_hash_table_insert (manager->priv->modems, - g_strdup (device), - modem); - g_object_set_data (G_OBJECT (modem), MANAGER_PLUGIN_TAG, plugin); - g_signal_connect (modem, "notify::" MM_BASE_MODEM_VALID, G_CALLBACK (modem_valid), manager); - } - - check_export_modem (manager, modem); -} - -static MMBaseModem * -find_modem_for_device (MMManager *manager, - const gchar *device) +static MMDevice * +find_device_by_modem (MMManager *manager, + MMBaseModem *modem) { GHashTableIter iter; gpointer key, value; - g_hash_table_iter_init (&iter, manager->priv->modems); + g_hash_table_iter_init (&iter, manager->priv->devices); while (g_hash_table_iter_next (&iter, &key, &value)) { - MMBaseModem *candidate = MM_BASE_MODEM (value); + MMDevice *candidate = MM_DEVICE (value); - if (g_str_equal (device, - mm_base_modem_get_device (candidate))) + if (modem == mm_device_peek_modem (candidate)) return candidate; } - return NULL; } -static MMBaseModem * -find_modem_for_port (MMManager *manager, - const gchar *subsys, - const gchar *name) +static MMDevice * +find_device_by_port (MMManager *manager, + GUdevDevice *port) { GHashTableIter iter; gpointer key, value; - g_hash_table_iter_init (&iter, manager->priv->modems); + g_hash_table_iter_init (&iter, manager->priv->devices); while (g_hash_table_iter_next (&iter, &key, &value)) { - MMBaseModem *candidate = MM_BASE_MODEM (value); + MMDevice *candidate = MM_DEVICE (value); - if (mm_base_modem_owns_port (candidate, subsys, name)) + if (mm_device_owns_port (candidate, port)) return candidate; } return NULL; } +static MMDevice * +find_device_by_sysfs_path (MMManager *self, + const gchar *sysfs_path) +{ + return g_hash_table_lookup (self->priv->devices, + sysfs_path); +} + +static MMDevice * +find_device_by_udev_device (MMManager *manager, + GUdevDevice *udev_device) +{ + return find_device_by_sysfs_path (manager, g_udev_device_get_sysfs_path (udev_device)); +} + static void find_port_support_ready_cb (MMPluginManager *plugin_manager, GAsyncResult *result, FindPortSupportContext *ctx) { GError *error = NULL; + MMDevice *device; MMPlugin *best_plugin; + /* Look for the container device, if any */ + device = find_device_by_udev_device (ctx->manager, ctx->physical_device); + best_plugin = mm_plugin_manager_find_port_support_finish (plugin_manager, result, &error); if (!best_plugin) { - MMBaseModem *existing; - if (error) { - mm_dbg ("(%s/%s): error checking support: '%s'", - g_udev_device_get_subsystem (ctx->device), - g_udev_device_get_name (ctx->device), - error->message); + mm_warn ("(%s/%s): error checking support: '%s'", + g_udev_device_get_subsystem (ctx->device), + g_udev_device_get_name (ctx->device), + error->message); g_error_free (error); } else { mm_dbg ("(%s/%s): not supported by any plugin", g_udev_device_get_subsystem (ctx->device), g_udev_device_get_name (ctx->device)); } - - /* So we couldn't get a plugin for this port, we should anyway check if - * there is already an existing modem for the physical device, and if - * so, check if it can already be exported. */ - existing = find_modem_for_device ( - ctx->manager, - g_udev_device_get_sysfs_path (ctx->physical_device)); - if (existing) - check_export_modem (ctx->manager, existing); } else { - mm_dbg ("(%s/%s): found plugin '%s' giving best support", + /* Found a best plugin for the port */ + + if (!device) { + /* Create a generic device to track the available ports, and add it to the + * manager. */ + device = mm_device_new (ctx->physical_device, + best_plugin); + g_hash_table_insert (ctx->manager->priv->devices, + g_strdup (g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device))), + device); + } + else if (!g_str_equal (mm_plugin_get_name (mm_device_peek_plugin (device)), + mm_plugin_get_name (best_plugin))) { + /* Warn if the best plugin found for this port differs from the + * best plugin found for the the first grabbed port */ + mm_warn ("(%s/%s): plugin mismatch error (expected: '%s', got: '%s')", + g_udev_device_get_subsystem (ctx->device), + g_udev_device_get_name (ctx->device), + mm_plugin_get_name (mm_device_peek_plugin (device)), + mm_plugin_get_name (best_plugin)); + } + + /* Add the port to the device */ + mm_dbg ("(%s/%s): added to device managed by plugin '%s'", g_udev_device_get_subsystem (ctx->device), g_udev_device_get_name (ctx->device), - mm_plugin_get_name ((MMPlugin *)best_plugin)); - - grab_port (ctx->manager, - best_plugin, - ctx->device, - ctx->physical_device); + mm_plugin_get_name (mm_device_peek_plugin (device))); + mm_device_grab_port (device, ctx->device); } - find_port_support_context_free (ctx); -} + if (device) { + const gchar *subsys; + const gchar *name; -static void -grab_port (MMManager *manager, - MMPlugin *plugin, - GUdevDevice *device, - GUdevDevice *physical_device) -{ - GError *error = NULL; - MMBaseModem *modem; - MMBaseModem *existing; - - existing = g_hash_table_lookup (manager->priv->modems, - g_udev_device_get_sysfs_path (physical_device)); - - /* While grabbing the first port, modem will get created */ - modem = mm_plugin_grab_port (plugin, - g_udev_device_get_subsystem (device), - g_udev_device_get_name (device), - existing, - &error); - if (!modem) { - mm_warn ("plugin '%s' claimed to support %s/%s but couldn't: (%d) %s", - mm_plugin_get_name (plugin), - g_udev_device_get_subsystem (device), - g_udev_device_get_name (device), - error ? error->code : -1, - (error && error->message) ? error->message : "(unknown)"); - g_clear_error (&error); - - if (existing) - check_export_modem (manager, existing); - return; + /* This port was probed after having created the modem */ + if (mm_device_peek_modem (device)) { + mm_dbg ("(%s/%s): port added to existing modem", + g_udev_device_get_subsystem (ctx->device), + g_udev_device_get_name (ctx->device)); + } + /* Every time we get a supports check result, we need to see if there + * are ports of the same device still being probed. */ + else if (mm_plugin_manager_is_finding_device_support (plugin_manager, + g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device)), + &subsys, + &name)) { + mm_dbg ("(%s/%s): outstanding support task prevents export of '%s'", + subsys, name, g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device))); + } + /* Plugin manager is not trying to find more ports supported by this + * device, so we can create the modem now! */ + else { + if (!mm_device_create_modem (device, ctx->manager->priv->object_manager, &error)) { + mm_warn ("Couldn't create '%s' modem for device at '%s': %s", + g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device)), + mm_plugin_get_name (mm_device_peek_plugin (device)), + error ? error->message : "Unknown error"); + } + } } - mm_info ("(%s): modem %s claimed port %s", - mm_plugin_get_name (plugin), - mm_base_modem_get_device (modem), - g_udev_device_get_name (device)); - - if (existing) { - g_assert (existing == modem); - check_export_modem (manager, modem); - } else { - /* If the modem was just created, store it */ - add_modem (manager, modem, plugin); - } + find_port_support_context_free (ctx); } static GUdevDevice * @@ -461,18 +296,17 @@ find_physical_device (GUdevDevice *child) static void device_added (MMManager *manager, - GUdevDevice *device) + GUdevDevice *port) { const char *subsys, *name, *physdev_path, *physdev_subsys; gboolean is_candidate; GUdevDevice *physdev = NULL; - MMPlugin *plugin; - MMBaseModem *existing; + MMDevice *existing; - g_return_if_fail (device != NULL); + g_return_if_fail (port != NULL); - subsys = g_udev_device_get_subsystem (device); - name = g_udev_device_get_name (device); + subsys = g_udev_device_get_subsystem (port); + name = g_udev_device_get_name (port); /* ignore VTs */ if (strncmp (name, "tty", 3) == 0 && isdigit (name[3])) @@ -485,18 +319,18 @@ device_added (MMManager *manager, * the device to a specific ModemManager driver, we need to ensure that all * rules have been processed before handling a device. */ - is_candidate = g_udev_device_get_property_as_boolean (device, "ID_MM_CANDIDATE"); + is_candidate = g_udev_device_get_property_as_boolean (port, "ID_MM_CANDIDATE"); if (!is_candidate) return; - if (find_modem_for_port (manager, subsys, name)) + if (find_device_by_port (manager, port)) return; /* Find the port's physical device's sysfs path. This is the kernel device * that "owns" all the ports of the device, like the USB device or the PCI * device the provides each tty or network port. */ - physdev = find_physical_device (device); + physdev = find_physical_device (port); if (!physdev) { /* Warn about it, but filter out some common ports that we know don't have * anything to do with mobile broadband. @@ -545,10 +379,7 @@ device_added (MMManager *manager, * asking all plugins whether they support this port, just let the owning * plugin check if it supports the port. */ - existing = find_modem_for_device (manager, physdev_path); - plugin = (existing ? - MM_PLUGIN (g_object_get_data (G_OBJECT (existing), MANAGER_PLUGIN_TAG)) : - NULL); + existing = find_device_by_udev_device (manager, physdev); /* Launch supports check in the Plugin Manager */ mm_plugin_manager_find_port_support ( @@ -556,12 +387,12 @@ device_added (MMManager *manager, subsys, name, physdev_path, - plugin, - existing, + existing ? mm_device_peek_plugin (existing) : NULL, + existing ? mm_device_peek_modem (existing) : NULL, (GAsyncReadyCallback)find_port_support_ready_cb, find_port_support_context_new (manager, - device, - physdev)); + port, + physdev)); out: if (physdev) @@ -569,48 +400,45 @@ out: } static void -device_removed (MMManager *manager, - GUdevDevice *device) +device_removed (MMManager *self, + GUdevDevice *udev_device) { - MMBaseModem *modem; - const char *subsys, *name; + MMDevice *device; - g_return_if_fail (device != NULL); - - subsys = g_udev_device_get_subsystem (device); - name = g_udev_device_get_name (device); + g_return_if_fail (udev_device != NULL); /* Ensure cached port probe infos get removed when the port is gone */ - mm_port_probe_cache_remove (device); + mm_port_probe_cache_remove (udev_device); - if (strcmp (subsys, "usb") != 0) { - /* find_modem_for_port handles tty and net removal */ - modem = find_modem_for_port (manager, subsys, name); - if (modem) { + if (!g_str_equal (g_udev_device_get_subsystem (udev_device), "usb")) { + /* Handle tty/net port removal */ + device = find_device_by_port (self, udev_device); + if (device) { mm_info ("(%s/%s): released by modem %s", - subsys, - name, - mm_base_modem_get_device (modem)); - mm_base_modem_release_port (modem, subsys, name); - return; + g_udev_device_get_subsystem (udev_device), + g_udev_device_get_name (udev_device), + g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device))); + mm_device_release_port (device, udev_device); } - } else { - /* This case is designed to handle the case where, at least with kernel 2.6.31, unplugging - * an in-use ttyACMx device results in udev generating remove events for the usb, but the - * ttyACMx device (subsystem tty) is not removed, since it was in-use. So if we have not - * found a modem for the port (above), we're going to look here to see if we have a modem - * associated with the newly removed device. If so, we'll remove the modem, since the - * device has been removed. That way, if the device is reinserted later, we'll go through - * the process of exporting it. - */ - const char *sysfs_path = g_udev_device_get_sysfs_path (device); - modem = find_modem_for_device (manager, sysfs_path); - if (modem) { - mm_dbg ("Removing modem claimed by removed device %s", sysfs_path); - remove_modem (manager, modem); - return; - } + return; + } + + /* This case is designed to handle the case where, at least with kernel 2.6.31, unplugging + * an in-use ttyACMx device results in udev generating remove events for the usb, but the + * ttyACMx device (subsystem tty) is not removed, since it was in-use. So if we have not + * found a modem for the port (above), we're going to look here to see if we have a modem + * associated with the newly removed device. If so, we'll remove the modem, since the + * device has been removed. That way, if the device is reinserted later, we'll go through + * the process of exporting it. + */ + device = find_device_by_udev_device (self, udev_device); + if (device) { + mm_dbg ("Removing device %s", + g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device))); + mm_device_remove_modem (device); + g_hash_table_remove (self->priv->devices, device); + return; } /* Maybe a plugin is checking whether or not the port is supported. @@ -631,7 +459,6 @@ handle_uevent (GUdevClient *client, /* A bit paranoid */ subsys = g_udev_device_get_subsystem (device); g_return_if_fail (subsys != NULL); - g_return_if_fail (!strcmp (subsys, "tty") || !strcmp (subsys, "net") || !strcmp (subsys, "usb")); /* We only care about tty/net devices when adding modem ports, @@ -676,17 +503,28 @@ remove_disable_ready (MMBaseModem *modem, GAsyncResult *res, MMManager *self) { + MMDevice *device; + /* We don't care about errors disabling at this point */ mm_base_modem_disable_finish (modem, res, NULL); - remove_modem (self, modem); + + device = find_device_by_modem (self, modem); + if (device) { + mm_device_remove_modem (device); + g_hash_table_remove (self->priv->devices, device); + } } static void foreach_disable (gpointer key, - MMBaseModem *modem, + MMDevice *device, MMManager *self) { - mm_base_modem_disable (modem, (GAsyncReadyCallback)remove_disable_ready, self); + MMBaseModem *modem; + + modem = mm_device_peek_modem (device); + if (modem) + mm_base_modem_disable (modem, (GAsyncReadyCallback)remove_disable_ready, self); } void @@ -698,7 +536,7 @@ mm_manager_shutdown (MMManager *self) /* Cancel all ongoing auth requests */ g_cancellable_cancel (self->priv->authp_cancellable); - g_hash_table_foreach (self->priv->modems, (GHFunc)foreach_disable, self); + g_hash_table_foreach (self->priv->devices, (GHFunc)foreach_disable, self); /* Disabling may take a few iterations of the mainloop, so the caller * has to iterate the mainloop until all devices have been disabled and @@ -709,10 +547,20 @@ mm_manager_shutdown (MMManager *self) guint32 mm_manager_num_modems (MMManager *self) { + GHashTableIter iter; + gpointer key, value; + guint32 n; + g_return_val_if_fail (self != NULL, 0); g_return_val_if_fail (MM_IS_MANAGER (self), 0); - return g_hash_table_size (self->priv->modems); + n = 0; + g_hash_table_iter_init (&iter, self->priv->devices); + while (g_hash_table_iter_next (&iter, &key, &value)) { + n += !!mm_device_peek_modem (MM_DEVICE (value)); + } + + return n; } /*****************************************************************************/ @@ -894,8 +742,8 @@ mm_manager_init (MMManager *manager) priv->authp = mm_auth_get_provider (); priv->authp_cancellable = g_cancellable_new (); - /* Setup internal list of modem objects */ - priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + /* Setup internal lists of device objects */ + priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); /* Setup UDev client */ priv->udev = g_udev_client_new (subsys); @@ -947,7 +795,7 @@ finalize (GObject *object) { MMManagerPrivate *priv = MM_MANAGER (object)->priv; - g_hash_table_destroy (priv->modems); + g_hash_table_destroy (priv->devices); mm_port_probe_cache_clear (); diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c index f5b0bd11..3a289582 100644 --- a/src/mm-plugin-base.c +++ b/src/mm-plugin-base.c @@ -759,30 +759,86 @@ supports_port_cancel (MMPlugin *plugin, } static MMBaseModem * -grab_port (MMPlugin *plugin, - const char *subsys, - const char *name, - MMBaseModem *existing, - GError **error) +create_modem (MMPlugin *self, + GList *ports, + GError **error) { - MMPluginBase *self = MM_PLUGIN_BASE (plugin); - MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); - MMPortProbe *probe; MMBaseModem *modem = NULL; - char *key; + MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); + GList *probes = NULL; + GList *l; + const gchar *name, *subsys, *sysfs_path, *driver; + guint16 vendor = 0, product = 0; + + /* Get the port probe results for each of the ports */ + for (l = ports; l; l = g_list_next (l)) { + MMPortProbe *probe; + gchar *key; - key = get_key (subsys, name); - probe = g_hash_table_lookup (priv->tasks, key); - g_assert (probe); + subsys = g_udev_device_get_subsystem (G_UDEV_DEVICE (l->data)); + name = g_udev_device_get_name (G_UDEV_DEVICE (l->data)); - /* Let the modem grab the port */ - modem = MM_PLUGIN_BASE_GET_CLASS (self)->grab_port (self, - existing, - probe, - error); + key = get_key (subsys, name); + probe = g_hash_table_lookup (priv->tasks, key); + g_assert (probe); + probes = g_list_prepend (probes, g_object_ref (probe)); + g_free (key); + } + + /* Get info from the first probe in the list */ + subsys = mm_port_probe_get_port_subsys (probes->data); + name = mm_port_probe_get_port_name (probes->data); + sysfs_path = mm_port_probe_get_port_physdev (probes->data); + driver = mm_port_probe_get_port_driver (probes->data); + + /* Vendor and Product IDs are really optional, we'll just warn if they + * cannot get loaded */ + if (!mm_plugin_base_get_device_ids (MM_PLUGIN_BASE (self), subsys, name, &vendor, &product)) + mm_warn ("Could not get modem vendor/product ID"); + + /* Let the plugin create the modem from the port probe results */ + modem = MM_PLUGIN_BASE_GET_CLASS (self)->create_modem (MM_PLUGIN_BASE (self), + sysfs_path, + driver, + vendor, + product, + probes, + error); + if (modem) { + /* Grab each port */ + for (l = probes; l; l = g_list_next (l)) { + GError *inner_error = NULL; + + /* If grabbing a port fails, just warn. We'll decide if the modem is + * valid or not when all ports get organized */ + if (!MM_PLUGIN_BASE_GET_CLASS (self)->grab_port (MM_PLUGIN_BASE (self), + modem, + MM_PORT_PROBE (l->data), + &inner_error)) { + mm_warn ("Could not grab port (%s/%s): '%s'", + mm_port_probe_get_port_subsys (MM_PORT_PROBE (l->data)), + mm_port_probe_get_port_name (MM_PORT_PROBE (l->data)), + inner_error ? inner_error->message : "unknown error"); + g_clear_error (&inner_error); + } + } + + /* If organizing ports fails, consider the modem invalid */ + if (!mm_base_modem_organize_ports (modem, error)) + g_clear_object (&modem); + } + + for (l = probes; l; l = g_list_next (l)) { + gchar *key; + + key = get_key (mm_port_probe_get_port_subsys (l->data), + mm_port_probe_get_port_name (l->data)); + g_hash_table_remove (priv->tasks, key); + g_free (key); + } + + g_list_free_full (probes, (GDestroyNotify) g_object_unref); - g_hash_table_remove (priv->tasks, key); - g_free (key); return modem; } @@ -797,7 +853,7 @@ plugin_init (MMPlugin *plugin_class) plugin_class->supports_port = supports_port; plugin_class->supports_port_finish = supports_port_finish; plugin_class->supports_port_cancel = supports_port_cancel; - plugin_class->grab_port = grab_port; + plugin_class->create_modem = create_modem; } static void @@ -1073,5 +1129,5 @@ mm_plugin_base_class_init (MMPluginBaseClass *klass) "Send delay for characters in the AT port, " "in microseconds", 0, G_MAXUINT64, 100000, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } diff --git a/src/mm-plugin-base.h b/src/mm-plugin-base.h index 3870fd89..c5d9bbad 100644 --- a/src/mm-plugin-base.h +++ b/src/mm-plugin-base.h @@ -58,10 +58,19 @@ struct _MMPluginBaseClass { GObjectClass parent; /* Mandatory subclass functions */ - MMBaseModem *(*grab_port) (MMPluginBase *plugin, - MMBaseModem *existing, - MMPortProbe *probe, - GError **error); + + MMBaseModem *(*create_modem) (MMPluginBase *plugin, + const gchar *sysfs_path, + const gchar *driver, + guint16 vendor, + guint16 product, + GList *probes, + GError **error); + + gboolean (*grab_port) (MMPluginBase *plugin, + MMBaseModem *modem, + MMPortProbe *probe, + GError **error); }; GType mm_plugin_base_get_type (void); diff --git a/src/mm-plugin.c b/src/mm-plugin.c index 950efc4c..6418e295 100644 --- a/src/mm-plugin.c +++ b/src/mm-plugin.c @@ -84,17 +84,14 @@ mm_plugin_supports_port_cancel (MMPlugin *plugin, } MMBaseModem * -mm_plugin_grab_port (MMPlugin *plugin, - const char *subsys, - const char *name, - MMBaseModem *existing, - GError **error) +mm_plugin_create_modem (MMPlugin *plugin, + GList *ports, + GError **error) { - g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE); - g_return_val_if_fail (subsys != NULL, FALSE); - g_return_val_if_fail (name != NULL, FALSE); + if (MM_PLUGIN_GET_INTERFACE (plugin)->create_modem) + return MM_PLUGIN_GET_INTERFACE (plugin)->create_modem (plugin, ports, error); - return MM_PLUGIN_GET_INTERFACE (plugin)->grab_port (plugin, subsys, name, existing, error); + return NULL; } /*****************************************************************************/ diff --git a/src/mm-plugin.h b/src/mm-plugin.h index 71120112..d31be1e4 100644 --- a/src/mm-plugin.h +++ b/src/mm-plugin.h @@ -83,19 +83,10 @@ struct _MMPlugin { const char *subsys, const char *name); - /* Will only be called if the plugin returns a value greater than 0 for - * the supports_port() method for this port. The plugin should create and - * return a new modem for the port's device if there is no existing modem - * to handle the port's hardware device, or should add the port to an - * existing modem and return that modem object. If an error is encountered - * while claiming the port, the error information should be returned in the - * error argument, and the plugin should return NULL. - */ - MMBaseModem * (*grab_port) (MMPlugin *self, - const char *subsys, - const char *name, - MMBaseModem *existing, - GError **error); + /* Given all the list of ports, launch creation of a new modem object */ + MMBaseModem * (*create_modem) (MMPlugin *self, + GList *ports, + GError **error); }; GType mm_plugin_get_type (void); @@ -120,10 +111,8 @@ void mm_plugin_supports_port_cancel (MMPlugin *plugin, const char *subsys, const char *name); -MMBaseModem *mm_plugin_grab_port (MMPlugin *plugin, - const char *subsys, - const char *name, - MMBaseModem *existing, +MMBaseModem *mm_plugin_create_modem (MMPlugin *plugin, + GList *ports, GError **error); #endif /* MM_PLUGIN_H */ |