From ac48c865a027fe761699df548d0e3cd14ece8ff6 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 23 Sep 2022 08:47:41 +0100 Subject: [PATCH] Add a helper to get a GUsbDevice from a FuUdevDevice --- libfwupdplugin/fu-udev-device.c | 69 +++++++++++++++++++++++++++++++++ libfwupdplugin/fu-udev-device.h | 10 +++++ libfwupdplugin/fu-usb-device.c | 7 ++++ libfwupdplugin/fwupdplugin.map | 1 + 4 files changed, 87 insertions(+) diff --git a/libfwupdplugin/fu-udev-device.c b/libfwupdplugin/fu-udev-device.c index b6ec668e2..a282a3373 100644 --- a/libfwupdplugin/fu-udev-device.c +++ b/libfwupdplugin/fu-udev-device.c @@ -2081,6 +2081,75 @@ fu_udev_device_get_children_with_subsystem(FuUdevDevice *self, const gchar *cons return g_steal_pointer(&out); } +/** + * fu_udev_device_find_usb_device: + * @FuUdevDevice: a #FuUdevDevice + * @error: (nullable): optional return location for an error + * + * Gets the matching #GUsbDevice for the #GUdevDevice. + * + * NOTE: This should never be stored in the device class as an instance variable, as the lifecycle + * for `GUsbDevice` may be different to the `FuUdevDevice`. Every time the `GUsbDevice` is used + * this function should be called. + * + * Returns: (transfer full): a #GUsbDevice, or NULL if unset or invalid + * + * Since: 1.8.7 + **/ +GUsbDevice * +fu_udev_device_find_usb_device(FuUdevDevice *self, GError **error) +{ +#if defined(HAVE_GUDEV) && defined(HAVE_GUSB) + FuUdevDevicePrivate *priv = GET_PRIVATE(self); + guint8 bus = 0; + guint8 address = 0; + g_autoptr(GUdevDevice) udev_device = g_object_ref(priv->udev_device); + g_autoptr(GUsbContext) usb_ctx = NULL; + g_autoptr(GUsbDevice) usb_device = NULL; + + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* look at the current device and all the parent devices until we can find the USB data */ + while (TRUE) { + g_autoptr(GUdevDevice) udev_device_parent = NULL; + bus = g_udev_device_get_sysfs_attr_as_int(udev_device, "busnum"); + address = g_udev_device_get_sysfs_attr_as_int(udev_device, "devnum"); + if (bus != 0 || address != 0) + break; + udev_device_parent = g_udev_device_get_parent(udev_device); + g_set_object(&udev_device, udev_device_parent); + } + + /* nothing found */ + if (bus == 0x0 && address == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "No parent device with busnum and devnum"); + return NULL; + } + + /* match device */ + usb_ctx = g_usb_context_new(error); + if (usb_ctx == NULL) + return NULL; + usb_device = g_usb_context_find_by_bus_address(usb_ctx, bus, address, error); + if (usb_device == NULL) + return NULL; +#if G_USB_CHECK_VERSION(0, 4, 1) + g_usb_device_add_tag(usb_device, "is-transient"); +#endif + return g_steal_pointer(&usb_device); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as or is unavailable"); + return NULL; +#endif +} + static void fu_udev_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { diff --git a/libfwupdplugin/fu-udev-device.h b/libfwupdplugin/fu-udev-device.h index abc744a04..5c78aeff1 100644 --- a/libfwupdplugin/fu-udev-device.h +++ b/libfwupdplugin/fu-udev-device.h @@ -13,6 +13,13 @@ #define GUdevDevice GObject #endif +#ifdef HAVE_GUSB +#include +#else +#define GUsbContext GObject +#define GUsbDevice GObject +#endif + #include "fu-plugin.h" #define FU_TYPE_UDEV_DEVICE (fu_udev_device_get_type()) @@ -139,3 +146,6 @@ GPtrArray * fu_udev_device_get_children_with_subsystem(FuUdevDevice *self, const gchar *subsystem); FuUdevDevice * fu_udev_device_get_parent_with_subsystem(FuUdevDevice *self, const gchar *subsystem); + +GUsbDevice * +fu_udev_device_find_usb_device(FuUdevDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT; diff --git a/libfwupdplugin/fu-usb-device.c b/libfwupdplugin/fu-usb-device.c index 0f21c05e4..b2abbe95a 100644 --- a/libfwupdplugin/fu-usb-device.c +++ b/libfwupdplugin/fu-usb-device.c @@ -834,6 +834,13 @@ fu_udev_device_unbind_driver(FuDevice *device, GError **error) FuUsbDevice * fu_usb_device_new(FuContext *ctx, GUsbDevice *usb_device) { +#if G_USB_CHECK_VERSION(0, 4, 3) + if (g_usb_device_has_tag(usb_device, "is-transient")) { + g_critical("cannot use a device built using fu_udev_device_find_usb_device() as " + "the GUsbContext is different"); + return NULL; + } +#endif return g_object_new(FU_TYPE_USB_DEVICE, "context", ctx, "usb-device", usb_device, NULL); } diff --git a/libfwupdplugin/fwupdplugin.map b/libfwupdplugin/fwupdplugin.map index e7bd97d63..a9a49ca0f 100644 --- a/libfwupdplugin/fwupdplugin.map +++ b/libfwupdplugin/fwupdplugin.map @@ -1139,6 +1139,7 @@ LIBFWUPDPLUGIN_1.8.6 { LIBFWUPDPLUGIN_1.8.7 { global: + fu_udev_device_find_usb_device; fu_udev_device_set_device_file; local: *; } LIBFWUPDPLUGIN_1.8.6;