aboutsummaryrefslogtreecommitdiff
path: root/src/mm-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mm-manager.c')
-rw-r--r--src/mm-manager.c313
1 files changed, 251 insertions, 62 deletions
diff --git a/src/mm-manager.c b/src/mm-manager.c
index 3d3b1ac1..c6445cf6 100644
--- a/src/mm-manager.c
+++ b/src/mm-manager.c
@@ -36,6 +36,8 @@ typedef struct {
GUdevClient *udev;
GSList *plugins;
GHashTable *modems;
+
+ GHashTable *supports;
} MMManagerPrivate;
static MMPlugin *
@@ -99,6 +101,7 @@ load_plugins (MMManager *manager)
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
GDir *dir;
const char *fname;
+ MMPlugin *generic_plugin = NULL;
if (!g_module_supported ()) {
g_warning ("GModules are not supported on your platform!");
@@ -122,10 +125,18 @@ load_plugins (MMManager *manager)
plugin = load_plugin (path);
g_free (path);
- if (plugin)
- priv->plugins = g_slist_append (priv->plugins, plugin);
+ if (plugin) {
+ if (!strcmp (mm_plugin_get_name (plugin), MM_PLUGIN_GENERIC_NAME))
+ generic_plugin = plugin;
+ else
+ priv->plugins = g_slist_append (priv->plugins, plugin);
+ }
}
+ /* Make sure the generic plugin is last */
+ if (generic_plugin)
+ priv->plugins = g_slist_append (priv->plugins, generic_plugin);
+
g_dir_close (dir);
}
@@ -179,7 +190,7 @@ modem_valid (MMModem *modem, GParamSpec *pspec, gpointer user_data)
device = mm_modem_get_device (modem);
g_assert (device);
- g_debug ("Exported modem %s", device);
+ g_debug ("Exported modem %s as %s", device, path);
g_free (device);
g_signal_emit (manager, signals[DEVICE_ADDED], 0, modem);
@@ -236,107 +247,281 @@ impl_manager_enumerate_devices (MMManager *manager,
return TRUE;
}
+static MMModem *
+find_modem_for_port (MMManager *manager, const char *subsys, const char *name)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, priv->modems);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ MMModem *modem = MM_MODEM (value);
+
+ if (mm_modem_owns_port (modem, subsys, name))
+ return modem;
+ }
+ return NULL;
+}
+
typedef struct {
- MMModem *modem;
- const char *subsys;
- const char *name;
-} FindPortInfo;
+ MMManager *manager;
+ char *subsys;
+ char *name;
+ GSList *plugins;
+ GSList *cur_plugin;
+ guint defer_id;
+
+ guint32 best_level;
+ MMPlugin *best_plugin;
+} SupportsInfo;
+
+static SupportsInfo *
+supports_info_new (MMManager *self, const char *subsys, const char *name)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self);
+ SupportsInfo *info;
+
+ info = g_malloc0 (sizeof (SupportsInfo));
+ info->manager = self;
+ info->subsys = g_strdup (subsys);
+ info->name = g_strdup (name);
+ info->plugins = g_slist_copy (priv->plugins);
+ info->cur_plugin = info->plugins;
+ return info;
+}
static void
-find_port (gpointer key, gpointer data, gpointer user_data)
+supports_info_free (SupportsInfo *info)
{
- FindPortInfo *info = user_data;
- MMModem *modem = MM_MODEM (data);
+ /* Cancel any in-process operation on the first plugin */
+ if (info->cur_plugin)
+ mm_plugin_cancel_supports_port (MM_PLUGIN (info->cur_plugin->data), info->subsys, info->name);
+
+ if (info->defer_id)
+ g_source_remove (info->defer_id);
+
+ g_free (info->subsys);
+ g_free (info->name);
+ g_slist_free (info->plugins);
+ memset (info, 0, sizeof (SupportsInfo));
+ g_free (info);
+}
- if (!info->modem && mm_modem_owns_port (modem, info->subsys, info->name))
- info->modem = modem;
+static char *
+get_key (const char *subsys, const char *name)
+{
+ return g_strdup_printf ("%s%s", subsys, name);
}
-static MMModem *
-find_modem_for_port (MMManager *manager, const char *subsys, const char *name)
+
+static void supports_callback (MMPlugin *plugin,
+ const char *subsys,
+ const char *name,
+ guint32 level,
+ gpointer user_data);
+
+static void try_supports_port (MMManager *manager,
+ MMPlugin *plugin,
+ const char *subsys,
+ const char *name,
+ SupportsInfo *info);
+
+static gboolean
+supports_defer_timeout (gpointer user_data)
{
- MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
- FindPortInfo info = { NULL, subsys, name };
+ SupportsInfo *info = user_data;
+
+ g_debug ("(%s): re-checking support...", info->name);
+ try_supports_port (info->manager,
+ MM_PLUGIN (info->cur_plugin->data),
+ info->subsys,
+ info->name,
+ info);
+ return FALSE;
+}
+
+static void
+try_supports_port (MMManager *manager,
+ MMPlugin *plugin,
+ const char *subsys,
+ const char *name,
+ SupportsInfo *info)
+{
+ MMPluginSupportsResult result;
+
+ result = mm_plugin_supports_port (plugin, subsys, name, supports_callback, info);
+
+ switch (result) {
+ case MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED:
+ /* If the plugin knows it doesn't support the modem, just call the
+ * callback and indicate 0 support.
+ */
+ supports_callback (plugin, subsys, name, 0, info);
+ break;
+ case MM_PLUGIN_SUPPORTS_PORT_DEFER:
+ g_debug ("(%s): (%s) deferring support check", mm_plugin_get_name (plugin), name);
+ if (info->defer_id)
+ g_source_remove (info->defer_id);
+
+ /* defer port detection for a bit as requested by the plugin */
+ info->defer_id = g_timeout_add (3000, supports_defer_timeout, info);
+ break;
+ case MM_PLUGIN_SUPPORTS_PORT_IN_PROGRESS:
+ default:
+ break;
+ }
+}
+
+static void
+supports_callback (MMPlugin *plugin,
+ const char *subsys,
+ const char *name,
+ guint32 level,
+ gpointer user_data)
+{
+ SupportsInfo *info = user_data;
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (info->manager);
+ MMModem *modem;
+ GError *error = NULL;
+ char *key;
+ GSList *iter;
+ MMPlugin *next_plugin = NULL;
+
+ info->cur_plugin = info->cur_plugin->next;
+ if (info->cur_plugin)
+ next_plugin = MM_PLUGIN (info->cur_plugin->data);
+
+ /* Is this plugin's result better than any one we've tried before? */
+ if (level > info->best_level) {
+ info->best_level = level;
+ info->best_plugin = plugin;
+ }
- g_hash_table_foreach (priv->modems, find_port, &info);
- return info.modem;
+ /* Prevent the generic plugin from probing devices that are already supported
+ * by other plugins. For Huawei for example, secondary ports shouldn't
+ * be probed, but the generic plugin would happily do so if allowed to.
+ */
+ if ( next_plugin
+ && !strcmp (mm_plugin_get_name (next_plugin), MM_PLUGIN_GENERIC_NAME)
+ && info->best_plugin)
+ next_plugin = NULL;
+
+ /* Try the next plugin */
+ if (next_plugin) {
+ try_supports_port (info->manager, next_plugin, info->subsys, info->name, info);
+ return;
+ }
+
+ /* No more plugins to try */
+ if (info->best_plugin) {
+ /* Create the modem */
+ modem = mm_plugin_grab_port (info->best_plugin, info->subsys, info->name, &error);
+ if (modem) {
+ guint32 modem_type = MM_MODEM_TYPE_UNKNOWN;
+ const char *type_name = "UNKNOWN";
+
+ g_object_get (G_OBJECT (modem), MM_MODEM_TYPE, &modem_type, NULL);
+ if (modem_type == MM_MODEM_TYPE_GSM)
+ type_name = "GSM";
+ else if (modem_type == MM_MODEM_TYPE_CDMA)
+ type_name = "CDMA";
+
+ g_message ("(%s): %s modem %s claimed port %s",
+ mm_plugin_get_name (info->best_plugin),
+ type_name,
+ mm_modem_get_device (modem),
+ name);
+
+ add_modem (info->manager, modem);
+ } else {
+ g_warning ("%s: plugin '%s' claimed to support %s/%s but couldn't: (%d) %s",
+ __func__,
+ mm_plugin_get_name (info->best_plugin),
+ subsys,
+ name,
+ error ? error->code : -1,
+ (error && error->message) ? error->message : "(unknown)");
+ }
+ }
+
+ /* Tell each plugin to clean up any outstanding supports task */
+ for (iter = info->plugins; iter; iter = g_slist_next (iter))
+ mm_plugin_cancel_supports_port (MM_PLUGIN (iter->data), subsys, name);
+ g_slist_free (info->plugins);
+ info->cur_plugin = info->plugins = NULL;
+
+ key = get_key (info->subsys, info->name);
+ g_hash_table_remove (priv->supports, key);
+ g_free (key);
}
static void
device_added (MMManager *manager, GUdevDevice *device)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
- GSList *iter;
- MMModem *modem = NULL;
const char *subsys, *name;
- MMPlugin *best_plugin = NULL;
- guint32 best_level = 0;
- GError *error = NULL;
+ SupportsInfo *info;
+ char *key;
+ gboolean found;
g_return_if_fail (device != NULL);
+ if (!g_slist_length (priv->plugins))
+ return;
+
subsys = g_udev_device_get_subsystem (device);
name = g_udev_device_get_name (device);
if (find_modem_for_port (manager, subsys, name))
return;
- /* Build up the list of plugins that support this port */
- for (iter = priv->plugins; iter; iter = iter->next) {
- MMPlugin *plugin = MM_PLUGIN (iter->data);
- guint32 level;
-
- level = mm_plugin_supports_port (plugin, subsys, name);
- if (level > best_level) {
- best_plugin = plugin;
- best_level = level;
- }
- }
-
- /* Let the best plugin handle this port */
- if (!best_plugin)
- return;
-
- modem = mm_plugin_grab_port (best_plugin, subsys, name, &error);
- if (modem) {
- guint32 modem_type = MM_MODEM_TYPE_UNKNOWN;
- const char *type_name = "UNKNOWN";
-
- g_object_get (G_OBJECT (modem), MM_MODEM_TYPE, &modem_type, NULL);
- if (modem_type == MM_MODEM_TYPE_GSM)
- type_name = "GSM";
- else if (modem_type == MM_MODEM_TYPE_CDMA)
- type_name = "CDMA";
-
- g_message ("(%s): %s modem %s claimed port %s",
- mm_plugin_get_name (best_plugin),
- type_name,
- mm_modem_get_device (modem),
- name);
- } else {
- g_warning ("%s: plugin '%s' claimed to support %s/%s but couldn't: (%d) %s",
- __func__, mm_plugin_get_name (best_plugin), subsys, name,
- error ? error->code : -1,
- (error && error->message) ? error->message : "(unknown)");
+ key = get_key (subsys, name);
+ found = !!g_hash_table_lookup (priv->supports, key);
+ if (found) {
+ g_free (key);
return;
}
- add_modem (manager, modem);
+ info = supports_info_new (manager, subsys, name);
+ g_hash_table_insert (priv->supports, key, info);
+
+ try_supports_port (manager, MM_PLUGIN (info->cur_plugin->data), subsys, name, info);
}
static void
device_removed (MMManager *manager, GUdevDevice *device)
{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
MMModem *modem;
const char *subsys, *name;
+ char *key;
+ SupportsInfo *info;
g_return_if_fail (device != NULL);
+ if (!g_slist_length (priv->plugins))
+ return;
+
subsys = g_udev_device_get_subsystem (device);
name = g_udev_device_get_name (device);
modem = find_modem_for_port (manager, subsys, name);
- if (modem)
+ if (modem) {
mm_modem_release_port (modem, subsys, name);
+ return;
+ }
+
+ /* Maybe a plugin is checking whether or not the port is supported */
+ key = get_key (subsys, name);
+ info = g_hash_table_lookup (priv->supports, key);
+
+ if (info) {
+ if (info->plugins)
+ mm_plugin_cancel_supports_port (MM_PLUGIN (info->plugins->data), subsys, name);
+ g_hash_table_remove (priv->supports, key);
+ }
+
+ g_free (key);
}
static void
@@ -391,6 +576,8 @@ mm_manager_init (MMManager *manager)
priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
load_plugins (manager);
+ priv->supports = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) supports_info_free);
+
priv->udev = g_udev_client_new (subsys);
g_assert (priv->udev);
g_signal_connect (priv->udev, "uevent", G_CALLBACK (handle_uevent), manager);
@@ -401,6 +588,7 @@ finalize (GObject *object)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (object);
+ g_hash_table_destroy (priv->supports);
g_hash_table_destroy (priv->modems);
g_slist_foreach (priv->plugins, (GFunc) g_object_unref, NULL);
@@ -454,3 +642,4 @@ mm_manager_class_init (MMManagerClass *manager_class)
dbus_g_error_domain_register (MM_MODEM_CONNECT_ERROR, "org.freedesktop.ModemManager.Modem", MM_TYPE_MODEM_CONNECT_ERROR);
dbus_g_error_domain_register (MM_MOBILE_ERROR, "org.freedesktop.ModemManager.Modem.Gsm", MM_TYPE_MOBILE_ERROR);
}
+