diff options
Diffstat (limited to 'src/mm-manager.c')
-rw-r--r-- | src/mm-manager.c | 561 |
1 files changed, 359 insertions, 202 deletions
diff --git a/src/mm-manager.c b/src/mm-manager.c index a19c9446..aa109a99 100644 --- a/src/mm-manager.c +++ b/src/mm-manager.c @@ -2,12 +2,12 @@ #include <string.h> #include <gmodule.h> +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include <gudev/gudev.h> #include <dbus/dbus-glib.h> #include <dbus/dbus-glib-lowlevel.h> #include "mm-manager.h" #include "mm-errors.h" -#include "mm-generic-gsm.h" -#include "mm-generic-cdma.h" #include "mm-plugin.h" static gboolean impl_manager_enumerate_devices (MMManager *manager, @@ -29,11 +29,15 @@ static guint signals[LAST_SIGNAL] = { 0 }; #define MM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MANAGER, MMManagerPrivate)) +#define DBUS_PATH_TAG "dbus-path" + typedef struct { DBusGConnection *connection; - LibHalContext *hal_ctx; + GUdevClient *udev; GSList *plugins; GHashTable *modems; + + GHashTable *supports; } MMManagerPrivate; static MMPlugin * @@ -97,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!"); @@ -120,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); } @@ -138,7 +151,7 @@ mm_manager_new (DBusGConnection *bus) if (manager) { MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); - priv->connection = bus; + priv->connection = dbus_g_connection_ref (bus); dbus_g_connection_register_g_object (priv->connection, MM_DBUS_PATH, G_OBJECT (manager)); @@ -147,296 +160,441 @@ mm_manager_new (DBusGConnection *bus) return manager; } -static char * -get_driver_name (LibHalContext *ctx, const char *udi) +static void +remove_modem (MMManager *manager, MMModem *modem) { - char *parent_udi; - char *driver = NULL; + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + char *device; - parent_udi = libhal_device_get_property_string (ctx, udi, "info.parent", NULL); - if (parent_udi) { - driver = libhal_device_get_property_string (ctx, parent_udi, "info.linux.driver", NULL); - libhal_free_string (parent_udi); - } + device = mm_modem_get_device (modem); + g_assert (device); + g_debug ("Removed modem %s", device); - return driver; + g_signal_emit (manager, signals[DEVICE_REMOVED], 0, modem); + g_hash_table_remove (priv->modems, device); + g_free (device); } -static MMModem * -create_generic_modem (MMManager *manager, const char *udi) +static void +modem_valid (MMModem *modem, GParamSpec *pspec, gpointer user_data) { + MMManager *manager = MM_MANAGER (user_data); MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); - MMModem *modem; - char **capabilities; - char **iter; - char *serial_device; - char *driver; - gboolean type_gsm = FALSE; - gboolean type_cdma = FALSE; - - capabilities = libhal_device_get_property_strlist (priv->hal_ctx, udi, "modem.command_sets", NULL); - for (iter = capabilities; iter && *iter; iter++) { - if (!strcmp (*iter, "GSM-07.07")) { - type_gsm = TRUE; - break; - } - if (!strcmp (*iter, "IS-707-A")) { - type_cdma = TRUE; - break; - } - } - g_strfreev (capabilities); - - if (!type_gsm && !type_cdma) - return NULL; + static guint32 id = 0; + char *path, *device; - serial_device = libhal_device_get_property_string (priv->hal_ctx, udi, "serial.device", NULL); - g_return_val_if_fail (serial_device != NULL, NULL); + if (mm_modem_get_valid (modem)) { + 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); - driver = get_driver_name (priv->hal_ctx, udi); - g_return_val_if_fail (driver != NULL, NULL); + device = mm_modem_get_device (modem); + g_assert (device); + g_debug ("Exported modem %s as %s", device, path); + g_free (device); - if (type_gsm) - modem = mm_generic_gsm_new (serial_device, driver); - else - modem = mm_generic_cdma_new (serial_device, driver); - - g_free (serial_device); - g_free (driver); - - if (modem) - g_debug ("Created new generic modem (%s)", udi); - else - g_warning ("Failed to create generic modem (%s)", udi); - - return modem; + g_signal_emit (manager, signals[DEVICE_ADDED], 0, modem); + } else + remove_modem (manager, modem); } static void -add_modem (MMManager *manager, const char *udi, MMModem *modem) +add_modem (MMManager *manager, MMModem *modem) { 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_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_free (device); +} - g_hash_table_insert (priv->modems, g_strdup (udi), modem); - dbus_g_connection_register_g_object (priv->connection, udi, G_OBJECT (modem)); - - g_signal_emit (manager, signals[DEVICE_ADDED], 0, modem); +static void +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) { + 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)); + } } -static MMModem * -modem_exists (MMManager *manager, const char *udi) +static gboolean +impl_manager_enumerate_devices (MMManager *manager, + GPtrArray **devices, + GError **err) { MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); - return (MMModem *) g_hash_table_lookup (priv->modems, udi); + *devices = g_ptr_array_sized_new (g_hash_table_size (priv->modems)); + g_hash_table_foreach (priv->modems, enumerate_devices_cb, devices); + + return TRUE; } -static void -create_initial_modems_from_plugins (MMManager *manager) +static MMModem * +find_modem_for_port (MMManager *manager, const char *subsys, const char *name) { MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); - GSList *iter; + GHashTableIter iter; + gpointer key, value; - for (iter = priv->plugins; iter; iter = iter->next) { - MMPlugin *plugin = MM_PLUGIN (iter->data); - char **udis; - int i; + g_hash_table_iter_init (&iter, priv->modems); + while (g_hash_table_iter_next (&iter, &key, &value)) { + MMModem *modem = MM_MODEM (value); - udis = mm_plugin_list_supported_udis (plugin, priv->hal_ctx); - if (udis) { - for (i = 0; udis[i]; i++) { - char *udi = udis[i]; - MMModem *modem; + if (mm_modem_owns_port (modem, subsys, name)) + return modem; + } + return NULL; +} - if (modem_exists (manager, udi)) { - g_warning ("Modem for UDI %s already exists, ignoring", udi); - continue; - } +typedef struct { + MMManager *manager; + char *subsys; + char *name; + GSList *plugins; + GSList *cur_plugin; + guint defer_id; + guint done_id; - modem = mm_plugin_create_modem (plugin, priv->hal_ctx, udi); - if (modem) - add_modem (manager, udi, modem); - } + guint32 best_level; + MMPlugin *best_plugin; +} SupportsInfo; - g_strfreev (udis); - } - } +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 -create_initial_modems_generic (MMManager *manager) +supports_info_free (SupportsInfo *info) { - MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); - char **devices; - int num_devices; - int i; - DBusError err; - - dbus_error_init (&err); - devices = libhal_find_device_by_capability (priv->hal_ctx, "modem", &num_devices, &err); - if (dbus_error_is_set (&err)) { - g_warning ("Could not list HAL devices: %s", err.message); - dbus_error_free (&err); - } + /* 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 (devices) { - for (i = 0; i < num_devices; i++) { - char *udi = devices[i]; - MMModem *modem; + if (info->defer_id) + g_source_remove (info->defer_id); - if (modem_exists (manager, udi)) - /* Already exists, most likely handled by a plugin */ - continue; + if (info->done_id) + g_source_remove (info->done_id); - modem = create_generic_modem (manager, udi); - if (modem) - add_modem (manager, g_strdup (udi), modem); - } - } + g_free (info->subsys); + g_free (info->name); + g_slist_free (info->plugins); + memset (info, 0, sizeof (SupportsInfo)); + g_free (info); +} - g_strfreev (devices); +static char * +get_key (const char *subsys, const char *name) +{ + return g_strdup_printf ("%s%s", subsys, name); } -static void -create_initial_modems (MMManager *manager) + +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) { - create_initial_modems_from_plugins (manager); - create_initial_modems_generic (manager); + 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 -enumerate_devices_cb (gpointer key, gpointer val, gpointer user_data) +try_supports_port (MMManager *manager, + MMPlugin *plugin, + const char *subsys, + const char *name, + SupportsInfo *info) { - GPtrArray **devices = (GPtrArray **) user_data; - - g_ptr_array_add (*devices, g_strdup ((char *) key)); + 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 gboolean -impl_manager_enumerate_devices (MMManager *manager, - GPtrArray **devices, - GError **err) +do_grab_port (gpointer user_data) { - MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + SupportsInfo *info = user_data; + MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (info->manager); + MMModem *modem; + GError *error = NULL; + char *key; + GSList *iter; - *devices = g_ptr_array_sized_new (g_hash_table_size (priv->modems)); - g_hash_table_foreach (priv->modems, enumerate_devices_cb, devices); + /* 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), + info->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), + info->subsys, + info->name, + error ? error->code : -1, + (error && error->message) ? error->message : "(unknown)"); + } + } - return TRUE; + /* 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), info->subsys, info->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); + + return FALSE; } static void -device_added (LibHalContext *ctx, const char *udi) +supports_callback (MMPlugin *plugin, + const char *subsys, + const char *name, + guint32 level, + gpointer user_data) +{ + 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); + + /* 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; + } + + /* 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; + + if (next_plugin) { + /* Try the next plugin */ + try_supports_port (info->manager, next_plugin, info->subsys, info->name, info); + } else { + /* All done; let the best modem grab the port */ + info->done_id = g_idle_add (do_grab_port, info); + } +} + +static void +device_added (MMManager *manager, GUdevDevice *device) { - MMManager *manager = MM_MANAGER (libhal_ctx_get_user_data (ctx)); MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); - GSList *iter; - MMModem *modem = NULL; + const char *subsys, *name; + SupportsInfo *info; + char *key; + gboolean found; + + g_return_if_fail (device != NULL); - if (modem_exists (manager, udi)) - /* Shouldn't happen */ + if (!g_slist_length (priv->plugins)) return; - for (iter = priv->plugins; iter && modem == NULL; iter = iter->next) { - MMPlugin *plugin = MM_PLUGIN (iter->data); + subsys = g_udev_device_get_subsystem (device); + name = g_udev_device_get_name (device); - if (mm_plugin_supports_udi (plugin, ctx, udi)) { - modem = mm_plugin_create_modem (plugin, ctx, udi); - if (modem) - break; - } + 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 (!modem) - /* None of the plugins supported the udi, try generic devices */ - modem = create_generic_modem (manager, udi); + info = supports_info_new (manager, subsys, name); + g_hash_table_insert (priv->supports, key, info); - if (modem) - add_modem (manager, udi, modem); + try_supports_port (manager, MM_PLUGIN (info->cur_plugin->data), subsys, name, info); } static void -device_removed (LibHalContext *ctx, const char *udi) +device_removed (MMManager *manager, GUdevDevice *device) { - MMManager *manager = MM_MANAGER (libhal_ctx_get_user_data (ctx)); + 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; - modem = modem_exists (manager, udi); + subsys = g_udev_device_get_subsystem (device); + name = g_udev_device_get_name (device); + modem = find_modem_for_port (manager, subsys, name); if (modem) { - g_debug ("Removed modem %s", udi); - g_signal_emit (manager, signals[DEVICE_REMOVED], 0, modem); - g_hash_table_remove (MM_MANAGER_GET_PRIVATE (manager)->modems, udi); + mm_modem_release_port (modem, subsys, name); + return; } -} -static void -device_new_capability (LibHalContext *ctx, const char *udi, const char *capability) -{ - device_added (ctx, udi); -} + /* 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); + } -DBusGConnection * -mm_manager_get_bus (MMManager *manager) -{ - g_return_val_if_fail (MM_IS_MANAGER (manager), NULL); - - return MM_MANAGER_GET_PRIVATE (manager)->connection; + g_free (key); } -static gboolean -remove_one (gpointer key, - gpointer value, - gpointer user_data) +static void +handle_uevent (GUdevClient *client, + const char *action, + GUdevDevice *device, + gpointer user_data) { - const char *udi = (char *) key; - MMModem *modem = MM_MODEM (value); - MMManager *manager = MM_MANAGER (user_data); + MMManager *self = MM_MANAGER (user_data); + const char *subsys; - g_debug ("Removed modem %s", udi); - g_signal_emit (manager, signals[DEVICE_REMOVED], 0, modem); + g_return_if_fail (action != NULL); - return TRUE; + /* 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")); + + if (!strcmp (action, "add")) + device_added (self, device); + else if (!strcmp (action, "remove")) + device_removed (self, device); } void -mm_manager_set_hal_ctx (MMManager *manager, - LibHalContext *hal_ctx) +mm_manager_start (MMManager *manager) { MMManagerPrivate *priv; + GList *devices, *iter; + g_return_if_fail (manager != NULL); g_return_if_fail (MM_IS_MANAGER (manager)); priv = MM_MANAGER_GET_PRIVATE (manager); - priv->hal_ctx = hal_ctx; - if (hal_ctx) { - libhal_ctx_set_user_data (hal_ctx, manager); - libhal_ctx_set_device_added (hal_ctx, device_added); - libhal_ctx_set_device_removed (hal_ctx, device_removed); - libhal_ctx_set_device_new_capability (hal_ctx, device_new_capability); + devices = g_udev_client_query_by_subsystem (priv->udev, "tty"); + for (iter = devices; iter; iter = g_list_next (iter)) + device_added (manager, G_UDEV_DEVICE (iter->data)); - create_initial_modems (manager); - } else { - g_hash_table_foreach_remove (priv->modems, remove_one, manager); - } -} - -LibHalContext * -mm_manager_get_hal_ctx (MMManager *manager) -{ - g_return_val_if_fail (MM_IS_MANAGER (manager), NULL); - - return MM_MANAGER_GET_PRIVATE (manager)->hal_ctx; + devices = g_udev_client_query_by_subsystem (priv->udev, "net"); + for (iter = devices; iter; iter = g_list_next (iter)) + device_added (manager, G_UDEV_DEVICE (iter->data)); } static void mm_manager_init (MMManager *manager) { MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager); + const char *subsys[3] = { "tty", "net", NULL }; 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); } static void @@ -444,16 +602,14 @@ 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); g_slist_free (priv->plugins); - if (priv->hal_ctx) { - mm_manager_set_hal_ctx (MM_MANAGER (object), NULL); - libhal_ctx_shutdown (priv->hal_ctx, NULL); - libhal_ctx_free (priv->hal_ctx); - } + if (priv->udev) + g_object_unref (priv->udev); if (priv->connection) dbus_g_connection_unref (priv->connection); @@ -500,3 +656,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); } + |