mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-10 01:44:10 +00:00
154 lines
4.3 KiB
C
154 lines
4.3 KiB
C
/*
|
|
* Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <fwupdplugin.h>
|
|
#include <gio/gio.h>
|
|
|
|
#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_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: bus not found");
|
|
return FALSE;
|
|
}
|
|
|
|
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_usb_ifnum,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GUdevClient) client = NULL;
|
|
g_autoptr(GUdevDevice) dev = NULL;
|
|
|
|
client = g_udev_client_new (NULL);
|
|
dev = g_udev_client_query_by_device_file (client, path);
|
|
if (dev == NULL) {
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_NOT_FOUND,
|
|
"failed to lookup device by path");
|
|
return FALSE;
|
|
}
|
|
|
|
return fu_mm_utils_get_udev_port_info (dev, out_device_bus, out_device_sysfs_path, out_port_usb_ifnum, error);
|
|
}
|