udev: move device probing and detection to udev

Get rid of dependency on HAL, using libgudev instead.  Fix up the plugin API
to no longer use either HAL or udev defines, but let plugins use whatever
mechanism they want for getting more information out of the device given the
subsystem and device node name.

Modems are now defined as "master" devices which "own" a one or more ports.
A port could be a serial tty device or a network device or whatever.  The
plugin figures out whether it supports a given port or not and then assigns
it to a new or existing modem.  Modems now have a 'valid' property that
should be set to TRUE when the modem has enough ports to operate correctly.
For devices (ex. 'hso') that use a network device for data transfer, the
modem would need to grab at least one TTY and the network device associated
with that physical device to be 'valid'.

Also move the generic modem support code to a plugin like other modem plugins,
and change the I-support-this-device mechanism to return a number indicating
the level of support.  For example, the generic plugin would return a quite
low number if the device indicates via probing that it can do GSM or CDMA, but
a more specific plugin can indicate better support for the device, and thus
the more specific plugin would win control.
This commit is contained in:
Dan Williams
2009-06-18 14:25:30 -04:00
parent 0555cc1824
commit 924814c101
27 changed files with 3279 additions and 1597 deletions
+17
View File
@@ -1,4 +1,5 @@
pkglib_LTLIBRARIES = \
libmm-plugin-generic.la \
libmm-plugin-huawei.la \
libmm-plugin-hso.la \
libmm-plugin-mbm.la \
@@ -8,6 +9,22 @@ pkglib_LTLIBRARIES = \
libmm-plugin-nokia.la \
libmm-plugin-zte.la
# Generic
libmm_plugin_generic_la_SOURCES = \
mm-plugin-generic.c \
mm-plugin-generic.h
libmm_plugin_generic_la_CPPFLAGS = \
$(MM_CFLAGS) \
$(GUDEV_CFLAGS) \
-I$(top_srcdir)/src
libmm_plugin_generic_la_LDFLAGS = \
$(GUDEV_LDFLAGS) \
-module \
-avoid-version
# Huawei
libmm_plugin_huawei_la_SOURCES = \
+350
View File
@@ -0,0 +1,350 @@
/* -*- 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) 2008 - 2009 Dan Williams <dcbw@redhat.com>
*/
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <time.h>
#include <gmodule.h>
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
#include <gudev/gudev.h>
#include "mm-plugin-generic.h"
#include "mm-generic-gsm.h"
#include "mm-generic-cdma.h"
#include "mm-serial-port.h"
static void plugin_init (MMPlugin *plugin_class);
G_DEFINE_TYPE_EXTENDED (MMPluginGeneric, mm_plugin_generic, G_TYPE_OBJECT,
0, G_IMPLEMENT_INTERFACE (MM_TYPE_PLUGIN, plugin_init))
int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
#define MM_PLUGIN_GENERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_PLUGIN_GENERIC, MMPluginGenericPrivate))
typedef struct {
GUdevClient *client;
GHashTable *modems;
} MMPluginGenericPrivate;
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_GENERIC, NULL));
}
/*****************************************************************************/
static char *
get_driver_name (GUdevDevice *device)
{
GUdevDevice *parent = NULL;
const char *driver;
char *ret;
driver = g_udev_device_get_driver (device);
if (!driver) {
parent = g_udev_device_get_parent (device);
if (parent)
driver = g_udev_device_get_driver (parent);
}
if (driver)
ret = g_strdup (driver);
if (parent)
g_object_unref (parent);
return ret;
}
static GUdevDevice *
find_physical_device (GUdevDevice *child)
{
GUdevDevice *iter, *old = NULL;
const char *bus, *type;
g_return_val_if_fail (child != NULL, NULL);
bus = g_udev_device_get_property (child, "ID_BUS");
if (!bus)
return NULL;
if (!strcmp (bus, "usb")) {
/* Walk the parents to find the 'usb_device' for this device. */
iter = g_object_ref (child);
while (iter) {
type = g_udev_device_get_devtype (iter);
if (type && !strcmp (type, "usb_device"))
return iter;
old = iter;
iter = g_udev_device_get_parent (old);
g_object_unref (old);
}
g_object_unref (child);
} else if (!strcmp (bus, "pci")) {
return g_udev_device_get_parent (child);
}
// FIXME: pci and pcmcia/cardbus? (like Sierra 850/860)
return NULL;
}
#define PROP_GSM "ID_MM_MODEM_GSM"
#define PROP_CDMA "ID_MM_MODEM_IS707_A"
#define PROP_EVDO1 "ID_MM_MODEM_IS856"
#define PROP_EVDOA "ID_MM_MODEM_IS856_A"
static GUdevDevice *
get_device_type (MMPlugin *plugin,
const char *subsys,
const char *name,
gboolean *gsm,
gboolean *cdma)
{
MMPluginGenericPrivate *priv = MM_PLUGIN_GENERIC_GET_PRIVATE (plugin);
GUdevDevice *device;
g_return_val_if_fail (plugin != NULL, NULL);
g_return_val_if_fail (MM_IS_PLUGIN (plugin), NULL);
g_return_val_if_fail (subsys != NULL, NULL);
g_return_val_if_fail (name != NULL, NULL);
device = g_udev_client_query_by_subsystem_and_name (priv->client, subsys, name);
if (!device)
return NULL;
if (g_udev_device_get_property_as_boolean (device, PROP_GSM))
*gsm = TRUE;
if ( g_udev_device_get_property_as_boolean (device, PROP_CDMA)
|| g_udev_device_get_property_as_boolean (device, PROP_EVDO1)
|| g_udev_device_get_property_as_boolean (device, PROP_EVDOA))
*cdma = TRUE;
return device;
}
static guint32
supports_port (MMPlugin *plugin,
const char *subsys,
const char *name)
{
GUdevDevice *device, *physdev = NULL;
gboolean gsm = FALSE, cdma = FALSE;
guint32 level = 0;
g_return_val_if_fail (plugin != NULL, 0);
g_return_val_if_fail (MM_IS_PLUGIN (plugin), 0);
g_return_val_if_fail (subsys != NULL, 0);
g_return_val_if_fail (name != NULL, 0);
/* Can't do anything with non-serial ports */
if (strcmp (subsys, "tty"))
return 0;
device = get_device_type (plugin, subsys, name, &gsm, &cdma);
if (!device)
return 0;
if (!gsm && !cdma)
goto out;
physdev = find_physical_device (device);
if (!physdev)
goto out;
g_object_unref (physdev);
level = 5;
out:
return level;
}
typedef struct {
char *key;
gpointer modem;
} FindInfo;
static void
find_modem (gpointer key, gpointer data, gpointer user_data)
{
FindInfo *info = user_data;
if (!info->key && data == info->modem)
info->key = g_strdup ((const char *) key);
}
static void
modem_destroyed (gpointer data, GObject *modem)
{
MMPluginGeneric *self = MM_PLUGIN_GENERIC (data);
MMPluginGenericPrivate *priv = MM_PLUGIN_GENERIC_GET_PRIVATE (self);
FindInfo info = { NULL, modem };
g_hash_table_foreach (priv->modems, find_modem, &info);
if (info.key)
g_hash_table_remove (priv->modems, info.key);
g_free (info.key);
}
static MMModem *
grab_port (MMPlugin *plugin,
const char *subsys,
const char *name,
GError **error)
{
MMPluginGeneric *self = MM_PLUGIN_GENERIC (plugin);
MMPluginGenericPrivate *priv = MM_PLUGIN_GENERIC_GET_PRIVATE (plugin);
GUdevDevice *device = NULL, *physdev = NULL;
const char *devfile, *sysfs_path;
char *driver = NULL;
MMModem *modem = NULL;
gboolean gsm = FALSE, cdma = FALSE;
g_return_val_if_fail (subsys != NULL, NULL);
g_return_val_if_fail (name != NULL, NULL);
/* Can't do anything with non-serial ports */
if (strcmp (subsys, "tty"))
return NULL;
device = get_device_type (plugin, subsys, name, &gsm, &cdma);
if (!device) {
g_set_error (error, 0, 0, "Could not get port's udev device.");
return NULL;
}
if (!gsm && !cdma) {
g_set_error (error, 0, 0, "Modem unsupported (not GSM or CDMA).");
goto out;
}
physdev = find_physical_device (device);
if (!physdev) {
g_set_error (error, 0, 0, "Could not get ports's physical device.");
goto out;
}
devfile = g_udev_device_get_device_file (device);
if (!devfile) {
g_set_error (error, 0, 0, "Could not get port's sysfs file.");
goto out;
}
driver = get_driver_name (device);
if (!devfile) {
g_set_error (error, 0, 0, "Could not get port's driver name.");
goto out;
}
sysfs_path = g_udev_device_get_sysfs_path (physdev);
if (!devfile) {
g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
goto out;
}
modem = g_hash_table_lookup (priv->modems, sysfs_path);
if (!modem) {
if (gsm) {
modem = mm_generic_gsm_new (sysfs_path,
driver,
mm_plugin_get_name (plugin));
} else if (cdma) {
modem = mm_generic_cdma_new (sysfs_path,
driver,
mm_plugin_get_name (plugin));
}
if (modem) {
if (!mm_modem_grab_port (modem, subsys, name, error)) {
g_object_unref (modem);
modem = NULL;
}
}
if (modem) {
g_object_weak_ref (G_OBJECT (modem), modem_destroyed, self);
g_hash_table_insert (priv->modems, g_strdup (sysfs_path), modem);
}
} else {
if (gsm || cdma) {
if (!mm_modem_grab_port (modem, subsys, name, error))
modem = NULL;
}
}
out:
g_free (driver);
g_object_unref (device);
g_object_unref (physdev);
return modem;
}
static const char *
get_name (MMPlugin *plugin)
{
return "Generic";
}
/*****************************************************************************/
static void
plugin_init (MMPlugin *plugin_class)
{
/* interface implementation */
plugin_class->get_name = get_name;
plugin_class->supports_port = supports_port;
plugin_class->grab_port = grab_port;
}
static void
mm_plugin_generic_init (MMPluginGeneric *self)
{
MMPluginGenericPrivate *priv = MM_PLUGIN_GENERIC_GET_PRIVATE (self);
const char *subsys[2] = { "tty", NULL };
priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
priv->client = g_udev_client_new (subsys);
}
static void
dispose (GObject *object)
{
MMPluginGenericPrivate *priv = MM_PLUGIN_GENERIC_GET_PRIVATE (object);
g_hash_table_destroy (priv->modems);
g_object_unref (priv->client);
}
static void
mm_plugin_generic_class_init (MMPluginGenericClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMPluginGenericPrivate));
object_class->dispose = dispose;
}
+27
View File
@@ -0,0 +1,27 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#ifndef MM_PLUGIN_GENERIC_H
#define MM_PLUGIN_GENERIC_H
#include "mm-plugin.h"
#define MM_TYPE_PLUGIN_GENERIC (mm_plugin_generic_get_type ())
#define MM_PLUGIN_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_GENERIC, MMPluginGeneric))
#define MM_PLUGIN_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_GENERIC, MMPluginGenericClass))
#define MM_IS_PLUGIN_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_GENERIC))
#define MM_IS_PLUGIN_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_GENERIC))
#define MM_PLUGIN_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_GENERIC, MMPluginGenericClass))
typedef struct {
GObject parent;
} MMPluginGeneric;
typedef struct {
GObjectClass parent;
} MMPluginGenericClass;
GType mm_plugin_generic_get_type (void);
G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
#endif /* MM_PLUGIN_GENERIC_H */