core: compile all ports before creating the modem object

Before this, we only exported the modem to DBus when all ports were organized,
in order to make sure that we select as primary port the one we really want and
not the first AT port grabbed. Given that to get all the ports organized we also
needed to wait to get all the ports grabbed, we can now also defer the creation
of the modem object until all the ports get grabbed. This allows us to create
different types of objects based on the ports available (e.g. we can now create
QMI-supported modem objects if we see a QMI port around).
This commit is contained in:
Aleksander Morgado
2012-08-06 20:06:44 +02:00
parent 4add521a98
commit 815693661c
23 changed files with 1096 additions and 868 deletions
+2
View File
@@ -148,6 +148,8 @@ ModemManager_SOURCES = \
mm-auth-provider.c \
mm-manager.c \
mm-manager.h \
mm-device.c \
mm-device.h \
mm-plugin-manager.c \
mm-plugin-manager.h \
mm-sim.h \
+413
View File
@@ -0,0 +1,413 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
* Copyright (C) 2012 Google, Inc.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ModemManager.h>
#include <mm-errors-types.h>
#include "mm-device.h"
#include "mm-log.h"
G_DEFINE_TYPE (MMDevice, mm_device, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_UDEV_DEVICE,
PROP_PLUGIN,
PROP_MODEM,
PROP_LAST
};
static GParamSpec *properties[PROP_LAST];
struct _MMDevicePrivate {
/* Parent UDev device */
GUdevDevice *udev_device;
/* Best plugin to manage this device */
MMPlugin *plugin;
/* List of ports in the device */
GList *udev_ports;
/* The Modem object for this device */
MMBaseModem *modem;
/* When exported, a reference to the object manager */
GDBusObjectManagerServer *object_manager;
};
/*****************************************************************************/
static gint
udev_port_cmp (GUdevDevice *a,
GUdevDevice *b)
{
return strcmp (g_udev_device_get_sysfs_path (a),
g_udev_device_get_sysfs_path (b));
}
gboolean
mm_device_owns_port (MMDevice *self,
GUdevDevice *udev_port)
{
return !!g_list_find_custom (self->priv->udev_ports,
udev_port,
(GCompareFunc)udev_port_cmp);
}
void
mm_device_grab_port (MMDevice *self,
GUdevDevice *udev_port)
{
if (!g_list_find_custom (self->priv->udev_ports,
udev_port,
(GCompareFunc)udev_port_cmp)) {
self->priv->udev_ports = g_list_prepend (self->priv->udev_ports,
g_object_ref (udev_port));
}
}
void
mm_device_release_port (MMDevice *self,
GUdevDevice *udev_port)
{
GList *found;
found = g_list_find_custom (self->priv->udev_ports,
udev_port,
(GCompareFunc)udev_port_cmp);
if (found) {
g_object_unref (found->data);
self->priv->udev_ports = g_list_delete_link (self->priv->udev_ports, found);
}
}
/*****************************************************************************/
static void
unexport_modem (MMDevice *self)
{
gchar *path;
g_assert (MM_IS_BASE_MODEM (self->priv->modem));
g_assert (G_IS_DBUS_OBJECT_MANAGER (self->priv->object_manager));
path = g_strdup (g_dbus_object_get_object_path (G_DBUS_OBJECT (self->priv->modem)));
if (path != NULL) {
g_dbus_object_manager_server_unexport (self->priv->object_manager, path);
g_object_set (self->priv->modem,
MM_BASE_MODEM_CONNECTION, NULL,
NULL);
mm_dbg ("Unexported modem '%s' from path '%s'",
g_udev_device_get_sysfs_path (self->priv->udev_device),
path);
g_free (path);
}
}
/*****************************************************************************/
static void
export_modem (MMDevice *self)
{
GDBusConnection *connection = NULL;
static guint32 id = 0;
gchar *path;
g_assert (MM_IS_BASE_MODEM (self->priv->modem));
g_assert (G_IS_DBUS_OBJECT_MANAGER (self->priv->object_manager));
/* If modem not yet valid (not fully initialized), don't export it */
if (!mm_base_modem_get_valid (self->priv->modem)) {
mm_dbg ("Modem '%s' not yet fully initialized",
g_udev_device_get_sysfs_path (self->priv->udev_device));
return;
}
/* Don't export already exported modems */
g_object_get (self->priv->modem,
"g-object-path", &path,
NULL);
if (path) {
g_free (path);
mm_dbg ("Modem '%s' already exported",
g_udev_device_get_sysfs_path (self->priv->udev_device));
return;
}
/* No outstanding port tasks, so if the modem is valid we can export it */
path = g_strdup_printf (MM_DBUS_MODEM_PREFIX "/%d", id++);
g_object_get (self->priv->object_manager,
"connection", &connection,
NULL);
g_object_set (self->priv->modem,
"g-object-path", path,
MM_BASE_MODEM_CONNECTION, connection,
NULL);
g_object_unref (connection);
g_dbus_object_manager_server_export (self->priv->object_manager,
G_DBUS_OBJECT_SKELETON (self->priv->modem));
mm_dbg ("Exported modem '%s' at path '%s'",
g_udev_device_get_sysfs_path (self->priv->udev_device),
path);
/* Once connected, dump additional debug info about the modem */
mm_dbg ("(%s): '%s' modem, VID 0x%04X PID 0x%04X (%s)",
path,
mm_base_modem_get_plugin (self->priv->modem),
(mm_base_modem_get_vendor_id (self->priv->modem) & 0xFFFF),
(mm_base_modem_get_product_id (self->priv->modem) & 0xFFFF),
g_udev_device_get_subsystem (self->priv->udev_device));
g_free (path);
}
/*****************************************************************************/
void
mm_device_remove_modem (MMDevice *self)
{
if (!self->priv->modem)
return;
unexport_modem (self);
/* Run dispose before unref-ing, in order to cleanup the SIM object,
* if any (which also holds a reference to the modem object) */
g_object_run_dispose (G_OBJECT (self->priv->modem));
g_clear_object (&(self->priv->modem));
g_clear_object (&(self->priv->object_manager));
}
/*****************************************************************************/
static void
modem_valid (MMBaseModem *modem,
GParamSpec *pspec,
MMDevice *self)
{
if (!mm_base_modem_get_valid (modem)) {
/* Modem no longer valid */
mm_device_remove_modem (self);
} else {
/* Modem now valid, export it */
export_modem (self);
}
}
gboolean
mm_device_create_modem (MMDevice *self,
GDBusObjectManagerServer *object_manager,
GError **error)
{
g_assert (self->priv->modem == NULL);
g_assert (self->priv->object_manager == NULL);
g_assert (self->priv->udev_ports != NULL);
mm_info ("Creating modem with plugin '%s' and '%u' ports",
mm_plugin_get_name (self->priv->plugin),
g_list_length (self->priv->udev_ports));
self->priv->modem = mm_plugin_create_modem (self->priv->plugin,
self->priv->udev_ports,
error);
if (self->priv->modem) {
/* Keep the object manager */
self->priv->object_manager = g_object_ref (object_manager);
/* We want to get notified when the modem becomes valid/invalid */
g_signal_connect (self->priv->modem,
"notify::" MM_BASE_MODEM_VALID,
G_CALLBACK (modem_valid),
self);
}
return !!self->priv->modem;
}
/*****************************************************************************/
GUdevDevice *
mm_device_peek_udev_device (MMDevice *self)
{
return self->priv->udev_device;
}
GUdevDevice *
mm_device_get_udev_device (MMDevice *self)
{
return G_UDEV_DEVICE (g_object_ref (self->priv->udev_device));
}
MMPlugin *
mm_device_peek_plugin (MMDevice *self)
{
return self->priv->plugin;
}
MMPlugin *
mm_device_get_plugin (MMDevice *self)
{
return MM_PLUGIN (g_object_ref (self->priv->plugin));
}
MMBaseModem *
mm_device_peek_modem (MMDevice *self)
{
return (self->priv->modem ?
MM_BASE_MODEM (self->priv->modem) :
NULL);
}
MMBaseModem *
mm_device_get_modem (MMDevice *self)
{
return (self->priv->modem ?
MM_BASE_MODEM (g_object_ref (self->priv->modem)) :
NULL);
}
/*****************************************************************************/
MMDevice *
mm_device_new (GUdevDevice *udev_device,
MMPlugin *plugin)
{
return MM_DEVICE (g_object_new (MM_TYPE_DEVICE,
MM_DEVICE_UDEV_DEVICE, udev_device,
MM_DEVICE_PLUGIN, plugin,
NULL));
}
static void
mm_device_init (MMDevice *self)
{
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
MM_TYPE_DEVICE,
MMDevicePrivate);
}
static void
set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MMDevice *self = MM_DEVICE (object);
switch (prop_id) {
case PROP_UDEV_DEVICE:
/* construct only */
self->priv->udev_device = g_value_dup_object (value);
break;
case PROP_PLUGIN:
/* construct only */
self->priv->plugin = g_value_dup_object (value);
break;
case PROP_MODEM:
g_clear_object (&(self->priv->modem));
self->priv->modem = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MMDevice *self = MM_DEVICE (object);
switch (prop_id) {
case PROP_UDEV_DEVICE:
g_value_set_object (value, self->priv->udev_device);
break;
case PROP_PLUGIN:
g_value_set_object (value, self->priv->plugin);
break;
case PROP_MODEM:
g_value_set_object (value, self->priv->modem);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
dispose (GObject *object)
{
MMDevice *self = MM_DEVICE (object);
g_clear_object (&(self->priv->udev_device));
g_clear_object (&(self->priv->plugin));
g_list_free_full (self->priv->udev_ports, (GDestroyNotify)g_object_unref);
g_clear_object (&(self->priv->modem));
G_OBJECT_CLASS (mm_device_parent_class)->dispose (object);
}
static void
mm_device_class_init (MMDeviceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMDevicePrivate));
/* Virtual methods */
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->dispose = dispose;
properties[PROP_UDEV_DEVICE] =
g_param_spec_object (MM_DEVICE_UDEV_DEVICE,
"UDev Device",
"UDev device object",
G_UDEV_TYPE_DEVICE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (object_class, PROP_UDEV_DEVICE, properties[PROP_UDEV_DEVICE]);
properties[PROP_PLUGIN] =
g_param_spec_object (MM_DEVICE_PLUGIN,
"Plugin",
"Best plugin to manage this device",
MM_TYPE_PLUGIN,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (object_class, PROP_PLUGIN, properties[PROP_PLUGIN]);
properties[PROP_MODEM] =
g_param_spec_object (MM_DEVICE_MODEM,
"Modem",
"The modem object",
MM_TYPE_BASE_MODEM,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]);
}
+74
View File
@@ -0,0 +1,74 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
* Copyright (C) 2012 Google, Inc.
*/
#ifndef MM_DEVICE_H
#define MM_DEVICE_H
#include <glib.h>
#include <glib-object.h>
#include <gudev/gudev.h>
#include "mm-plugin.h"
#define MM_TYPE_DEVICE (mm_device_get_type ())
#define MM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_DEVICE, MMDevice))
#define MM_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_DEVICE, MMDeviceClass))
#define MM_IS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_DEVICE))
#define MM_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_DEVICE))
#define MM_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_DEVICE, MMDeviceClass))
typedef struct _MMDevice MMDevice;
typedef struct _MMDeviceClass MMDeviceClass;
typedef struct _MMDevicePrivate MMDevicePrivate;
#define MM_DEVICE_UDEV_DEVICE "udev-device"
#define MM_DEVICE_PLUGIN "plugin"
#define MM_DEVICE_MODEM "modem"
struct _MMDevice {
GObject parent;
MMDevicePrivate *priv;
};
struct _MMDeviceClass {
GObjectClass parent;
};
GType mm_device_get_type (void);
MMDevice *mm_device_new (GUdevDevice *udev_device,
MMPlugin *plugin);
void mm_device_grab_port (MMDevice *self,
GUdevDevice *udev_port);
void mm_device_release_port (MMDevice *self,
GUdevDevice *udev_port);
gboolean mm_device_owns_port (MMDevice *self,
GUdevDevice *udev_port);
gboolean mm_device_create_modem (MMDevice *self,
GDBusObjectManagerServer *object_manager,
GError **error);
void mm_device_remove_modem (MMDevice *self);
GUdevDevice *mm_device_peek_udev_device (MMDevice *self);
GUdevDevice *mm_device_get_udev_device (MMDevice *self);
MMPlugin *mm_device_peek_plugin (MMDevice *self);
MMPlugin *mm_device_get_plugin (MMDevice *self);
MMBaseModem *mm_device_peek_modem (MMDevice *self);
MMBaseModem *mm_device_get_modem (MMDevice *self);
#endif /* MM_DEVICE_H */
+168 -320
View File
@@ -27,17 +27,13 @@
#include <mm-gdbus-manager.h>
#include "mm-manager.h"
#include "mm-device.h"
#include "mm-plugin-manager.h"
#include "mm-auth.h"
#include "mm-plugin.h"
#include "mm-log.h"
#include "mm-port-probe-cache.h"
static void grab_port (MMManager *manager,
MMPlugin *plugin,
GUdevDevice *device,
GUdevDevice *physical_device);
static void initable_iface_init (GInitableIface *iface);
G_DEFINE_TYPE_EXTENDED (MMManager, mm_manager, MM_GDBUS_TYPE_ORG_FREEDESKTOP_MODEM_MANAGER1_SKELETON, 0,
@@ -60,9 +56,9 @@ struct _MMManagerPrivate {
GCancellable *authp_cancellable;
/* The Plugin Manager object */
MMPluginManager *plugin_manager;
/* The container of currently available modems */
GHashTable *modems;
/* DBus The Object Manager server */
/* The container of devices being prepared */
GHashTable *devices;
/* The Object Manager server */
GDBusObjectManagerServer *object_manager;
};
@@ -95,308 +91,147 @@ find_port_support_context_free (FindPortSupportContext *ctx)
g_free (ctx);
}
static void
remove_modem (MMManager *manager,
MMBaseModem *modem)
{
gchar *path;
gchar *device;
device = g_strdup (mm_base_modem_get_device (modem));
path = g_strdup (g_dbus_object_get_object_path (G_DBUS_OBJECT (modem)));
/* If we get DBus object path, modem was exported */
if (path) {
g_dbus_object_manager_server_unexport (manager->priv->object_manager, path);
g_object_set (modem,
MM_BASE_MODEM_CONNECTION, NULL,
NULL);
mm_dbg ("Unexported modem '%s' from path '%s'", device, path);
g_free (path);
} else {
mm_dbg ("Removing modem '%s', which wasn't exported yet", device);
}
/* Run dispose before unref-ing, in order to cleanup the SIM object,
* if any (which also holds a reference to the modem object) */
g_object_run_dispose (G_OBJECT (modem));
g_hash_table_remove (manager->priv->modems, device);
g_free (device);
}
static void
debug_modem_info (MMManager *self,
MMBaseModem *modem,
const gchar *path)
{
GUdevDevice *physdev;
const gchar *subsys;
physdev = g_udev_client_query_by_sysfs_path (self->priv->udev,
mm_base_modem_get_device (modem));
subsys = (physdev ?
g_udev_device_get_subsystem (physdev) :
NULL);
mm_dbg ("(%s): '%s' modem, VID 0x%04X PID 0x%04X (%s)",
path,
mm_base_modem_get_plugin (modem),
(mm_base_modem_get_vendor_id (modem) & 0xFFFF),
(mm_base_modem_get_product_id (modem) & 0xFFFF),
subsys ? subsys : "unknown");
if (physdev)
g_object_unref (physdev);
}
static void
check_export_modem (MMManager *self,
MMBaseModem *modem)
{
GError *error = NULL;
static guint32 id = 0;
const gchar *modem_physdev;
const gchar *name;
const gchar *subsys;
gchar *path;
/* 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_base_modem_get_device (modem);
g_assert (modem_physdev);
/* Check for ports that are in the process of being interrogated by plugins */
if (mm_plugin_manager_is_finding_device_support (self->priv->plugin_manager,
modem_physdev,
&subsys,
&name)) {
mm_dbg ("(%s/%s): outstanding support task prevents export of '%s'",
subsys, name, modem_physdev);
return;
}
/* Plugin manager is not trying to find more ports supported by this device,
* so we can organize the ports now (if not done already). */
if (!mm_base_modem_organize_ports (modem, &error)) {
/* If the ports were not properly organized, the modem will be marked as
* invalid and therefore removed */
mm_err ("Failed to organize modem ports: '%s'",
error->message);
g_error_free (error);
remove_modem (self, modem);
return;
}
/* If modem not yet valid (not fully initialized), don't export it */
if (!mm_base_modem_get_valid (modem))
return;
/* Don't export already exported modems */
g_object_get (modem,
"g-object-path", &path,
NULL);
if (path) {
g_free (path);
mm_dbg ("Modem '%s' already exported", modem_physdev);
return;
}
/* No outstanding port tasks, so if the modem is valid we can export it */
path = g_strdup_printf (MM_DBUS_MODEM_PREFIX "/%d", id++);
g_object_set (modem,
"g-object-path", path,
MM_BASE_MODEM_CONNECTION, self->priv->connection,
NULL);
g_dbus_object_manager_server_export (self->priv->object_manager,
G_DBUS_OBJECT_SKELETON (modem));
mm_dbg ("Exported modem '%s' at path '%s'", modem_physdev, path);
/* Once connected, dump additional debug info about the modem */
debug_modem_info (self, modem, path);
g_free (path);
}
static void
modem_valid (MMBaseModem *modem,
GParamSpec *pspec,
gpointer user_data)
{
MMManager *manager = MM_MANAGER (user_data);
if (mm_base_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,
MMBaseModem *modem,
MMPlugin *plugin)
{
const gchar *device;
device = mm_base_modem_get_device (modem);
if (!g_hash_table_lookup (manager->priv->modems, device)) {
mm_dbg ("Added modem %s", device);
g_hash_table_insert (manager->priv->modems,
g_strdup (device),
modem);
g_object_set_data (G_OBJECT (modem), MANAGER_PLUGIN_TAG, plugin);
g_signal_connect (modem, "notify::" MM_BASE_MODEM_VALID, G_CALLBACK (modem_valid), manager);
}
check_export_modem (manager, modem);
}
static MMBaseModem *
find_modem_for_device (MMManager *manager,
const gchar *device)
static MMDevice *
find_device_by_modem (MMManager *manager,
MMBaseModem *modem)
{
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init (&iter, manager->priv->modems);
g_hash_table_iter_init (&iter, manager->priv->devices);
while (g_hash_table_iter_next (&iter, &key, &value)) {
MMBaseModem *candidate = MM_BASE_MODEM (value);
MMDevice *candidate = MM_DEVICE (value);
if (g_str_equal (device,
mm_base_modem_get_device (candidate)))
if (modem == mm_device_peek_modem (candidate))
return candidate;
}
return NULL;
}
static MMBaseModem *
find_modem_for_port (MMManager *manager,
const gchar *subsys,
const gchar *name)
static MMDevice *
find_device_by_port (MMManager *manager,
GUdevDevice *port)
{
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init (&iter, manager->priv->modems);
g_hash_table_iter_init (&iter, manager->priv->devices);
while (g_hash_table_iter_next (&iter, &key, &value)) {
MMBaseModem *candidate = MM_BASE_MODEM (value);
MMDevice *candidate = MM_DEVICE (value);
if (mm_base_modem_owns_port (candidate, subsys, name))
if (mm_device_owns_port (candidate, port))
return candidate;
}
return NULL;
}
static MMDevice *
find_device_by_sysfs_path (MMManager *self,
const gchar *sysfs_path)
{
return g_hash_table_lookup (self->priv->devices,
sysfs_path);
}
static MMDevice *
find_device_by_udev_device (MMManager *manager,
GUdevDevice *udev_device)
{
return find_device_by_sysfs_path (manager, g_udev_device_get_sysfs_path (udev_device));
}
static void
find_port_support_ready_cb (MMPluginManager *plugin_manager,
GAsyncResult *result,
FindPortSupportContext *ctx)
{
GError *error = NULL;
MMDevice *device;
MMPlugin *best_plugin;
/* Look for the container device, if any */
device = find_device_by_udev_device (ctx->manager, ctx->physical_device);
best_plugin = mm_plugin_manager_find_port_support_finish (plugin_manager,
result,
&error);
if (!best_plugin) {
MMBaseModem *existing;
if (error) {
mm_dbg ("(%s/%s): error checking support: '%s'",
g_udev_device_get_subsystem (ctx->device),
g_udev_device_get_name (ctx->device),
error->message);
mm_warn ("(%s/%s): error checking support: '%s'",
g_udev_device_get_subsystem (ctx->device),
g_udev_device_get_name (ctx->device),
error->message);
g_error_free (error);
} else {
mm_dbg ("(%s/%s): not supported by any plugin",
g_udev_device_get_subsystem (ctx->device),
g_udev_device_get_name (ctx->device));
}
/* So we couldn't get a plugin for this port, we should anyway check if
* there is already an existing modem for the physical device, and if
* so, check if it can already be exported. */
existing = find_modem_for_device (
ctx->manager,
g_udev_device_get_sysfs_path (ctx->physical_device));
if (existing)
check_export_modem (ctx->manager, existing);
} else {
mm_dbg ("(%s/%s): found plugin '%s' giving best support",
/* Found a best plugin for the port */
if (!device) {
/* Create a generic device to track the available ports, and add it to the
* manager. */
device = mm_device_new (ctx->physical_device,
best_plugin);
g_hash_table_insert (ctx->manager->priv->devices,
g_strdup (g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device))),
device);
}
else if (!g_str_equal (mm_plugin_get_name (mm_device_peek_plugin (device)),
mm_plugin_get_name (best_plugin))) {
/* Warn if the best plugin found for this port differs from the
* best plugin found for the the first grabbed port */
mm_warn ("(%s/%s): plugin mismatch error (expected: '%s', got: '%s')",
g_udev_device_get_subsystem (ctx->device),
g_udev_device_get_name (ctx->device),
mm_plugin_get_name (mm_device_peek_plugin (device)),
mm_plugin_get_name (best_plugin));
}
/* Add the port to the device */
mm_dbg ("(%s/%s): added to device managed by plugin '%s'",
g_udev_device_get_subsystem (ctx->device),
g_udev_device_get_name (ctx->device),
mm_plugin_get_name ((MMPlugin *)best_plugin));
mm_plugin_get_name (mm_device_peek_plugin (device)));
mm_device_grab_port (device, ctx->device);
}
grab_port (ctx->manager,
best_plugin,
ctx->device,
ctx->physical_device);
if (device) {
const gchar *subsys;
const gchar *name;
/* This port was probed after having created the modem */
if (mm_device_peek_modem (device)) {
mm_dbg ("(%s/%s): port added to existing modem",
g_udev_device_get_subsystem (ctx->device),
g_udev_device_get_name (ctx->device));
}
/* Every time we get a supports check result, we need to see if there
* are ports of the same device still being probed. */
else if (mm_plugin_manager_is_finding_device_support (plugin_manager,
g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device)),
&subsys,
&name)) {
mm_dbg ("(%s/%s): outstanding support task prevents export of '%s'",
subsys, name, g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device)));
}
/* Plugin manager is not trying to find more ports supported by this
* device, so we can create the modem now! */
else {
if (!mm_device_create_modem (device, ctx->manager->priv->object_manager, &error)) {
mm_warn ("Couldn't create '%s' modem for device at '%s': %s",
g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device)),
mm_plugin_get_name (mm_device_peek_plugin (device)),
error ? error->message : "Unknown error");
}
}
}
find_port_support_context_free (ctx);
}
static void
grab_port (MMManager *manager,
MMPlugin *plugin,
GUdevDevice *device,
GUdevDevice *physical_device)
{
GError *error = NULL;
MMBaseModem *modem;
MMBaseModem *existing;
existing = g_hash_table_lookup (manager->priv->modems,
g_udev_device_get_sysfs_path (physical_device));
/* While grabbing the first port, modem will get created */
modem = mm_plugin_grab_port (plugin,
g_udev_device_get_subsystem (device),
g_udev_device_get_name (device),
existing,
&error);
if (!modem) {
mm_warn ("plugin '%s' claimed to support %s/%s but couldn't: (%d) %s",
mm_plugin_get_name (plugin),
g_udev_device_get_subsystem (device),
g_udev_device_get_name (device),
error ? error->code : -1,
(error && error->message) ? error->message : "(unknown)");
g_clear_error (&error);
if (existing)
check_export_modem (manager, existing);
return;
}
mm_info ("(%s): modem %s claimed port %s",
mm_plugin_get_name (plugin),
mm_base_modem_get_device (modem),
g_udev_device_get_name (device));
if (existing) {
g_assert (existing == modem);
check_export_modem (manager, modem);
} else {
/* If the modem was just created, store it */
add_modem (manager, modem, plugin);
}
}
static GUdevDevice *
find_physical_device (GUdevDevice *child)
{
@@ -461,18 +296,17 @@ find_physical_device (GUdevDevice *child)
static void
device_added (MMManager *manager,
GUdevDevice *device)
GUdevDevice *port)
{
const char *subsys, *name, *physdev_path, *physdev_subsys;
gboolean is_candidate;
GUdevDevice *physdev = NULL;
MMPlugin *plugin;
MMBaseModem *existing;
MMDevice *existing;
g_return_if_fail (device != NULL);
g_return_if_fail (port != NULL);
subsys = g_udev_device_get_subsystem (device);
name = g_udev_device_get_name (device);
subsys = g_udev_device_get_subsystem (port);
name = g_udev_device_get_name (port);
/* ignore VTs */
if (strncmp (name, "tty", 3) == 0 && isdigit (name[3]))
@@ -485,18 +319,18 @@ device_added (MMManager *manager,
* the device to a specific ModemManager driver, we need to ensure that all
* rules have been processed before handling a device.
*/
is_candidate = g_udev_device_get_property_as_boolean (device, "ID_MM_CANDIDATE");
is_candidate = g_udev_device_get_property_as_boolean (port, "ID_MM_CANDIDATE");
if (!is_candidate)
return;
if (find_modem_for_port (manager, subsys, name))
if (find_device_by_port (manager, port))
return;
/* 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);
physdev = find_physical_device (port);
if (!physdev) {
/* Warn about it, but filter out some common ports that we know don't have
* anything to do with mobile broadband.
@@ -545,10 +379,7 @@ device_added (MMManager *manager,
* asking all plugins whether they support this port, just let the owning
* plugin check if it supports the port.
*/
existing = find_modem_for_device (manager, physdev_path);
plugin = (existing ?
MM_PLUGIN (g_object_get_data (G_OBJECT (existing), MANAGER_PLUGIN_TAG)) :
NULL);
existing = find_device_by_udev_device (manager, physdev);
/* Launch supports check in the Plugin Manager */
mm_plugin_manager_find_port_support (
@@ -556,12 +387,12 @@ device_added (MMManager *manager,
subsys,
name,
physdev_path,
plugin,
existing,
existing ? mm_device_peek_plugin (existing) : NULL,
existing ? mm_device_peek_modem (existing) : NULL,
(GAsyncReadyCallback)find_port_support_ready_cb,
find_port_support_context_new (manager,
device,
physdev));
port,
physdev));
out:
if (physdev)
@@ -569,48 +400,45 @@ out:
}
static void
device_removed (MMManager *manager,
GUdevDevice *device)
device_removed (MMManager *self,
GUdevDevice *udev_device)
{
MMBaseModem *modem;
const char *subsys, *name;
MMDevice *device;
g_return_if_fail (device != NULL);
subsys = g_udev_device_get_subsystem (device);
name = g_udev_device_get_name (device);
g_return_if_fail (udev_device != NULL);
/* Ensure cached port probe infos get removed when the port is gone */
mm_port_probe_cache_remove (device);
mm_port_probe_cache_remove (udev_device);
if (strcmp (subsys, "usb") != 0) {
/* find_modem_for_port handles tty and net removal */
modem = find_modem_for_port (manager, subsys, name);
if (modem) {
if (!g_str_equal (g_udev_device_get_subsystem (udev_device), "usb")) {
/* Handle tty/net port removal */
device = find_device_by_port (self, udev_device);
if (device) {
mm_info ("(%s/%s): released by modem %s",
subsys,
name,
mm_base_modem_get_device (modem));
mm_base_modem_release_port (modem, subsys, name);
return;
g_udev_device_get_subsystem (udev_device),
g_udev_device_get_name (udev_device),
g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device)));
mm_device_release_port (device, udev_device);
}
} else {
/* This case is designed to handle the case where, at least with kernel 2.6.31, unplugging
* an in-use ttyACMx device results in udev generating remove events for the usb, but the
* ttyACMx device (subsystem tty) is not removed, since it was in-use. So if we have not
* found a modem for the port (above), we're going to look here to see if we have a modem
* associated with the newly removed device. If so, we'll remove the modem, since the
* device has been removed. That way, if the device is reinserted later, we'll go through
* the process of exporting it.
*/
const char *sysfs_path = g_udev_device_get_sysfs_path (device);
modem = find_modem_for_device (manager, sysfs_path);
if (modem) {
mm_dbg ("Removing modem claimed by removed device %s", sysfs_path);
remove_modem (manager, modem);
return;
}
return;
}
/* This case is designed to handle the case where, at least with kernel 2.6.31, unplugging
* an in-use ttyACMx device results in udev generating remove events for the usb, but the
* ttyACMx device (subsystem tty) is not removed, since it was in-use. So if we have not
* found a modem for the port (above), we're going to look here to see if we have a modem
* associated with the newly removed device. If so, we'll remove the modem, since the
* device has been removed. That way, if the device is reinserted later, we'll go through
* the process of exporting it.
*/
device = find_device_by_udev_device (self, udev_device);
if (device) {
mm_dbg ("Removing device %s",
g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device)));
mm_device_remove_modem (device);
g_hash_table_remove (self->priv->devices, device);
return;
}
/* Maybe a plugin is checking whether or not the port is supported.
@@ -631,7 +459,6 @@ handle_uevent (GUdevClient *client,
/* 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") || !strcmp (subsys, "usb"));
/* We only care about tty/net devices when adding modem ports,
@@ -676,17 +503,28 @@ remove_disable_ready (MMBaseModem *modem,
GAsyncResult *res,
MMManager *self)
{
MMDevice *device;
/* We don't care about errors disabling at this point */
mm_base_modem_disable_finish (modem, res, NULL);
remove_modem (self, modem);
device = find_device_by_modem (self, modem);
if (device) {
mm_device_remove_modem (device);
g_hash_table_remove (self->priv->devices, device);
}
}
static void
foreach_disable (gpointer key,
MMBaseModem *modem,
MMDevice *device,
MMManager *self)
{
mm_base_modem_disable (modem, (GAsyncReadyCallback)remove_disable_ready, self);
MMBaseModem *modem;
modem = mm_device_peek_modem (device);
if (modem)
mm_base_modem_disable (modem, (GAsyncReadyCallback)remove_disable_ready, self);
}
void
@@ -698,7 +536,7 @@ mm_manager_shutdown (MMManager *self)
/* Cancel all ongoing auth requests */
g_cancellable_cancel (self->priv->authp_cancellable);
g_hash_table_foreach (self->priv->modems, (GHFunc)foreach_disable, self);
g_hash_table_foreach (self->priv->devices, (GHFunc)foreach_disable, self);
/* Disabling may take a few iterations of the mainloop, so the caller
* has to iterate the mainloop until all devices have been disabled and
@@ -709,10 +547,20 @@ mm_manager_shutdown (MMManager *self)
guint32
mm_manager_num_modems (MMManager *self)
{
GHashTableIter iter;
gpointer key, value;
guint32 n;
g_return_val_if_fail (self != NULL, 0);
g_return_val_if_fail (MM_IS_MANAGER (self), 0);
return g_hash_table_size (self->priv->modems);
n = 0;
g_hash_table_iter_init (&iter, self->priv->devices);
while (g_hash_table_iter_next (&iter, &key, &value)) {
n += !!mm_device_peek_modem (MM_DEVICE (value));
}
return n;
}
/*****************************************************************************/
@@ -894,8 +742,8 @@ mm_manager_init (MMManager *manager)
priv->authp = mm_auth_get_provider ();
priv->authp_cancellable = g_cancellable_new ();
/* Setup internal list of modem objects */
priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
/* Setup internal lists of device objects */
priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
/* Setup UDev client */
priv->udev = g_udev_client_new (subsys);
@@ -947,7 +795,7 @@ finalize (GObject *object)
{
MMManagerPrivate *priv = MM_MANAGER (object)->priv;
g_hash_table_destroy (priv->modems);
g_hash_table_destroy (priv->devices);
mm_port_probe_cache_clear ();
+77 -21
View File
@@ -759,30 +759,86 @@ supports_port_cancel (MMPlugin *plugin,
}
static MMBaseModem *
grab_port (MMPlugin *plugin,
const char *subsys,
const char *name,
MMBaseModem *existing,
GError **error)
create_modem (MMPlugin *self,
GList *ports,
GError **error)
{
MMPluginBase *self = MM_PLUGIN_BASE (plugin);
MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self);
MMPortProbe *probe;
MMBaseModem *modem = NULL;
char *key;
MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self);
GList *probes = NULL;
GList *l;
const gchar *name, *subsys, *sysfs_path, *driver;
guint16 vendor = 0, product = 0;
key = get_key (subsys, name);
probe = g_hash_table_lookup (priv->tasks, key);
g_assert (probe);
/* Get the port probe results for each of the ports */
for (l = ports; l; l = g_list_next (l)) {
MMPortProbe *probe;
gchar *key;
/* Let the modem grab the port */
modem = MM_PLUGIN_BASE_GET_CLASS (self)->grab_port (self,
existing,
probe,
error);
subsys = g_udev_device_get_subsystem (G_UDEV_DEVICE (l->data));
name = g_udev_device_get_name (G_UDEV_DEVICE (l->data));
key = get_key (subsys, name);
probe = g_hash_table_lookup (priv->tasks, key);
g_assert (probe);
probes = g_list_prepend (probes, g_object_ref (probe));
g_free (key);
}
/* Get info from the first probe in the list */
subsys = mm_port_probe_get_port_subsys (probes->data);
name = mm_port_probe_get_port_name (probes->data);
sysfs_path = mm_port_probe_get_port_physdev (probes->data);
driver = mm_port_probe_get_port_driver (probes->data);
/* Vendor and Product IDs are really optional, we'll just warn if they
* cannot get loaded */
if (!mm_plugin_base_get_device_ids (MM_PLUGIN_BASE (self), subsys, name, &vendor, &product))
mm_warn ("Could not get modem vendor/product ID");
/* Let the plugin create the modem from the port probe results */
modem = MM_PLUGIN_BASE_GET_CLASS (self)->create_modem (MM_PLUGIN_BASE (self),
sysfs_path,
driver,
vendor,
product,
probes,
error);
if (modem) {
/* Grab each port */
for (l = probes; l; l = g_list_next (l)) {
GError *inner_error = NULL;
/* If grabbing a port fails, just warn. We'll decide if the modem is
* valid or not when all ports get organized */
if (!MM_PLUGIN_BASE_GET_CLASS (self)->grab_port (MM_PLUGIN_BASE (self),
modem,
MM_PORT_PROBE (l->data),
&inner_error)) {
mm_warn ("Could not grab port (%s/%s): '%s'",
mm_port_probe_get_port_subsys (MM_PORT_PROBE (l->data)),
mm_port_probe_get_port_name (MM_PORT_PROBE (l->data)),
inner_error ? inner_error->message : "unknown error");
g_clear_error (&inner_error);
}
}
/* If organizing ports fails, consider the modem invalid */
if (!mm_base_modem_organize_ports (modem, error))
g_clear_object (&modem);
}
for (l = probes; l; l = g_list_next (l)) {
gchar *key;
key = get_key (mm_port_probe_get_port_subsys (l->data),
mm_port_probe_get_port_name (l->data));
g_hash_table_remove (priv->tasks, key);
g_free (key);
}
g_list_free_full (probes, (GDestroyNotify) g_object_unref);
g_hash_table_remove (priv->tasks, key);
g_free (key);
return modem;
}
@@ -797,7 +853,7 @@ plugin_init (MMPlugin *plugin_class)
plugin_class->supports_port = supports_port;
plugin_class->supports_port_finish = supports_port_finish;
plugin_class->supports_port_cancel = supports_port_cancel;
plugin_class->grab_port = grab_port;
plugin_class->create_modem = create_modem;
}
static void
@@ -1073,5 +1129,5 @@ mm_plugin_base_class_init (MMPluginBaseClass *klass)
"Send delay for characters in the AT port, "
"in microseconds",
0, G_MAXUINT64, 100000,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
+13 -4
View File
@@ -58,10 +58,19 @@ struct _MMPluginBaseClass {
GObjectClass parent;
/* Mandatory subclass functions */
MMBaseModem *(*grab_port) (MMPluginBase *plugin,
MMBaseModem *existing,
MMPortProbe *probe,
GError **error);
MMBaseModem *(*create_modem) (MMPluginBase *plugin,
const gchar *sysfs_path,
const gchar *driver,
guint16 vendor,
guint16 product,
GList *probes,
GError **error);
gboolean (*grab_port) (MMPluginBase *plugin,
MMBaseModem *modem,
MMPortProbe *probe,
GError **error);
};
GType mm_plugin_base_get_type (void);
+6 -9
View File
@@ -84,17 +84,14 @@ mm_plugin_supports_port_cancel (MMPlugin *plugin,
}
MMBaseModem *
mm_plugin_grab_port (MMPlugin *plugin,
const char *subsys,
const char *name,
MMBaseModem *existing,
GError **error)
mm_plugin_create_modem (MMPlugin *plugin,
GList *ports,
GError **error)
{
g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE);
g_return_val_if_fail (subsys != NULL, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
if (MM_PLUGIN_GET_INTERFACE (plugin)->create_modem)
return MM_PLUGIN_GET_INTERFACE (plugin)->create_modem (plugin, ports, error);
return MM_PLUGIN_GET_INTERFACE (plugin)->grab_port (plugin, subsys, name, existing, error);
return NULL;
}
/*****************************************************************************/
+6 -17
View File
@@ -83,19 +83,10 @@ struct _MMPlugin {
const char *subsys,
const char *name);
/* Will only be called if the plugin returns a value greater than 0 for
* the supports_port() method for this port. The plugin should create and
* return a new modem for the port's device if there is no existing modem
* to handle the port's hardware device, or should add the port to an
* existing modem and return that modem object. If an error is encountered
* while claiming the port, the error information should be returned in the
* error argument, and the plugin should return NULL.
*/
MMBaseModem * (*grab_port) (MMPlugin *self,
const char *subsys,
const char *name,
MMBaseModem *existing,
GError **error);
/* Given all the list of ports, launch creation of a new modem object */
MMBaseModem * (*create_modem) (MMPlugin *self,
GList *ports,
GError **error);
};
GType mm_plugin_get_type (void);
@@ -120,10 +111,8 @@ void mm_plugin_supports_port_cancel (MMPlugin *plugin,
const char *subsys,
const char *name);
MMBaseModem *mm_plugin_grab_port (MMPlugin *plugin,
const char *subsys,
const char *name,
MMBaseModem *existing,
MMBaseModem *mm_plugin_create_modem (MMPlugin *plugin,
GList *ports,
GError **error);
#endif /* MM_PLUGIN_H */