Add a helper to get a GUsbDevice from a FuUdevDevice

This commit is contained in:
Richard Hughes 2022-09-23 08:47:41 +01:00
parent 507b38dea9
commit ac48c865a0
4 changed files with 87 additions and 0 deletions

View File

@ -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 <gudev.h> or <gusb.h> is unavailable");
return NULL;
#endif
}
static void
fu_udev_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{

View File

@ -13,6 +13,13 @@
#define GUdevDevice GObject
#endif
#ifdef HAVE_GUSB
#include <gusb.h>
#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;

View File

@ -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);
}

View File

@ -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;