mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-14 07:09:57 +00:00
modem-manager: add generic support for PCI based modems
No longer rely on the modems being USB based, we can also support PCI based devices with the same protocols.
This commit is contained in:
parent
99b4475777
commit
3417128704
@ -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,17 +297,34 @@ 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));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* convert the instance IDs to GUIDs */
|
||||
fu_device_convert_instance_ids (device);
|
||||
|
@ -12,21 +12,65 @@
|
||||
|
||||
#include "fu-mm-utils.h"
|
||||
|
||||
static gchar *
|
||||
find_device_bus_subsystem (GUdevDevice *device)
|
||||
{
|
||||
g_autoptr(GUdevDevice) iter = NULL;
|
||||
|
||||
iter = g_object_ref (device);
|
||||
while (iter != NULL) {
|
||||
g_autoptr(GUdevDevice) next = NULL;
|
||||
const gchar *subsys;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
next = g_udev_device_get_parent (iter);
|
||||
g_set_object (&iter, next);
|
||||
}
|
||||
|
||||
/* 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_ifnum,
|
||||
gint *out_port_usb_ifnum,
|
||||
GError **error)
|
||||
{
|
||||
gint port_ifnum = -1;
|
||||
const gchar *aux;
|
||||
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: bus not found");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (g_strcmp0 (device_bus, "USB") == 0) {
|
||||
/* ID_USB_INTERFACE_NUM is set on the port device itself */
|
||||
aux = g_udev_device_get_property (device, "ID_USB_INTERFACE_NUM");
|
||||
const gchar *aux = g_udev_device_get_property (device, "ID_USB_INTERFACE_NUM");
|
||||
if (aux != NULL)
|
||||
port_ifnum = (guint16) g_ascii_strtoull (aux, NULL, 16);
|
||||
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
|
||||
@ -45,26 +89,52 @@ fu_mm_utils_get_udev_port_info (GUdevDevice *device,
|
||||
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 (parent == 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_FOUND,
|
||||
"failed to lookup device info: parent usb_device not found");
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
"device bus unsupported: %s", device_bus);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (out_port_ifnum != NULL)
|
||||
*out_port_ifnum = port_ifnum;
|
||||
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);
|
||||
}
|
||||
|
@ -11,12 +11,14 @@
|
||||
#include <gudev/gudev.h>
|
||||
|
||||
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 */
|
||||
|
@ -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"))) {
|
||||
|
Loading…
Reference in New Issue
Block a user