diff --git a/plugins/modem-manager/fu-mm-device.c b/plugins/modem-manager/fu-mm-device.c index 1d42c17da..f5865c1ba 100644 --- a/plugins/modem-manager/fu-mm-device.c +++ b/plugins/modem-manager/fu-mm-device.c @@ -144,6 +144,7 @@ fu_mm_device_probe_default (FuDevice *device, GError **error) guint n_ports = 0; g_autoptr(MMFirmwareUpdateSettings) update_settings = NULL; g_autofree gchar *device_sysfs_path = NULL; + g_autofree gchar *device_bus = NULL; /* inhibition uid is the modem interface 'Device' property, which may * be the device sysfs path or a different user-provided id */ @@ -251,11 +252,12 @@ fu_mm_device_probe_default (FuDevice *device, GError **error) } if (self->port_at != NULL) { - fu_mm_utils_get_port_info (self->port_at, &device_sysfs_path, &self->port_at_ifnum, NULL); + fu_mm_utils_get_port_info (self->port_at, &device_bus, &device_sysfs_path, &self->port_at_ifnum, NULL); } if (self->port_qmi != NULL) { g_autofree gchar *qmi_device_sysfs_path = NULL; - fu_mm_utils_get_port_info (self->port_qmi, &qmi_device_sysfs_path, &self->port_qmi_ifnum, NULL); + g_autofree gchar *qmi_device_bus = NULL; + fu_mm_utils_get_port_info (self->port_qmi, &qmi_device_bus, &qmi_device_sysfs_path, &self->port_qmi_ifnum, NULL); if (device_sysfs_path == NULL && qmi_device_sysfs_path != NULL) { device_sysfs_path = g_steal_pointer (&qmi_device_sysfs_path); } else if (g_strcmp0 (device_sysfs_path, qmi_device_sysfs_path) != 0) { @@ -264,14 +266,24 @@ fu_mm_device_probe_default (FuDevice *device, GError **error) device_sysfs_path, qmi_device_sysfs_path); return FALSE; } + if (device_bus == NULL && qmi_device_bus != NULL) { + device_bus = g_steal_pointer (&qmi_device_bus); + } else if (g_strcmp0 (device_bus, qmi_device_bus) != 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "mismatched device bus: %s != %s", + device_bus, qmi_device_bus); + return FALSE; + } } /* if no device sysfs file, error out */ - if (device_sysfs_path == NULL) { + if (device_sysfs_path == NULL || device_bus == NULL) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "failed to find device sysfs path"); + "failed to find device details"); return FALSE; } @@ -285,15 +297,32 @@ fu_mm_device_probe_default (FuDevice *device, GError **error) for (guint i = 0; device_ids[i] != NULL; i++) fu_device_add_instance_id (device, device_ids[i]); if (fu_device_get_vendor_ids (device) == NULL) { - g_autofree gchar *path = g_build_filename (device_sysfs_path, "idVendor", NULL); - g_autofree gchar *value = NULL; + g_autofree gchar *path = NULL; + g_autofree gchar *value_str = NULL; g_autoptr(GError) error_local = NULL; - if (!g_file_get_contents (path, &value, NULL, &error_local)) { + if (g_strcmp0 (device_bus, "USB") == 0) + path = g_build_filename (device_sysfs_path, "idVendor", NULL); + else if (g_strcmp0 (device_bus, "PCI") == 0) + path = g_build_filename (device_sysfs_path, "vendor", NULL); + + if (path == NULL) { + g_warning ("failed to set vendor ID: unsupported bus: %s", device_bus); + } else if (!g_file_get_contents (path, &value_str, NULL, &error_local)) { g_warning ("failed to set vendor ID: %s", error_local->message); } else { - g_autofree gchar *vendor_id = g_strdup_printf ("USB:0x%s", g_strchomp (value)); - fu_device_add_vendor_id (device, vendor_id); + guint64 value_int; + + /* note: the string value may be prefixed with '0x' (e.g. when reading + * the PCI 'vendor' attribute, or not prefixed with anything, as in the + * USB 'idVendor' attribute. */ + value_int = g_ascii_strtoull (value_str, NULL, 16); + if (value_int > G_MAXUINT16) { + g_warning ("failed to set vendor ID: invalid value: %s", value_str); + } else { + g_autofree gchar *vendor_id = g_strdup_printf ("%s:0x%04X", device_bus, (guint) value_int); + fu_device_add_vendor_id (device, vendor_id); + } } } diff --git a/plugins/modem-manager/fu-mm-utils.c b/plugins/modem-manager/fu-mm-utils.c index d39f03315..86164e835 100644 --- a/plugins/modem-manager/fu-mm-utils.c +++ b/plugins/modem-manager/fu-mm-utils.c @@ -12,59 +12,129 @@ #include "fu-mm-utils.h" -gboolean -fu_mm_utils_get_udev_port_info (GUdevDevice *device, - gchar **out_device_sysfs_path, - gint *out_port_ifnum, - GError **error) +static gchar * +find_device_bus_subsystem (GUdevDevice *device) { - gint port_ifnum = -1; - const gchar *aux; - g_autoptr(GUdevDevice) parent = NULL; - g_autofree gchar *device_sysfs_path = NULL; + g_autoptr(GUdevDevice) iter = NULL; - /* ID_USB_INTERFACE_NUM is set on the port device itself */ - aux = g_udev_device_get_property (device, "ID_USB_INTERFACE_NUM"); - if (aux != NULL) - port_ifnum = (guint16) g_ascii_strtoull (aux, NULL, 16); - - /* we need to traverse all parents of the give udev device until we find - * the first 'usb_device' reported, which is the GUdevDevice associated with - * the full USB device (i.e. all ports of the same device). - */ - parent = g_udev_device_get_parent (device); - while (parent != NULL) { + iter = g_object_ref (device); + while (iter != NULL) { g_autoptr(GUdevDevice) next = NULL; + const gchar *subsys; - if (g_strcmp0 (g_udev_device_get_devtype (parent), "usb_device") == 0) { - device_sysfs_path = g_strdup (g_udev_device_get_sysfs_path (parent)); - break; + /* stop search as soon as we find a parent object + * of one of the bus subsystems supported in + * ModemManager */ + subsys = g_udev_device_get_subsystem (iter); + if ((g_strcmp0 (subsys, "usb") == 0) || + (g_strcmp0 (subsys, "pcmcia") == 0) || + (g_strcmp0 (subsys, "pci") == 0) || + (g_strcmp0 (subsys, "platform") == 0) || + (g_strcmp0 (subsys, "pnp") == 0) || + (g_strcmp0 (subsys, "sdio") == 0)) { + return g_ascii_strup (subsys, -1); } - /* check next parent */ - next = g_udev_device_get_parent (parent); - g_set_object (&parent, next); + next = g_udev_device_get_parent (iter); + g_set_object (&iter, next); } - if (parent == NULL) { + /* no more parents to check */ + return NULL; +} + +gboolean +fu_mm_utils_get_udev_port_info (GUdevDevice *device, + gchar **out_device_bus, + gchar **out_device_sysfs_path, + gint *out_port_usb_ifnum, + GError **error) +{ + gint port_usb_ifnum = -1; + g_autoptr(GUdevDevice) parent = NULL; + g_autofree gchar *device_sysfs_path = NULL; + g_autofree gchar *device_bus = NULL; + + /* lookup the main bus the device is in; for supported devices it will + * usually be either 'PCI' or 'USB' */ + device_bus = find_device_bus_subsystem (device); + if (device_bus == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - "failed to lookup device info: parent usb_device not found"); + "failed to lookup device info: bus not found"); return FALSE; } - if (out_port_ifnum != NULL) - *out_port_ifnum = port_ifnum; + if (g_strcmp0 (device_bus, "USB") == 0) { + /* ID_USB_INTERFACE_NUM is set on the port device itself */ + const gchar *aux = g_udev_device_get_property (device, "ID_USB_INTERFACE_NUM"); + if (aux != NULL) + port_usb_ifnum = (guint16) g_ascii_strtoull (aux, NULL, 16); + + /* we need to traverse all parents of the give udev device until we find + * the first 'usb_device' reported, which is the GUdevDevice associated with + * the full USB device (i.e. all ports of the same device). + */ + parent = g_udev_device_get_parent (device); + while (parent != NULL) { + g_autoptr(GUdevDevice) next = NULL; + + if (g_strcmp0 (g_udev_device_get_devtype (parent), "usb_device") == 0) { + device_sysfs_path = g_strdup (g_udev_device_get_sysfs_path (parent)); + break; + } + + /* check next parent */ + next = g_udev_device_get_parent (parent); + g_set_object (&parent, next); + } + } else if (g_strcmp0 (device_bus, "PCI") == 0) { + /* the first parent in the 'pci' subsystem is our physical device */ + parent = g_udev_device_get_parent (device); + while (parent != NULL) { + g_autoptr(GUdevDevice) next = NULL; + + if (g_strcmp0 (g_udev_device_get_subsystem (parent), "pci") == 0) { + device_sysfs_path = g_strdup (g_udev_device_get_sysfs_path (parent)); + break; + } + + /* check next parent */ + next = g_udev_device_get_parent (parent); + g_set_object (&parent, next); + } + } else { + /* other subsystems, we don't support firmware upgrade for those */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "device bus unsupported: %s", device_bus); + return FALSE; + } + + if (device_sysfs_path == NULL) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + "failed to lookup device info: physical device not found"); + return FALSE; + } + + if (out_port_usb_ifnum != NULL) + *out_port_usb_ifnum = port_usb_ifnum; if (out_device_sysfs_path != NULL) *out_device_sysfs_path = g_steal_pointer (&device_sysfs_path); + if (out_device_bus != NULL) + *out_device_bus = g_steal_pointer (&device_bus); return TRUE; } gboolean fu_mm_utils_get_port_info (const gchar *path, + gchar **out_device_bus, gchar **out_device_sysfs_path, - gint *out_port_ifnum, + gint *out_port_usb_ifnum, GError **error) { g_autoptr(GUdevClient) client = NULL; @@ -80,5 +150,5 @@ fu_mm_utils_get_port_info (const gchar *path, return FALSE; } - return fu_mm_utils_get_udev_port_info (dev, out_device_sysfs_path, out_port_ifnum, error); + return fu_mm_utils_get_udev_port_info (dev, out_device_bus, out_device_sysfs_path, out_port_usb_ifnum, error); } diff --git a/plugins/modem-manager/fu-mm-utils.h b/plugins/modem-manager/fu-mm-utils.h index e0126337e..a4ddd12ab 100644 --- a/plugins/modem-manager/fu-mm-utils.h +++ b/plugins/modem-manager/fu-mm-utils.h @@ -11,12 +11,14 @@ #include gboolean fu_mm_utils_get_udev_port_info (GUdevDevice *dev, + gchar **device_bus, gchar **device_sysfs_path, - gint *port_ifnum, + gint *port_usb_ifnum, GError **error); gboolean fu_mm_utils_get_port_info (const gchar *path, + gchar **device_bus, gchar **device_sysfs_path, - gint *port_ifnum, + gint *port_usb_ifnum, GError **error); #endif /* __FU_MM_UTILS_H */ diff --git a/plugins/modem-manager/fu-plugin-modem-manager.c b/plugins/modem-manager/fu-plugin-modem-manager.c index 514f1b0bf..4b8c43547 100644 --- a/plugins/modem-manager/fu-plugin-modem-manager.c +++ b/plugins/modem-manager/fu-plugin-modem-manager.c @@ -149,23 +149,24 @@ fu_plugin_mm_udev_uevent_cb (GUdevClient *udev, const gchar *name = g_udev_device_get_name (device); g_autofree gchar *path = NULL; g_autofree gchar *device_sysfs_path = NULL; + g_autofree gchar *device_bus = NULL; gint ifnum = -1; if (action == NULL || subsystem == NULL || priv->inhibited == NULL || name == NULL) return TRUE; /* ignore if loading port info fails */ - if (!fu_mm_utils_get_udev_port_info (device, &device_sysfs_path, &ifnum, NULL)) + if (!fu_mm_utils_get_udev_port_info (device, &device_bus, &device_sysfs_path, &ifnum, NULL)) + return TRUE; + + /* ignore non-USB and non-PCI events */ + if (g_strcmp0 (device_bus, "USB") != 0 && g_strcmp0 (device_bus, "PCI") != 0) return TRUE; /* ignore all events for ports not owned by our device */ if (g_strcmp0 (device_sysfs_path, priv->inhibited->physical_id) != 0) return TRUE; - /* ignore non-cdc-wdm usbmisc ports */ - if (g_str_equal (subsystem, "usbmisc") && !g_str_has_prefix (name, "cdc-wdm")) - return TRUE; - path = g_strdup_printf ("/dev/%s", name); if ((g_str_equal (action, "add")) || (g_str_equal (action, "change"))) {