Initial commit.
This commit is contained in:
457
src/mm-manager.c
Normal file
457
src/mm-manager.c
Normal file
@@ -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);
|
||||
}
|
Reference in New Issue
Block a user