modem: rework modem detection to better handle udev probing and multiple ports

Ignore multiple ports from the same modem device, becuase we can only use one
any exposing more than one in the UI is stupid and useless.  This requires some
drunken wandering through the HAL device tree to get the real device that all
the TTYs are owned by.

Second, prefer devices that both HAL and udev think are modems before using
a device that only udev thinks is a modem.  This requires that we delay
handling all ttys until HAL has finished sending them, so that if the second
tty is the one HAL thinks is a modem, we use that instead of some other port
that HAL sends first, but which might happen to have udev capabilities.
This commit is contained in:
Dan Williams 2009-03-16 09:35:59 -04:00
parent fd043258fe
commit d5cdea6a79
2 changed files with 468 additions and 108 deletions

View file

@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2007 - 2008 Novell, Inc.
* Copyright (C) 2007 - 2008 Red Hat, Inc.
* Copyright (C) 2007 - 2009 Red Hat, Inc.
*/
#include "config.h"
@ -50,12 +50,26 @@
#define HAL_DBUS_SERVICE "org.freedesktop.Hal"
typedef struct {
GType device_type;
char *capability_str;
char *category;
gboolean (*is_device_fn) (NMHalManager *self, const char *udi);
NMDeviceCreatorFn creator_fn;
} DeviceCreator;
static void emit_udi_added (NMHalManager *self, const char *udi, DeviceCreator *creator);
typedef struct {
LibHalContext *hal_ctx;
NMDBusManager *dbus_mgr;
GSList *device_creators;
gboolean rfkilled; /* Authoritative rfkill state */
GSList *deferred_modems;
guint deferred_modem_id;
DeviceCreator *modem_creator;
/* Killswitch handling */
GSList *killswitch_list;
guint32 killswitch_poll_id;
@ -87,14 +101,6 @@ static gboolean poll_killswitches (gpointer user_data);
/* Device creators */
typedef struct {
GType device_type;
char *capability_str;
char *category;
gboolean (*is_device_fn) (NMHalManager *self, const char *udi);
NMDeviceCreatorFn creator_fn;
} DeviceCreator;
static DeviceCreator *
get_creator (NMHalManager *self, const char *udi)
{
@ -498,7 +504,7 @@ error:
}
#endif
static gboolean
static void
hal_get_modem_capabilities (LibHalContext *ctx,
const char *udi,
gboolean *gsm,
@ -506,16 +512,16 @@ hal_get_modem_capabilities (LibHalContext *ctx,
{
char **capabilities, **iter;
g_return_val_if_fail (ctx != NULL, FALSE);
g_return_val_if_fail (udi != NULL, FALSE);
g_return_val_if_fail (gsm != NULL, FALSE);
g_return_val_if_fail (*gsm == FALSE, FALSE);
g_return_val_if_fail (cdma != NULL, FALSE);
g_return_val_if_fail (*cdma == FALSE, FALSE);
g_return_if_fail (ctx != NULL);
g_return_if_fail (udi != NULL);
g_return_if_fail (gsm != NULL);
g_return_if_fail (*gsm == FALSE);
g_return_if_fail (cdma != NULL);
g_return_if_fail (*cdma == FALSE);
/* Make sure it has the 'modem' capability first */
if (!libhal_device_query_capability (ctx, udi, "modem", NULL))
return TRUE;
return;
capabilities = libhal_device_get_property_strlist (ctx, udi, "modem.command_sets", NULL);
/* 'capabilites' may be NULL */
@ -531,81 +537,207 @@ hal_get_modem_capabilities (LibHalContext *ctx,
}
if (capabilities)
g_strfreev (capabilities);
}
/* Compatiblity with the pre-specification bits */
if (!*gsm && !*cdma) {
capabilities = libhal_device_get_property_strlist (ctx, udi, "info.capabilities", NULL);
for (iter = capabilities; iter && *iter; iter++) {
if (!strcmp (*iter, "gsm")) {
*gsm = TRUE;
break;
}
if (!strcmp (*iter, "cdma")) {
*cdma = TRUE;
break;
}
}
if (capabilities)
g_strfreev (capabilities);
typedef struct {
guint32 refcount;
char *udi;
char *origdev_udi;
gboolean gsm;
gboolean cdma;
guint32 usb_interface_number;
guint32 serial_port;
} DeferredModem;
#if 0
static void
deferred_modem_ref (DeferredModem *modem)
{
g_return_if_fail (modem != NULL);
g_return_if_fail (modem->refcount > 0);
modem->refcount++;
}
#endif
static void
deferred_modem_unref (DeferredModem *modem)
{
g_return_if_fail (modem != NULL);
g_return_if_fail (modem->refcount > 0);
modem->refcount--;
if (modem->refcount == 0) {
g_free (modem->udi);
g_free (modem->origdev_udi);
memset (modem, 0, sizeof (DeferredModem));
g_free (modem);
}
}
static DeferredModem *
deferred_modem_new (const char *udi,
const char *origdev_udi,
gboolean gsm,
gboolean cdma,
LibHalContext *ctx)
{
DeferredModem *modem;
char *parent;
dbus_int32_t serial_port = -1, usb_interface = -1;
DBusError error;
parent = libhal_device_get_property_string (ctx, udi, "info.parent", NULL);
if (!parent) {
g_warning ("couldn't get parent for '%s'", udi);
return NULL;
}
return TRUE;
modem = g_new0 (DeferredModem, 1);
modem->refcount = 1;
modem->udi = g_strdup (udi);
modem->origdev_udi = g_strdup (origdev_udi);
modem->gsm = gsm;
modem->cdma = cdma;
dbus_error_init (&error);
usb_interface = libhal_device_get_property_int (ctx, parent, "usb.interface.number", &error);
if (dbus_error_is_set (&error)) {
dbus_error_free (&error);
usb_interface = -1;
}
dbus_error_init (&error);
serial_port = libhal_device_get_property_int (ctx, udi, "serial.port", &error);
if (dbus_error_is_set (&error)) {
dbus_error_free (&error);
serial_port = -1;
}
if (usb_interface == -1 && serial_port == -1) {
g_warning ("couldn't get usb.interface.number and serial.port for '%s'", udi);
deferred_modem_unref (modem);
modem = NULL;
} else {
modem->usb_interface_number = usb_interface;
modem->serial_port = serial_port;
}
libhal_free_string (parent);
return modem;
}
static gint
deferred_modem_sort_func (const DeferredModem *a, const DeferredModem *b)
{
gint ret;
/* Sort is: origdev_udi, usb_interface, serial_port */
ret = strcmp (a->origdev_udi, b->origdev_udi);
if (ret)
return ret;
if (a->usb_interface_number < b->usb_interface_number)
return -1;
else if (a->usb_interface_number > b->usb_interface_number)
return 1;
if (a->serial_port < b->serial_port)
return -1;
else if (a->serial_port > b->serial_port)
return 1;
return 0;
}
static DeferredModem *
deferred_modem_find (NMHalManager *self, const char *udi)
{
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
GSList *iter;
for (iter = priv->deferred_modems; iter; iter = g_slist_next (iter)) {
DeferredModem *candidate = iter->data;
if (!strcmp (candidate->udi, udi))
return candidate;
}
return NULL;
}
static DeferredModem *
deferred_modem_find_by_origdev (NMHalManager *self, const char *origdev_udi)
{
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
GSList *iter;
for (iter = priv->deferred_modems; iter; iter = g_slist_next (iter)) {
DeferredModem *candidate = iter->data;
if (!strcmp (candidate->origdev_udi, origdev_udi))
return candidate;
}
return NULL;
}
static void
deferred_modem_remove (NMHalManager *self, DeferredModem *modem)
{
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
GSList *iter, *new = NULL;
g_return_if_fail (modem != NULL);
for (iter = priv->deferred_modems; iter; iter = g_slist_next (iter)) {
if (iter->data != modem)
new = g_slist_insert_sorted (new, iter->data, (GCompareFunc) deferred_modem_sort_func);
}
g_slist_free (priv->deferred_modems);
priv->deferred_modems = new;
}
static gboolean
deferred_modem_timeout (gpointer data)
{
NMHalManager *self = data;
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
GSList *iter;
nm_info ("Re-checking deferred serial ports");
for (iter = priv->deferred_modems; iter; iter = g_slist_next (iter)) {
DeferredModem *modem = iter->data;
/* re-emit udi-added for each UDI that was deferred */
emit_udi_added (self, modem->udi, priv->modem_creator);
}
g_slist_foreach (priv->deferred_modems, (GFunc) deferred_modem_unref, NULL);
g_slist_free (priv->deferred_modems);
priv->deferred_modems = NULL;
priv->deferred_modem_id = 0;
return FALSE;
}
static GObject *
modem_device_creator (NMHalManager *self,
const char *udi,
const char *origdev_udi,
gboolean managed)
new_modem_device (const char *udi,
gboolean gsm,
gboolean cdma,
const char *ttyname,
const char *driver,
gboolean managed,
LibHalContext *ctx)
{
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
char *serial_device;
const char *ttyname;
char *sysfs_path;
char *driver = NULL;
GObject *device = NULL;
gboolean type_gsm = FALSE;
gboolean type_cdma = FALSE;
char *netdev = NULL;
gboolean udev = TRUE;
serial_device = libhal_device_get_property_string (priv->hal_ctx, udi, "serial.device", NULL);
driver = nm_get_device_driver_name (priv->hal_ctx, origdev_udi);
if (!serial_device || !driver)
goto out;
/* Try udev first */
sysfs_path = libhal_device_get_property_string (priv->hal_ctx, udi, "linux.sysfs_path", NULL);
if (!sysfs_path) {
nm_warning ("could not determine sysfs path for '%s'", serial_device);
goto out;
}
ttyname = serial_device + strlen ("/dev/");
#if HAVE_LIBUDEV
libudev_get_modem_capabilities (sysfs_path, &type_gsm, &type_cdma);
#else
udevadm_get_modem_capabilities (sysfs_path, &type_gsm, &type_cdma);
#endif
libhal_free_string (sysfs_path);
/* If udev didn't know anything, try deprecated HAL modem capabilities */
if (!type_gsm && !type_cdma) {
hal_get_modem_capabilities (priv->hal_ctx, udi, &type_gsm, &type_cdma);
udev = FALSE;
}
if (!type_gsm && !type_cdma)
goto out;
nm_info ("(%s): detected %s modem via %s capabilities",
ttyname,
type_gsm ? "GSM" : "CDMA",
udev ? "udev" : "HAL");
g_return_val_if_fail (udi != NULL, NULL);
g_return_val_if_fail (gsm || cdma, NULL);
g_return_val_if_fail (ttyname != NULL, NULL);
g_return_val_if_fail (driver != NULL, NULL);
g_return_val_if_fail (ctx != NULL, NULL);
/* Special handling of 'hso' cards (until punted to ModemManager) */
if (type_gsm && !strcmp (driver, "hso")) {
if (gsm && !strcmp (driver, "hso")) {
char *hsotype_path;
char *contents = NULL;
gboolean success;
@ -621,31 +753,31 @@ modem_device_creator (NMHalManager *self,
if ( !strstr (contents, "Control")
&& !strstr (contents, "control")) {
g_free (contents);
goto out;
return NULL;
}
g_free (contents);
}
netdev = get_hso_netdev (priv->hal_ctx, udi);
netdev = get_hso_netdev (ctx, udi);
}
/* Special handling of Option cards (until punted to ModemManager). Only
* the first USB interface can be used for control and PPP.
*/
if (type_gsm && !strcmp (driver, "option")) {
if (gsm && !strcmp (driver, "option")) {
char *parent;
guint32 vendor_id = 0, usb_interface = 0;
parent = libhal_device_get_property_string (priv->hal_ctx, udi, "info.parent", NULL);
parent = libhal_device_get_property_string (ctx, udi, "info.parent", NULL);
if (!parent)
goto out;
return NULL;
vendor_id = libhal_device_get_property_int (priv->hal_ctx, parent, "usb.vendor_id", NULL);
vendor_id = libhal_device_get_property_int (ctx, parent, "usb.vendor_id", NULL);
if (vendor_id == 0x0AF0) {
usb_interface = libhal_device_get_property_int (priv->hal_ctx, parent, "usb.interface.number", NULL);
usb_interface = libhal_device_get_property_int (ctx, parent, "usb.interface.number", NULL);
if (usb_interface > 0) {
g_free (parent);
goto out;
libhal_free_string (parent);
return NULL;
}
}
libhal_free_string (parent);
@ -654,33 +786,176 @@ modem_device_creator (NMHalManager *self,
/* Special handling of Ericsson F3507g 'mbm' devices; already handled by
* ModemManager more correctly in HEAD.
*/
if (type_gsm && !strcmp (driver, "cdc_acm")) {
if (gsm && !strcmp (driver, "cdc_acm")) {
char *parent;
guint32 usb_interface;
parent = is_mbm (priv->hal_ctx, udi);
parent = is_mbm (ctx, udi);
if (parent) {
usb_interface = libhal_device_get_property_int (priv->hal_ctx, parent, "usb.interface.number", NULL);
if (usb_interface != 1) {
libhal_free_string (parent);
goto out;
}
usb_interface = libhal_device_get_property_int (ctx, parent, "usb.interface.number", NULL);
libhal_free_string (parent);
if (usb_interface != 1)
return NULL;
}
}
if (type_gsm) {
if (netdev)
device = (GObject *) nm_hso_gsm_device_new (udi, ttyname, NULL, netdev, driver, managed);
else
device = (GObject *) nm_gsm_device_new (udi, ttyname, NULL, driver, managed);
} else if (type_cdma)
if (gsm && netdev)
device = (GObject *) nm_hso_gsm_device_new (udi, ttyname, NULL, netdev, driver, managed);
else if (gsm)
device = (GObject *) nm_gsm_device_new (udi, ttyname, NULL, driver, managed);
else if (cdma)
device = (GObject *) nm_cdma_device_new (udi, ttyname, NULL, driver, managed);
else
g_assert_not_reached ();
return device;
}
static char *
nm_get_modem_device_driver_name (LibHalContext *ctx, const char *udi)
{
char *driver_name = NULL;
char *origdev_udi;
origdev_udi = libhal_device_get_property_string (ctx, udi, "serial.originating_device", NULL);
/* Older HAL uses "physical_device" */
if (!origdev_udi)
origdev_udi = libhal_device_get_property_string (ctx, udi, "serial.physical_device", NULL);
if (origdev_udi && libhal_device_property_exists (ctx, origdev_udi, "info.linux.driver", NULL)) {
char *drv = libhal_device_get_property_string (ctx, origdev_udi, "info.linux.driver", NULL);
driver_name = g_strdup (drv);
libhal_free_string (drv);
}
libhal_free_string (origdev_udi);
return driver_name;
}
static GObject *
modem_device_creator (NMHalManager *self,
const char *udi,
const char *origdev_udi,
gboolean managed)
{
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
char *serial_device;
const char *ttyname;
char *sysfs_path;
char *driver = NULL;
GObject *device = NULL;
gboolean udev_gsm = FALSE;
gboolean udev_cdma = FALSE;
gboolean hal_gsm = FALSE;
gboolean hal_cdma = FALSE;
gboolean later = FALSE;
gboolean udev_success = FALSE;
DeferredModem *deferred = NULL;
serial_device = libhal_device_get_property_string (priv->hal_ctx, udi, "serial.device", NULL);
/* For serial devices, 'origdev_udi' will be the actual USB or platform device,
* not HAL's 'serial.originating_device'.
*/
driver = nm_get_modem_device_driver_name (priv->hal_ctx, udi);
if (!serial_device || !driver)
goto out;
ttyname = serial_device + strlen ("/dev/");
/* If the UDI was deferred from earlier, just make the device with the
* cached capabilities.
*/
deferred = deferred_modem_find (self, udi);
if (deferred) {
udev_gsm = deferred->gsm;
udev_cdma = deferred->cdma;
device = new_modem_device (udi, deferred->gsm, deferred->cdma, ttyname, driver, managed, priv->hal_ctx);
goto out;
}
/* Get udev probed capabilities */
sysfs_path = libhal_device_get_property_string (priv->hal_ctx, udi, "linux.sysfs_path", NULL);
if (!sysfs_path) {
nm_warning ("(%s): could not determine sysfs path", ttyname);
goto out;
}
#if HAVE_LIBUDEV
udev_success = libudev_get_modem_capabilities (sysfs_path, &udev_gsm, &udev_cdma);
#else
udev_success = udevadm_get_modem_capabilities (sysfs_path, &udev_gsm, &udev_cdma);
#endif
libhal_free_string (sysfs_path);
/* Get HAL capabilities too */
hal_get_modem_capabilities (priv->hal_ctx, udi, &hal_gsm, &hal_cdma);
/* If for some reason running the udev prober failed fall back to HAL */
if (!udev_success) {
nm_warning ("(%s): udev probing failed; using only HAL modem capabilities.", ttyname);
udev_gsm = hal_gsm;
udev_cdma = hal_cdma;
}
/* If it's not known to either udev or HAL as a modem, nothing to do */
if (!udev_gsm && !udev_cdma && !hal_gsm && !hal_cdma) {
nm_info ("(%s): ignoring due to lack of mobile broadband capabilties", ttyname);
goto out;
}
nm_info ("(%s): found serial port (udev:%s%s%s hal:%s%s%s)",
ttyname,
udev_gsm ? "GSM" : "", udev_gsm && udev_cdma ? "/" : "", udev_cdma ? "CDMA" : "",
hal_gsm ? "GSM" : "", hal_gsm && hal_cdma ? "/" : "", hal_cdma ? "CDMA" : "");
/* If other ports owned by this port's originating device are already
* deferred, defer this port as well.
*/
later = !!deferred_modem_find_by_origdev (self, origdev_udi);
/* The logic goes like this:
*
* a) if both udev and HAL agree on the port command sets, use it
* b) if HAL thinks its a modem, but the command set disagrees with udev,
* assume udev probing is correct
* c) if HAL thinks it's a modem but udev does not, don't use it
* d) if HAL doesn't think it's a modem but udev does, schedule a timer
* to check back later after HAL is done sending ttys.
*/
if (udev_gsm == hal_gsm && udev_cdma == hal_cdma) {
/* Case (a): HAL and udev agree. */
if (!later)
device = new_modem_device (udi, udev_gsm, udev_cdma, ttyname, driver, managed, priv->hal_ctx);
} else if ((hal_gsm || hal_cdma) && (udev_gsm || udev_cdma)) {
/* Case (b): HAL and udev think it's a modem, but they don't
* agree on the command set the modem supports. Trust udev.
*/
if (!later)
device = new_modem_device (udi, udev_gsm, udev_cdma, ttyname, driver, managed, priv->hal_ctx);
} else if ((hal_gsm || hal_cdma) && !udev_gsm && !udev_cdma) {
/* Case (c): HAL thinks it's a modem, but udev doesn't */
nm_info ("(%s): ignoring due to lack of probed mobile broadband capabilties", ttyname);
later = FALSE;
} else {
/* Case (d): HAL doesn't think its a modem, but udev does */
later = TRUE;
}
if (!device && later) {
deferred = deferred_modem_new (udi, origdev_udi, udev_gsm, udev_cdma, priv->hal_ctx);
priv->deferred_modems = g_slist_insert_sorted (priv->deferred_modems,
deferred,
(GCompareFunc) deferred_modem_sort_func);
if (priv->deferred_modem_id)
g_source_remove (priv->deferred_modem_id);
priv->deferred_modem_id = g_timeout_add_seconds (4, deferred_modem_timeout, self);
nm_info ("(%s): deferring until all ports found", ttyname);
}
out:
libhal_free_string (serial_device);
g_free (driver);
return device;
}
@ -716,21 +991,62 @@ register_built_in_creators (NMHalManager *self)
creator->is_device_fn = is_modem_device;
creator->creator_fn = modem_device_creator;
priv->device_creators = g_slist_append (priv->device_creators, creator);
priv->modem_creator = creator;
}
static void
emit_udi_added (NMHalManager *self, const char *udi, DeviceCreator *creator)
{
NMHalManagerPrivate *priv = NM_HAL_MANAGER_GET_PRIVATE (self);
char *od, *tmp;
char *od = NULL, *tmp, *parent, *bus = NULL;
g_return_if_fail (self != NULL);
g_return_if_fail (udi != NULL);
g_return_if_fail (creator != NULL);
tmp = g_strdup_printf ("%s.originating_device", creator->category);
od = libhal_device_get_property_string (priv->hal_ctx, udi, tmp, NULL);
g_free (tmp);
/* For USB serial devices, originating device should really be the "USB Device"
* object, which is the grandparent of the tty device. Only this grandparent
* is really common among all ttys of the device; HAL's "serial.originating_device"
* points to the "USB Interface" parent of the tty, but this interface only
* sometimes has multiple child ttys. More commonly, the "USB Device"
* object provides several "USB Interface" objects, which each provide one tty.
* Thus, to ensure that NM only uses the correct tty for the deivce,
* we need to filter on the "USB Device" instead of the "USB Interface".
*/
parent = libhal_device_get_property_string (priv->hal_ctx, udi, "info.parent", NULL);
if (parent)
bus = libhal_device_get_property_string (priv->hal_ctx, parent, "info.bus", NULL);
if (bus && !strcmp (bus, "usb")) {
char *usb_intf_udi;
usb_intf_udi = libhal_device_get_property_string (priv->hal_ctx, udi, "info.parent", NULL);
if (usb_intf_udi) {
od = libhal_device_get_property_string (priv->hal_ctx, usb_intf_udi, "info.parent", NULL);
/* Ensure the grandparent really is the "USB Device" */
if (od) {
tmp = libhal_device_get_property_string (priv->hal_ctx, od, "info.bus", NULL);
if (!tmp || strcmp (tmp, "usb_device")) {
libhal_free_string (od);
od = NULL;
}
if (tmp)
libhal_free_string (tmp);
}
libhal_free_string (usb_intf_udi);
}
}
libhal_free_string (bus);
libhal_free_string (parent);
/* For non-USB devices, and ss a fallback, just use the originating device
* of the tty; though this might result in more than one modem being detected by NM.
*/
if (!od) {
tmp = g_strdup_printf ("%s.originating_device", creator->category);
od = libhal_device_get_property_string (priv->hal_ctx, udi, tmp, NULL);
g_free (tmp);
}
if (!od) {
/* Older HAL uses 'physical_device' */
@ -767,6 +1083,13 @@ static void
device_removed (LibHalContext *ctx, const char *udi)
{
NMHalManager *self = NM_HAL_MANAGER (libhal_ctx_get_user_data (ctx));
DeferredModem *modem;
modem = deferred_modem_find (self, udi);
if (modem) {
deferred_modem_remove (self, modem);
deferred_modem_unref (modem);
}
g_signal_emit (self, signals[UDI_REMOVED], 0, udi);
}
@ -1210,6 +1533,12 @@ dispose (GObject *object)
g_slist_foreach (priv->device_creators, destroy_creator, NULL);
g_slist_free (priv->device_creators);
g_slist_foreach (priv->deferred_modems, (GFunc) deferred_modem_unref, NULL);
g_slist_free (priv->deferred_modems);
if (priv->deferred_modem_id)
g_source_remove (priv->deferred_modem_id);
hal_deinit (self);
G_OBJECT_CLASS (nm_hal_manager_parent_class)->dispose (object);

View file

@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2007 - 2008 Novell, Inc.
* Copyright (C) 2007 - 2008 Red Hat, Inc.
* Copyright (C) 2007 - 2009 Red Hat, Inc.
*/
#include <netinet/ether.h>
@ -1160,6 +1160,29 @@ nm_manager_get_device_by_udi (NMManager *manager, const char *udi)
return NULL;
}
static NMDevice *
nm_manager_get_device_by_originating_device (NMManager *manager, const char *od)
{
GSList *iter;
for (iter = NM_MANAGER_GET_PRIVATE (manager)->devices; iter; iter = iter->next) {
const char *candidate_od = g_object_get_data (G_OBJECT (iter->data), ORIGDEV_TAG);
if (candidate_od && !strcmp (candidate_od, od))
return NM_DEVICE (iter->data);
}
return NULL;
}
static void
nm_manager_set_originating_device (NMDevice *device, const char *originating_device)
{
g_return_if_fail (device != NULL);
g_return_if_fail (originating_device != NULL);
g_object_set_data_full (G_OBJECT (device), ORIGDEV_TAG, g_strdup (originating_device), g_free);
}
static gboolean
nm_manager_udi_is_managed (NMManager *self, const char *udi)
{
@ -1667,10 +1690,18 @@ hal_manager_udi_added_cb (NMHalManager *hal_mgr,
if (nm_manager_get_device_by_udi (self, udi))
return;
/* Ignore multiple ports for serial devices */
if (general_type == NM_TYPE_SERIAL_DEVICE) {
if (nm_manager_get_device_by_originating_device (self, originating_device))
return;
}
device = creator_fn (hal_mgr, udi, originating_device, nm_manager_udi_is_managed (self, udi));
if (!device)
return;
nm_manager_set_originating_device (NM_DEVICE (device), originating_device);
priv->devices = g_slist_append (priv->devices, device);
g_signal_connect (device, "state-changed",