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.c499
1 files changed, 420 insertions, 79 deletions
diff --git a/src/mm-manager.c b/src/mm-manager.c
index 1a93170..1dd1902 100644
--- a/src/mm-manager.c
+++ b/src/mm-manager.c
@@ -11,10 +11,11 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#include <string.h>
+#include <ctype.h>
#include <gmodule.h>
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
#include <gudev/gudev.h>
@@ -52,6 +53,21 @@ typedef struct {
GHashTable *supports;
} MMManagerPrivate;
+typedef struct {
+ MMManager *manager;
+ char *subsys;
+ char *name;
+ char *physdev_path;
+ GSList *plugins;
+ GSList *cur_plugin;
+ guint defer_id;
+ guint done_id;
+
+ guint32 best_level;
+ MMPlugin *best_plugin;
+} SupportsInfo;
+
+
static MMPlugin *
load_plugin (const char *path)
{
@@ -188,44 +204,98 @@ remove_modem (MMManager *manager, MMModem *modem)
}
static void
-modem_valid (MMModem *modem, GParamSpec *pspec, gpointer user_data)
+check_export_modem (MMManager *self, MMModem *modem)
{
- MMManager *manager = MM_MANAGER (user_data);
- MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
- static guint32 id = 0;
- char *path, *device;
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self);
+ char *modem_physdev;
+ GHashTableIter iter;
+ gpointer value;
+
+ /* 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_modem_get_device (modem);
+ g_assert (modem_physdev);
+
+ /* Check for ports that are in the process of being interrogated by plugins */
+ g_hash_table_iter_init (&iter, priv->supports);
+ while (g_hash_table_iter_next (&iter, NULL, &value)) {
+ SupportsInfo *info = value;
+
+ if (!strcmp (info->physdev_path, modem_physdev)) {
+ g_debug ("(%s/%s): outstanding support task prevents export of %s",
+ info->subsys, info->name, modem_physdev);
+ goto out;
+ }
+ }
+
+ /* Already exported? This can happen if the modem is exported and the kernel
+ * discovers another of the modem's ports.
+ */
+ if (g_object_get_data (G_OBJECT (modem), DBUS_PATH_TAG))
+ goto out;
+
+ /* No outstanding port tasks, so if the modem is valid we can export it */
if (mm_modem_get_valid (modem)) {
+ static guint32 id = 0;
+ char *path, *data_device = NULL;
+
path = g_strdup_printf (MM_DBUS_PATH"/Modems/%d", id++);
dbus_g_connection_register_g_object (priv->connection, path, G_OBJECT (modem));
g_object_set_data_full (G_OBJECT (modem), DBUS_PATH_TAG, path, (GDestroyNotify) g_free);
- device = mm_modem_get_device (modem);
- g_assert (device);
- g_debug ("Exported modem %s as %s", device, path);
- g_free (device);
+ g_debug ("Exported modem %s as %s", modem_physdev, path);
- g_signal_emit (manager, signals[DEVICE_ADDED], 0, modem);
- } else
+ g_object_get (G_OBJECT (modem), MM_MODEM_DATA_DEVICE, &data_device, NULL);
+ g_debug ("(%s): data port is %s", path, data_device);
+ g_free (data_device);
+
+ g_signal_emit (self, signals[DEVICE_ADDED], 0, modem);
+ }
+
+out:
+ g_free (modem_physdev);
+}
+
+static void
+modem_valid (MMModem *modem, GParamSpec *pspec, gpointer user_data)
+{
+ MMManager *manager = MM_MANAGER (user_data);
+
+ if (mm_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, MMModem *modem)
+add_modem (MMManager *manager, MMModem *modem, MMPlugin *plugin)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
char *device;
- gboolean valid = FALSE;
device = mm_modem_get_device (modem);
g_assert (device);
if (!g_hash_table_lookup (priv->modems, device)) {
g_hash_table_insert (priv->modems, g_strdup (device), modem);
+ g_object_set_data (G_OBJECT (modem), MANAGER_PLUGIN_TAG, plugin);
+
g_debug ("Added modem %s", device);
- g_signal_connect (modem, "notify::valid", G_CALLBACK (modem_valid), manager);
- g_object_get (modem, MM_MODEM_VALID, &valid, NULL);
- if (valid)
- modem_valid (modem, NULL, manager);
+ g_signal_connect (modem, "notify::" MM_MODEM_VALID, G_CALLBACK (modem_valid), manager);
+ check_export_modem (manager, modem);
}
g_free (device);
}
@@ -236,10 +306,8 @@ enumerate_devices_cb (gpointer key, gpointer val, gpointer user_data)
MMModem *modem = MM_MODEM (val);
GPtrArray **devices = (GPtrArray **) user_data;
const char *path;
- gboolean valid = FALSE;
- g_object_get (G_OBJECT (modem), MM_MODEM_VALID, &valid, NULL);
- if (valid) {
+ if (mm_modem_get_valid (modem)) {
path = g_object_get_data (G_OBJECT (modem), DBUS_PATH_TAG);
g_return_if_fail (path != NULL);
g_ptr_array_add (*devices, g_strdup (path));
@@ -265,15 +333,18 @@ find_modem_for_device (MMManager *manager, const char *device)
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
GHashTableIter iter;
gpointer key, value;
+ MMModem *found = NULL;
g_hash_table_iter_init (&iter, priv->modems);
- while (g_hash_table_iter_next (&iter, &key, &value)) {
- MMModem *modem = MM_MODEM (value);
+ while (g_hash_table_iter_next (&iter, &key, &value) && !found) {
+ MMModem *candidate = MM_MODEM (value);
+ char *candidate_device = mm_modem_get_device (candidate);
- if (!strcmp (device, mm_modem_get_device (modem)))
- return modem;
+ if (!strcmp (device, candidate_device))
+ found = candidate;
+ g_free (candidate_device);
}
- return NULL;
+ return found;
}
@@ -294,21 +365,11 @@ find_modem_for_port (MMManager *manager, const char *subsys, const char *name)
return NULL;
}
-typedef struct {
- MMManager *manager;
- char *subsys;
- char *name;
- GSList *plugins;
- GSList *cur_plugin;
- guint defer_id;
- guint done_id;
-
- guint32 best_level;
- MMPlugin *best_plugin;
-} SupportsInfo;
-
static SupportsInfo *
-supports_info_new (MMManager *self, const char *subsys, const char *name)
+supports_info_new (MMManager *self,
+ const char *subsys,
+ const char *name,
+ const char *physdev_path)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self);
SupportsInfo *info;
@@ -317,6 +378,7 @@ supports_info_new (MMManager *self, const char *subsys, const char *name)
info->manager = self;
info->subsys = g_strdup (subsys);
info->name = g_strdup (name);
+ info->physdev_path = g_strdup (physdev_path);
info->plugins = g_slist_copy (priv->plugins);
info->cur_plugin = info->plugins;
return info;
@@ -357,20 +419,21 @@ static void supports_callback (MMPlugin *plugin,
static void try_supports_port (MMManager *manager,
MMPlugin *plugin,
- const char *subsys,
- const char *name,
+ MMModem *existing,
SupportsInfo *info);
static gboolean
supports_defer_timeout (gpointer user_data)
{
SupportsInfo *info = user_data;
+ MMModem *existing;
+
+ existing = find_modem_for_device (info->manager, info->physdev_path);
g_debug ("(%s): re-checking support...", info->name);
try_supports_port (info->manager,
MM_PLUGIN (info->cur_plugin->data),
- info->subsys,
- info->name,
+ existing,
info);
return FALSE;
}
@@ -378,23 +441,30 @@ supports_defer_timeout (gpointer user_data)
static void
try_supports_port (MMManager *manager,
MMPlugin *plugin,
- const char *subsys,
- const char *name,
+ MMModem *existing,
SupportsInfo *info)
{
MMPluginSupportsResult result;
- result = mm_plugin_supports_port (plugin, subsys, name, supports_callback, info);
+ result = mm_plugin_supports_port (plugin,
+ info->subsys,
+ info->name,
+ info->physdev_path,
+ existing,
+ 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);
+ supports_callback (plugin, info->subsys, info->name, 0, info);
break;
case MM_PLUGIN_SUPPORTS_PORT_DEFER:
- g_debug ("(%s): (%s) deferring support check", mm_plugin_get_name (plugin), name);
+ g_debug ("(%s): (%s) deferring support check",
+ mm_plugin_get_name (plugin),
+ info->name);
if (info->defer_id)
g_source_remove (info->defer_id);
@@ -407,23 +477,54 @@ try_supports_port (MMManager *manager,
}
}
+static void
+supports_cleanup (MMManager *self,
+ const char *subsys,
+ const char *name,
+ MMModem *modem)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (self);
+ char *key;
+
+ g_return_if_fail (subsys != NULL);
+ g_return_if_fail (name != NULL);
+
+ key = get_key (subsys, name);
+ g_hash_table_remove (priv->supports, key);
+ g_free (key);
+
+ /* Each time a supports task is cleaned up, check whether the modem is
+ * now completely probed/handled and should be exported to D-Bus clients.
+ *
+ * IMPORTANT: this must be done after removing the supports into from
+ * priv->supports since check_export_modem() searches through priv->supports
+ * for outstanding supports tasks.
+ */
+ if (modem)
+ check_export_modem (self, modem);
+}
+
static gboolean
do_grab_port (gpointer user_data)
{
SupportsInfo *info = user_data;
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (info->manager);
- MMModem *modem;
+ MMModem *modem = NULL;
GError *error = NULL;
- char *key;
GSList *iter;
/* No more plugins to try */
if (info->best_plugin) {
+ MMModem *existing;
+
+ existing = g_hash_table_lookup (priv->modems, info->physdev_path);
+
/* Create the modem */
- modem = mm_plugin_grab_port (info->best_plugin, info->subsys, info->name, &error);
+ modem = mm_plugin_grab_port (info->best_plugin, info->subsys, info->name, existing, &error);
if (modem) {
guint32 modem_type = MM_MODEM_TYPE_UNKNOWN;
const char *type_name = "UNKNOWN";
+ char *device;;
g_object_get (G_OBJECT (modem), MM_MODEM_TYPE, &modem_type, NULL);
if (modem_type == MM_MODEM_TYPE_GSM)
@@ -431,13 +532,15 @@ do_grab_port (gpointer user_data)
else if (modem_type == MM_MODEM_TYPE_CDMA)
type_name = "CDMA";
+ device = mm_modem_get_device (modem);
g_message ("(%s): %s modem %s claimed port %s",
mm_plugin_get_name (info->best_plugin),
type_name,
- mm_modem_get_device (modem),
+ device,
info->name);
+ g_free (device);
- add_modem (info->manager, modem);
+ add_modem (info->manager, modem, info->best_plugin);
} else {
g_warning ("%s: plugin '%s' claimed to support %s/%s but couldn't: (%d) %s",
__func__,
@@ -446,6 +549,7 @@ do_grab_port (gpointer user_data)
info->name,
error ? error->code : -1,
(error && error->message) ? error->message : "(unknown)");
+ modem = existing;
}
}
@@ -455,10 +559,7 @@ do_grab_port (gpointer user_data)
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);
-
+ supports_cleanup (info->manager, info->subsys, info->name, modem);
return FALSE;
}
@@ -471,10 +572,7 @@ supports_callback (MMPlugin *plugin,
{
SupportsInfo *info = user_data;
MMPlugin *next_plugin = NULL;
-
- info->cur_plugin = info->cur_plugin->next;
- if (info->cur_plugin)
- next_plugin = MM_PLUGIN (info->cur_plugin->data);
+ MMModem *existing;
/* Is this plugin's result better than any one we've tried before? */
if (level > info->best_level) {
@@ -482,32 +580,147 @@ supports_callback (MMPlugin *plugin,
info->best_plugin = plugin;
}
- /* 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 there's already a modem for this port's physical device, stop asking
+ * plugins because the same plugin that owns the modem gets this port no
+ * matter what.
*/
- if ( next_plugin
- && !strcmp (mm_plugin_get_name (next_plugin), MM_PLUGIN_GENERIC_NAME)
- && info->best_plugin)
- next_plugin = NULL;
+ existing = find_modem_for_device (info->manager, info->physdev_path);
+ if (existing) {
+ MMPlugin *existing_plugin;
+
+ existing_plugin = MM_PLUGIN (g_object_get_data (G_OBJECT (existing), MANAGER_PLUGIN_TAG));
+ g_assert (existing_plugin);
+
+ if (plugin == existing_plugin) {
+ if (level == 0) {
+ /* If the plugin that just completed the support check claims not to
+ * support this port, but this plugin is clearly the right plugin
+ * since it claimed this port's physical modem, just drop the port.
+ */
+ g_debug ("(%s/%s): ignoring port unsupported by physical modem's plugin",
+ info->subsys, info->name);
+ supports_cleanup (info->manager, info->subsys, info->name, existing);
+ return;
+ }
+
+ /* Otherwise, this port was supported by the plugin that owns the
+ * port's physical modem, so we stop the supports checks anyway.
+ */
+ next_plugin = NULL;
+ } else if (info->best_plugin != existing_plugin) {
+ /* If this port hasn't yet been handled by the right plugin, stop
+ * asking all other plugins if they support this port, just let the
+ * plugin that handles this port's physical device see if it
+ * supports it.
+ */
+ next_plugin = existing_plugin;
+ } else {
+ g_debug ("(%s/%s): plugin %p (%s) existing %p (%s) info->best %p (%s)",
+ info->subsys, info->name,
+ plugin,
+ plugin ? mm_plugin_get_name (plugin) : "none",
+ existing_plugin,
+ existing_plugin ? mm_plugin_get_name (existing_plugin) : "none",
+ info->best_plugin,
+ info->best_plugin ? mm_plugin_get_name (info->best_plugin) : "none");
+ g_assert_not_reached ();
+ }
+ } else {
+ info->cur_plugin = g_slist_next (info->cur_plugin);
+ if (info->cur_plugin)
+ next_plugin = MM_PLUGIN (info->cur_plugin->data);
+ }
+
+ /* Don't bother with Generic if some other plugin already supports this port */
+ if (next_plugin) {
+ const char *next_name = mm_plugin_get_name (next_plugin);
+
+ if (info->best_plugin && !strcmp (next_name, MM_PLUGIN_GENERIC_NAME))
+ next_plugin = NULL;
+ }
if (next_plugin) {
/* Try the next plugin */
- try_supports_port (info->manager, next_plugin, info->subsys, info->name, info);
+ try_supports_port (info->manager, next_plugin, existing, info);
} else {
/* All done; let the best modem grab the port */
info->done_id = g_idle_add (do_grab_port, info);
}
}
+static GUdevDevice *
+find_physical_device (GUdevDevice *child)
+{
+ GUdevDevice *iter, *old = NULL;
+ GUdevDevice *physdev = NULL;
+ const char *subsys, *type;
+ guint32 i = 0;
+ gboolean is_usb = FALSE, is_pci = FALSE, is_pcmcia = FALSE, is_platform = FALSE;
+
+ g_return_val_if_fail (child != NULL, NULL);
+
+ iter = g_object_ref (child);
+ while (iter && i++ < 8) {
+ subsys = g_udev_device_get_subsystem (iter);
+ if (subsys) {
+ if (is_usb || !strcmp (subsys, "usb")) {
+ is_usb = TRUE;
+ type = g_udev_device_get_devtype (iter);
+ if (type && !strcmp (type, "usb_device")) {
+ physdev = iter;
+ break;
+ }
+ } else if (is_pcmcia || !strcmp (subsys, "pcmcia")) {
+ GUdevDevice *pcmcia_parent;
+ const char *tmp_subsys;
+
+ is_pcmcia = TRUE;
+
+ /* If the parent of this PCMCIA device is no longer part of
+ * the PCMCIA subsystem, we want to stop since we're looking
+ * for the base PCMCIA device, not the PCMCIA controller which
+ * is usually PCI or some other bus type.
+ */
+ pcmcia_parent = g_udev_device_get_parent (iter);
+ if (pcmcia_parent) {
+ tmp_subsys = g_udev_device_get_subsystem (pcmcia_parent);
+ if (tmp_subsys && strcmp (tmp_subsys, "pcmcia"))
+ physdev = iter;
+ g_object_unref (pcmcia_parent);
+ if (physdev)
+ break;
+ }
+ } else if (is_platform || !strcmp (subsys, "platform")) {
+ /* Take the first platform parent as the physical device */
+ is_platform = TRUE;
+ physdev = iter;
+ break;
+ } else if (is_pci || !strcmp (subsys, "pci")) {
+ is_pci = TRUE;
+ physdev = iter;
+ break;
+ }
+ }
+
+ old = iter;
+ iter = g_udev_device_get_parent (old);
+ g_object_unref (old);
+ }
+
+ return physdev;
+}
+
static void
device_added (MMManager *manager, GUdevDevice *device)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
- const char *subsys, *name;
+ const char *subsys, *name, *physdev_path, *physdev_subsys;
SupportsInfo *info;
char *key;
gboolean found;
+ GUdevDevice *physdev = NULL;
+ MMPlugin *plugin;
+ MMModem *existing;
g_return_if_fail (device != NULL);
@@ -517,20 +730,77 @@ device_added (MMManager *manager, GUdevDevice *device)
subsys = g_udev_device_get_subsystem (device);
name = g_udev_device_get_name (device);
+ /* ignore VTs */
+ if (strncmp (name, "tty", 3) == 0 && isdigit (name[3]))
+ return;
+
if (find_modem_for_port (manager, subsys, name))
return;
key = get_key (subsys, name);
found = !!g_hash_table_lookup (priv->supports, key);
- if (found) {
- g_free (key);
- return;
+ if (found)
+ goto out;
+
+ /* 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);
+ if (!physdev) {
+ /* Warn about it, but filter out some common ports that we know don't have
+ * anything to do with mobile broadband.
+ */
+ if ( strcmp (name, "console")
+ && strcmp (name, "ptmx")
+ && strcmp (name, "lo")
+ && strcmp (name, "tty")
+ && !strstr (name, "virbr"))
+ g_debug ("(%s/%s): could not get port's parent device", subsys, name);
+
+ goto out;
+ }
+
+ /* Is the device blacklisted? */
+ if (g_udev_device_get_property_as_boolean (physdev, "ID_MM_DEVICE_IGNORE")) {
+ g_debug ("(%s/%s): port's parent device is blacklisted", subsys, name);
+ goto out;
+ }
+
+ /* If the physdev is a 'platform' device that's not whitelisted, ignore it */
+ physdev_subsys = g_udev_device_get_subsystem (physdev);
+ if ( physdev_subsys
+ && !strcmp (physdev_subsys, "platform")
+ && !g_udev_device_get_property_as_boolean (physdev, "ID_MM_PLATFORM_DRIVER_PROBE")) {
+ g_debug ("(%s/%s): port's parent platform driver is not whitelisted", subsys, name);
+ goto out;
}
- info = supports_info_new (manager, subsys, name);
- g_hash_table_insert (priv->supports, key, info);
+ physdev_path = g_udev_device_get_sysfs_path (physdev);
+ if (!physdev_path) {
+ g_debug ("(%s/%s): could not get port's parent device sysfs path", subsys, name);
+ goto out;
+ }
+
+ /* Success; now ask plugins if they can handle this port */
+ info = supports_info_new (manager, subsys, name, physdev_path);
+ g_hash_table_insert (priv->supports, g_strdup (key), info);
+
+ /* If this port's physical modem is already owned by a plugin, don't bother
+ * asking all plugins whether they support this port, just let the owning
+ * plugin check if it supports the port.
+ */
+ plugin = MM_PLUGIN (info->cur_plugin->data);
+ existing = find_modem_for_device (manager, physdev_path);
+ if (existing)
+ plugin = MM_PLUGIN (g_object_get_data (G_OBJECT (existing), MANAGER_PLUGIN_TAG));
- try_supports_port (manager, MM_PLUGIN (info->cur_plugin->data), subsys, name, info);
+ try_supports_port (manager, plugin, existing, info);
+
+out:
+ if (physdev)
+ g_object_unref (physdev);
+ g_free (key);
}
static void
@@ -628,12 +898,83 @@ mm_manager_start (MMManager *manager)
priv = MM_MANAGER_GET_PRIVATE (manager);
devices = g_udev_client_query_by_subsystem (priv->udev, "tty");
- for (iter = devices; iter; iter = g_list_next (iter))
+ for (iter = devices; iter; iter = g_list_next (iter)) {
device_added (manager, G_UDEV_DEVICE (iter->data));
+ g_object_unref (G_OBJECT (iter->data));
+ }
+ g_list_free (devices);
devices = g_udev_client_query_by_subsystem (priv->udev, "net");
- for (iter = devices; iter; iter = g_list_next (iter))
+ for (iter = devices; iter; iter = g_list_next (iter)) {
device_added (manager, G_UDEV_DEVICE (iter->data));
+ g_object_unref (G_OBJECT (iter->data));
+ }
+ g_list_free (devices);
+}
+
+typedef struct {
+ MMManager *manager;
+ MMModem *modem;
+} RemoveInfo;
+
+static gboolean
+remove_disable_one (gpointer user_data)
+{
+ RemoveInfo *info = user_data;
+
+ remove_modem (info->manager, info->modem);
+ g_free (info);
+ return FALSE;
+}
+
+static void
+remove_disable_done (MMModem *modem,
+ GError *error,
+ gpointer user_data)
+{
+ RemoveInfo *info;
+
+ /* Schedule modem removal from an idle handler since we get here deep
+ * in the modem removal callchain and can't remove it quite yet from here.
+ */
+ info = g_malloc0 (sizeof (RemoveInfo));
+ info->manager = MM_MANAGER (user_data);
+ info->modem = modem;
+ g_idle_add (remove_disable_one, info);
+}
+
+void
+mm_manager_shutdown (MMManager *self)
+{
+ GList *modems, *iter;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MANAGER (self));
+
+ modems = g_hash_table_get_values (MM_MANAGER_GET_PRIVATE (self)->modems);
+ for (iter = modems; iter; iter = g_list_next (iter)) {
+ MMModem *modem = MM_MODEM (iter->data);
+
+ if (mm_modem_get_state (modem) >= MM_MODEM_STATE_ENABLING)
+ mm_modem_disable (modem, remove_disable_done, self);
+ else
+ remove_disable_done (modem, NULL, self);
+ }
+ g_list_free (modems);
+
+ /* Disabling may take a few iterations of the mainloop, so the caller
+ * has to iterate the mainloop until all devices have been disabled and
+ * removed.
+ */
+}
+
+guint32
+mm_manager_num_modems (MMManager *self)
+{
+ g_return_val_if_fail (self != NULL, 0);
+ g_return_val_if_fail (MM_IS_MANAGER (self), 0);
+
+ return g_hash_table_size (MM_MANAGER_GET_PRIVATE (self)->modems);
}
static void