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.c457
1 files changed, 457 insertions, 0 deletions
diff --git a/src/mm-manager.c b/src/mm-manager.c
new file mode 100644
index 00000000..6ea936f6
--- /dev/null
+++ b/src/mm-manager.c
@@ -0,0 +1,457 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#include <string.h>
+#include <gmodule.h>
+#include <libhal.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include "mm-manager.h"
+#include "mm-modem-error.h"
+#include "mm-generic-gsm.h"
+#include "mm-generic-cdma.h"
+#include "mm-plugin.h"
+
+static gboolean impl_manager_enumerate_devices (MMManager *manager,
+ GPtrArray **devices,
+ GError **err);
+
+#include "mm-manager-glue.h"
+
+G_DEFINE_TYPE (MMManager, mm_manager, G_TYPE_OBJECT)
+
+enum {
+ DEVICE_ADDED,
+ DEVICE_REMOVED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+#define MM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MANAGER, MMManagerPrivate))
+
+typedef struct {
+ DBusGConnection *connection;
+ LibHalContext *hal_ctx;
+ GSList *plugins;
+ GHashTable *modems;
+} MMManagerPrivate;
+
+static MMPlugin *
+load_plugin (const char *path)
+{
+ MMPlugin *plugin = NULL;
+ GModule *module;
+ MMPluginCreateFunc plugin_create_func;
+ int *major_plugin_version, *minor_plugin_version;
+
+ module = g_module_open (path, G_MODULE_BIND_LAZY);
+ if (!module) {
+ g_warning ("Could not load plugin %s: %s", path, g_module_error ());
+ return NULL;
+ }
+
+ if (!g_module_symbol (module, "mm_plugin_major_version", (gpointer *) &major_plugin_version)) {
+ g_warning ("Could not load plugin %s: Missing major version info", path);
+ goto out;
+ }
+
+ if (*major_plugin_version != MM_PLUGIN_MAJOR_VERSION) {
+ g_warning ("Could not load plugin %s: Plugin major version %d, %d is required",
+ path, *major_plugin_version, MM_PLUGIN_MAJOR_VERSION);
+ goto out;
+ }
+
+ if (!g_module_symbol (module, "mm_plugin_minor_version", (gpointer *) &minor_plugin_version)) {
+ g_warning ("Could not load plugin %s: Missing minor version info", path);
+ goto out;
+ }
+
+ if (*minor_plugin_version != MM_PLUGIN_MINOR_VERSION) {
+ g_warning ("Could not load plugin %s: Plugin minor version %d, %d is required",
+ path, *minor_plugin_version, MM_PLUGIN_MINOR_VERSION);
+ goto out;
+ }
+
+ if (!g_module_symbol (module, "mm_plugin_create", (gpointer *) &plugin_create_func)) {
+ g_warning ("Could not load plugin %s: %s", path, g_module_error ());
+ goto out;
+ }
+
+ plugin = (*plugin_create_func) ();
+ if (plugin) {
+ g_object_weak_ref (G_OBJECT (plugin), (GWeakNotify) g_module_close, module);
+ g_message ("Loaded plugin %s", mm_plugin_get_name (plugin));
+ } else
+ g_warning ("Could not load plugin %s: initialization failed", path);
+
+ out:
+ if (!plugin)
+ g_module_close (module);
+
+ return plugin;
+}
+
+static void
+load_plugins (MMManager *manager)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ GDir *dir;
+ const char *fname;
+
+ if (!g_module_supported ()) {
+ g_warning ("GModules are not supported on your platform!");
+ return;
+ }
+
+ dir = g_dir_open (PLUGINDIR, 0, NULL);
+ if (!dir) {
+ g_warning ("No plugins found");
+ return;
+ }
+
+ while ((fname = g_dir_read_name (dir)) != NULL) {
+ char *path;
+ MMPlugin *plugin;
+
+ if (!strstr (fname, G_MODULE_SUFFIX))
+ continue;
+
+ path = g_module_build_path (PLUGINDIR, fname);
+ plugin = load_plugin (path);
+ g_free (path);
+
+ if (plugin)
+ priv->plugins = g_slist_append (priv->plugins, plugin);
+ }
+
+ g_dir_close (dir);
+}
+
+MMManager *
+mm_manager_new (void)
+{
+ return g_object_new (MM_TYPE_MANAGER, NULL);
+}
+
+static char *
+get_driver_name (LibHalContext *ctx, const char *udi)
+{
+ char *parent_udi;
+ char *driver = NULL;
+
+ 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);
+ }
+
+ return driver;
+}
+
+static MMModem *
+create_generic_modem (MMManager *manager, const char *udi)
+{
+ 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;
+
+ serial_device = libhal_device_get_property_string (priv->hal_ctx, udi, "serial.device", NULL);
+ g_return_val_if_fail (serial_device != NULL, NULL);
+
+ driver = get_driver_name (priv->hal_ctx, udi);
+ g_return_val_if_fail (driver != NULL, NULL);
+
+ 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);
+
+ return modem;
+}
+
+static void
+add_modem (MMManager *manager, const char *udi, MMModem *modem)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+
+ g_debug ("Added modem %s", udi);
+ 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 MMModem *
+modem_exists (MMManager *manager, const char *udi)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+
+ return (MMModem *) g_hash_table_lookup (priv->modems, udi);
+}
+
+static void
+create_initial_modems_from_plugins (MMManager *manager)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ GSList *iter;
+
+ for (iter = priv->plugins; iter; iter = iter->next) {
+ MMPlugin *plugin = MM_PLUGIN (iter->data);
+ GSList *udis;
+ GSList *udi_iter;
+
+ udis = mm_plugin_list_supported_udis (plugin, priv->hal_ctx);
+ for (udi_iter = udis; udi_iter; udi_iter = udi_iter->next) {
+ char *udi = (char *) udi_iter->data;
+ MMModem *modem;
+
+ if (modem_exists (manager, udi)) {
+ g_warning ("Modem for UDI %s already exists, ignoring", udi);
+ continue;
+ }
+
+ modem = mm_plugin_create_modem (plugin, priv->hal_ctx, udi);
+ if (modem)
+ add_modem (manager, udi, modem);
+ else
+ g_warning ("Plugin failed to create modem for UDI %s", udi);
+ }
+
+ g_slist_foreach (udis, (GFunc) g_free, NULL);
+ g_slist_free (udis);
+ }
+}
+
+static void
+create_initial_modems_generic (MMManager *manager)
+{
+ 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);
+ }
+
+ if (devices) {
+ for (i = 0; i < num_devices; i++) {
+ char *udi = devices[i];
+ MMModem *modem;
+
+ if (modem_exists (manager, udi))
+ /* Already exists, most likely handled by a plugin */
+ continue;
+
+ modem = create_generic_modem (manager, udi);
+ if (modem)
+ add_modem (manager, g_strdup (udi), modem);
+ }
+ }
+
+ g_strfreev (devices);
+}
+
+static void
+create_initial_modems (MMManager *manager)
+{
+ create_initial_modems_from_plugins (manager);
+ create_initial_modems_generic (manager);
+}
+
+static void
+enumerate_devices_cb (gpointer key, gpointer val, gpointer user_data)
+{
+ GPtrArray **devices = (GPtrArray **) user_data;
+
+ g_ptr_array_add (*devices, g_strdup ((char *) key));
+}
+
+static gboolean
+impl_manager_enumerate_devices (MMManager *manager,
+ GPtrArray **devices,
+ GError **err)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+
+ *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
+device_added (LibHalContext *ctx, const char *udi)
+{
+ MMManager *manager = MM_MANAGER (libhal_ctx_get_user_data (ctx));
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ GSList *iter;
+ MMModem *modem = NULL;
+
+ if (modem_exists (manager, udi))
+ /* Shouldn't happen */
+ return;
+
+ for (iter = priv->plugins; iter && modem == NULL; iter = iter->next) {
+ MMPlugin *plugin = MM_PLUGIN (iter->data);
+
+ if (mm_plugin_supports_udi (plugin, ctx, udi)) {
+ modem = mm_plugin_create_modem (plugin, ctx, udi);
+ if (modem)
+ break;
+ }
+ }
+
+ if (!modem)
+ /* None of the plugins supported the udi, try generic devices */
+ modem = create_generic_modem (manager, udi);
+
+ if (modem)
+ add_modem (manager, udi, modem);
+}
+
+static void
+device_removed (LibHalContext *ctx, const char *udi)
+{
+ MMManager *manager = MM_MANAGER (libhal_ctx_get_user_data (ctx));
+ MMModem *modem;
+
+ modem = modem_exists (manager, udi);
+ 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);
+ }
+}
+
+static void
+device_new_capability (LibHalContext *ctx, const char *udi, const char *capability)
+{
+ device_added (ctx, udi);
+}
+
+static void
+mm_manager_init (MMManager *manager)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ GError *err = NULL;
+ DBusError dbus_error;
+
+ priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+
+ priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
+ if (!priv->connection)
+ g_error ("Could not connect to system bus.");
+
+ dbus_g_connection_register_g_object (priv->connection,
+ MM_DBUS_PATH,
+ G_OBJECT (manager));
+
+ priv->hal_ctx = libhal_ctx_new ();
+ if (!priv->hal_ctx)
+ g_error ("Could not get connection to the HAL service.");
+
+ libhal_ctx_set_dbus_connection (priv->hal_ctx, dbus_g_connection_get_connection (priv->connection));
+
+ dbus_error_init (&dbus_error);
+ if (!libhal_ctx_init (priv->hal_ctx, &dbus_error))
+ g_error ("libhal_ctx_init() failed: %s\n"
+ "Make sure the hal daemon is running?",
+ dbus_error.message);
+
+ load_plugins (manager);
+
+ libhal_ctx_set_user_data (priv->hal_ctx, manager);
+ libhal_ctx_set_device_added (priv->hal_ctx, device_added);
+ libhal_ctx_set_device_removed (priv->hal_ctx, device_removed);
+ libhal_ctx_set_device_new_capability (priv->hal_ctx, device_new_capability);
+
+ create_initial_modems (manager);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (object);
+
+ 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) {
+ libhal_ctx_shutdown (priv->hal_ctx, NULL);
+ libhal_ctx_free (priv->hal_ctx);
+ }
+
+ if (priv->connection)
+ dbus_g_connection_unref (priv->connection);
+
+ G_OBJECT_CLASS (mm_manager_parent_class)->finalize (object);
+}
+
+static void
+mm_manager_class_init (MMManagerClass *manager_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
+
+ g_type_class_add_private (object_class, sizeof (MMManagerPrivate));
+
+ /* Virtual methods */
+ object_class->finalize = finalize;
+
+ /* Signals */
+ signals[DEVICE_ADDED] =
+ g_signal_new ("device-added",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMManagerClass, device_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+
+ signals[DEVICE_REMOVED] =
+ g_signal_new ("device-removed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMManagerClass, device_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (manager_class),
+ &dbus_glib_mm_manager_object_info);
+
+ dbus_g_error_domain_register (MM_MODEM_ERROR, NULL, MM_TYPE_MODEM_ERROR);
+}