mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-02 00:37:35 +00:00
721 lines
19 KiB
C
721 lines
19 KiB
C
/*
|
|
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#define G_LOG_DOMAIN "FuUsbDevice"
|
|
|
|
#include "config.h"
|
|
|
|
#include "fu-device-private.h"
|
|
#include "fu-usb-device-private.h"
|
|
|
|
/**
|
|
* FuUsbDevice:
|
|
*
|
|
* A USB device.
|
|
*
|
|
* See also: [class@FuDevice], [class@FuHidDevice]
|
|
*/
|
|
|
|
typedef struct {
|
|
GUsbDevice *usb_device;
|
|
FuDeviceLocker *usb_device_locker;
|
|
} FuUsbDevicePrivate;
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE(FuUsbDevice, fu_usb_device, FU_TYPE_DEVICE)
|
|
enum { PROP_0, PROP_USB_DEVICE, PROP_LAST };
|
|
|
|
#define GET_PRIVATE(o) (fu_usb_device_get_instance_private(o))
|
|
|
|
static void
|
|
fu_usb_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
|
|
{
|
|
FuUsbDevice *device = FU_USB_DEVICE(object);
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(device);
|
|
switch (prop_id) {
|
|
case PROP_USB_DEVICE:
|
|
g_value_set_object(value, priv->usb_device);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fu_usb_device_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
|
|
{
|
|
FuUsbDevice *device = FU_USB_DEVICE(object);
|
|
switch (prop_id) {
|
|
case PROP_USB_DEVICE:
|
|
fu_usb_device_set_dev(device, g_value_get_object(value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fu_usb_device_finalize(GObject *object)
|
|
{
|
|
FuUsbDevice *device = FU_USB_DEVICE(object);
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(device);
|
|
|
|
if (priv->usb_device_locker != NULL)
|
|
g_object_unref(priv->usb_device_locker);
|
|
if (priv->usb_device != NULL)
|
|
g_object_unref(priv->usb_device);
|
|
|
|
G_OBJECT_CLASS(fu_usb_device_parent_class)->finalize(object);
|
|
}
|
|
|
|
static void
|
|
fu_usb_device_init(FuUsbDevice *device)
|
|
{
|
|
#ifdef HAVE_GUSB
|
|
fu_device_retry_add_recovery(FU_DEVICE(device),
|
|
G_USB_DEVICE_ERROR,
|
|
G_USB_DEVICE_ERROR_NO_DEVICE,
|
|
NULL);
|
|
fu_device_retry_add_recovery(FU_DEVICE(device),
|
|
G_USB_DEVICE_ERROR,
|
|
G_USB_DEVICE_ERROR_PERMISSION_DENIED,
|
|
NULL);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fu_usb_device_is_open:
|
|
* @device: a #FuUsbDevice
|
|
*
|
|
* Finds out if a USB device is currently open.
|
|
*
|
|
* Returns: %TRUE if the device is open.
|
|
*
|
|
* Since: 1.0.3
|
|
**/
|
|
gboolean
|
|
fu_usb_device_is_open(FuUsbDevice *device)
|
|
{
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(device);
|
|
g_return_val_if_fail(FU_IS_USB_DEVICE(device), FALSE);
|
|
return priv->usb_device_locker != NULL;
|
|
}
|
|
|
|
#ifdef HAVE_GUSB
|
|
static gboolean
|
|
fu_usb_device_query_hub(FuUsbDevice *self, GError **error)
|
|
{
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(self);
|
|
gsize sz = 0;
|
|
guint16 value = 0x29;
|
|
guint8 data[0x0c] = {0x0};
|
|
g_autofree gchar *devid = NULL;
|
|
|
|
/* longer descriptor for SuperSpeed */
|
|
if (fu_usb_device_get_spec(self) >= 0x0300)
|
|
value = 0x2a;
|
|
if (!g_usb_device_control_transfer(priv->usb_device,
|
|
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
|
|
G_USB_DEVICE_REQUEST_TYPE_CLASS,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
0x06, /* LIBUSB_REQUEST_GET_DESCRIPTOR */
|
|
value << 8,
|
|
0x00,
|
|
data,
|
|
sizeof(data),
|
|
&sz,
|
|
1000,
|
|
NULL,
|
|
error)) {
|
|
g_prefix_error(error, "failed to get USB descriptor: ");
|
|
return FALSE;
|
|
}
|
|
if (g_getenv("FU_USB_DEVICE_DEBUG") != NULL)
|
|
fu_common_dump_raw(G_LOG_DOMAIN, "HUB_DT", data, sz);
|
|
|
|
/* see http://www.usblyzer.com/usb-hub-class-decoder.htm */
|
|
if (sz == 0x09) {
|
|
devid = g_strdup_printf("USB\\VID_%04X&PID_%04X&HUB_%02X",
|
|
g_usb_device_get_vid(priv->usb_device),
|
|
g_usb_device_get_pid(priv->usb_device),
|
|
data[7]);
|
|
fu_device_add_instance_id(FU_DEVICE(self), devid);
|
|
} else if (sz == 0x0c) {
|
|
devid = g_strdup_printf("USB\\VID_%04X&PID_%04X&HUB_%02X%02X",
|
|
g_usb_device_get_vid(priv->usb_device),
|
|
g_usb_device_get_pid(priv->usb_device),
|
|
data[11],
|
|
data[10]);
|
|
fu_device_add_instance_id(FU_DEVICE(self), devid);
|
|
}
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
static gboolean
|
|
fu_usb_device_open(FuDevice *device, GError **error)
|
|
{
|
|
#ifdef HAVE_GUSB
|
|
FuUsbDevice *self = FU_USB_DEVICE(device);
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(FuDeviceLocker) locker = NULL;
|
|
|
|
g_return_val_if_fail(FU_IS_USB_DEVICE(self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
|
|
/* already open */
|
|
if (priv->usb_device_locker != NULL)
|
|
return TRUE;
|
|
|
|
/* open */
|
|
locker = fu_device_locker_new(priv->usb_device, error);
|
|
if (locker == NULL)
|
|
return FALSE;
|
|
|
|
/* success */
|
|
priv->usb_device_locker = g_steal_pointer(&locker);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_usb_device_setup(FuDevice *device, GError **error)
|
|
{
|
|
#ifdef HAVE_GUSB
|
|
FuUsbDevice *self = FU_USB_DEVICE(device);
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(self);
|
|
guint idx;
|
|
|
|
g_return_val_if_fail(FU_IS_USB_DEVICE(self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
|
|
/* get vendor */
|
|
if (fu_device_get_vendor(device) == NULL) {
|
|
idx = g_usb_device_get_manufacturer_index(priv->usb_device);
|
|
if (idx != 0x00) {
|
|
g_autofree gchar *tmp = NULL;
|
|
g_autoptr(GError) error_local = NULL;
|
|
tmp =
|
|
g_usb_device_get_string_descriptor(priv->usb_device, idx, &error_local);
|
|
if (tmp != NULL)
|
|
fu_device_set_vendor(device, g_strchomp(tmp));
|
|
else
|
|
g_debug(
|
|
"failed to load manufacturer string for usb device %u:%u: %s",
|
|
g_usb_device_get_bus(priv->usb_device),
|
|
g_usb_device_get_address(priv->usb_device),
|
|
error_local->message);
|
|
}
|
|
}
|
|
|
|
/* get product */
|
|
if (fu_device_get_name(device) == NULL) {
|
|
idx = g_usb_device_get_product_index(priv->usb_device);
|
|
if (idx != 0x00) {
|
|
g_autofree gchar *tmp = NULL;
|
|
g_autoptr(GError) error_local = NULL;
|
|
tmp =
|
|
g_usb_device_get_string_descriptor(priv->usb_device, idx, &error_local);
|
|
if (tmp != NULL)
|
|
fu_device_set_name(device, g_strchomp(tmp));
|
|
else
|
|
g_debug("failed to load product string for usb device %u:%u: %s",
|
|
g_usb_device_get_bus(priv->usb_device),
|
|
g_usb_device_get_address(priv->usb_device),
|
|
error_local->message);
|
|
}
|
|
}
|
|
|
|
/* get serial number */
|
|
if (!fu_device_has_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_NO_SERIAL_NUMBER) &&
|
|
fu_device_get_serial(device) == NULL) {
|
|
idx = g_usb_device_get_serial_number_index(priv->usb_device);
|
|
if (idx != 0x00) {
|
|
g_autofree gchar *tmp = NULL;
|
|
g_autoptr(GError) error_local = NULL;
|
|
tmp =
|
|
g_usb_device_get_string_descriptor(priv->usb_device, idx, &error_local);
|
|
if (tmp != NULL)
|
|
fu_device_set_serial(device, g_strchomp(tmp));
|
|
else
|
|
g_debug(
|
|
"failed to load serial number string for usb device %u:%u: %s",
|
|
g_usb_device_get_bus(priv->usb_device),
|
|
g_usb_device_get_address(priv->usb_device),
|
|
error_local->message);
|
|
}
|
|
}
|
|
|
|
/* get the hub descriptor if this is a hub */
|
|
if (g_usb_device_get_device_class(priv->usb_device) == G_USB_DEVICE_CLASS_HUB) {
|
|
if (!fu_usb_device_query_hub(self, error))
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_usb_device_close(FuDevice *device, GError **error)
|
|
{
|
|
FuUsbDevice *self = FU_USB_DEVICE(device);
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(self);
|
|
|
|
g_return_val_if_fail(FU_IS_USB_DEVICE(self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
|
|
/* already open */
|
|
if (priv->usb_device_locker == NULL)
|
|
return TRUE;
|
|
|
|
g_clear_object(&priv->usb_device_locker);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_usb_device_probe(FuDevice *device, GError **error)
|
|
{
|
|
#ifdef HAVE_GUSB
|
|
FuUsbDevice *self = FU_USB_DEVICE(device);
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(self);
|
|
guint16 release;
|
|
g_autofree gchar *devid0 = NULL;
|
|
g_autofree gchar *devid1 = NULL;
|
|
g_autofree gchar *devid2 = NULL;
|
|
g_autofree gchar *platform_id = NULL;
|
|
g_autofree gchar *vendor_id = NULL;
|
|
g_autoptr(GPtrArray) intfs = NULL;
|
|
|
|
/* set vendor ID */
|
|
vendor_id = g_strdup_printf("USB:0x%04X", g_usb_device_get_vid(priv->usb_device));
|
|
fu_device_add_vendor_id(device, vendor_id);
|
|
|
|
/* set the version if the release has been set */
|
|
release = g_usb_device_get_release(priv->usb_device);
|
|
if (release != 0x0 &&
|
|
fu_device_get_version_format(device) == FWUPD_VERSION_FORMAT_UNKNOWN) {
|
|
g_autofree gchar *version = NULL;
|
|
version = fu_common_version_from_uint16(release, FWUPD_VERSION_FORMAT_BCD);
|
|
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_BCD);
|
|
fu_device_set_version(device, version);
|
|
}
|
|
|
|
/* add GUIDs in order of priority */
|
|
devid2 = g_strdup_printf("USB\\VID_%04X&PID_%04X&REV_%04X",
|
|
g_usb_device_get_vid(priv->usb_device),
|
|
g_usb_device_get_pid(priv->usb_device),
|
|
release);
|
|
fu_device_add_instance_id(device, devid2);
|
|
devid1 = g_strdup_printf("USB\\VID_%04X&PID_%04X",
|
|
g_usb_device_get_vid(priv->usb_device),
|
|
g_usb_device_get_pid(priv->usb_device));
|
|
fu_device_add_instance_id(device, devid1);
|
|
devid0 = g_strdup_printf("USB\\VID_%04X", g_usb_device_get_vid(priv->usb_device));
|
|
fu_device_add_instance_id_full(device, devid0, FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS);
|
|
|
|
/* add the interface GUIDs */
|
|
intfs = g_usb_device_get_interfaces(priv->usb_device, error);
|
|
if (intfs == NULL)
|
|
return FALSE;
|
|
for (guint i = 0; i < intfs->len; i++) {
|
|
GUsbInterface *intf = g_ptr_array_index(intfs, i);
|
|
g_autofree gchar *intid1 = NULL;
|
|
g_autofree gchar *intid2 = NULL;
|
|
g_autofree gchar *intid3 = NULL;
|
|
intid1 = g_strdup_printf("USB\\CLASS_%02X&SUBCLASS_%02X&PROT_%02X",
|
|
g_usb_interface_get_class(intf),
|
|
g_usb_interface_get_subclass(intf),
|
|
g_usb_interface_get_protocol(intf));
|
|
fu_device_add_instance_id_full(device, intid1, FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS);
|
|
intid2 = g_strdup_printf("USB\\CLASS_%02X&SUBCLASS_%02X",
|
|
g_usb_interface_get_class(intf),
|
|
g_usb_interface_get_subclass(intf));
|
|
fu_device_add_instance_id_full(device, intid2, FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS);
|
|
intid3 = g_strdup_printf("USB\\CLASS_%02X", g_usb_interface_get_class(intf));
|
|
fu_device_add_instance_id_full(device, intid3, FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS);
|
|
}
|
|
|
|
/* add 2 levels of parent IDs */
|
|
platform_id = g_strdup(g_usb_device_get_platform_id(priv->usb_device));
|
|
for (guint i = 0; i < 2; i++) {
|
|
gchar *tok = g_strrstr(platform_id, ":");
|
|
if (tok == NULL)
|
|
break;
|
|
*tok = '\0';
|
|
if (g_strcmp0(platform_id, "usb") == 0)
|
|
break;
|
|
fu_device_add_parent_physical_id(device, platform_id);
|
|
}
|
|
#endif
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_usb_device_get_vid:
|
|
* @self: a #FuUsbDevice
|
|
*
|
|
* Gets the device vendor code.
|
|
*
|
|
* Returns: integer, or 0x0 if unset or invalid
|
|
*
|
|
* Since: 1.1.2
|
|
**/
|
|
guint16
|
|
fu_usb_device_get_vid(FuUsbDevice *self)
|
|
{
|
|
#ifdef HAVE_GUSB
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FU_IS_USB_DEVICE(self), 0x0000);
|
|
if (priv->usb_device == NULL)
|
|
return 0x0;
|
|
return g_usb_device_get_vid(priv->usb_device);
|
|
#else
|
|
return 0x0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fu_usb_device_get_pid:
|
|
* @self: a #FuUsbDevice
|
|
*
|
|
* Gets the device product code.
|
|
*
|
|
* Returns: integer, or 0x0 if unset or invalid
|
|
*
|
|
* Since: 1.1.2
|
|
**/
|
|
guint16
|
|
fu_usb_device_get_pid(FuUsbDevice *self)
|
|
{
|
|
#ifdef HAVE_GUSB
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FU_IS_USB_DEVICE(self), 0x0000);
|
|
if (priv->usb_device == NULL)
|
|
return 0x0;
|
|
return g_usb_device_get_pid(priv->usb_device);
|
|
#else
|
|
return 0x0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fu_usb_device_get_platform_id:
|
|
* @self: a #FuUsbDevice
|
|
*
|
|
* Gets the device platform ID.
|
|
*
|
|
* Returns: string, or NULL if unset or invalid
|
|
*
|
|
* Since: 1.1.2
|
|
**/
|
|
const gchar *
|
|
fu_usb_device_get_platform_id(FuUsbDevice *self)
|
|
{
|
|
#ifdef HAVE_GUSB
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FU_IS_USB_DEVICE(self), NULL);
|
|
if (priv->usb_device == NULL)
|
|
return NULL;
|
|
return g_usb_device_get_platform_id(priv->usb_device);
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fu_usb_device_get_spec:
|
|
* @self: a #FuUsbDevice
|
|
*
|
|
* Gets the string USB revision for the device.
|
|
*
|
|
* Returns: a specification revision in BCD format, or 0x0 if not supported
|
|
*
|
|
* Since: 1.3.4
|
|
**/
|
|
guint16
|
|
fu_usb_device_get_spec(FuUsbDevice *self)
|
|
{
|
|
#if G_USB_CHECK_VERSION(0, 3, 1)
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FU_IS_USB_DEVICE(self), 0x0);
|
|
if (priv->usb_device == NULL)
|
|
return 0x0;
|
|
return g_usb_device_get_spec(priv->usb_device);
|
|
#else
|
|
return 0x0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fu_usb_device_set_dev:
|
|
* @device: a #FuUsbDevice
|
|
* @usb_device: (nullable): optional #GUsbDevice
|
|
*
|
|
* Sets the #GUsbDevice to use.
|
|
*
|
|
* Since: 1.0.2
|
|
**/
|
|
void
|
|
fu_usb_device_set_dev(FuUsbDevice *device, GUsbDevice *usb_device)
|
|
{
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(device);
|
|
|
|
g_return_if_fail(FU_IS_USB_DEVICE(device));
|
|
|
|
/* need to re-probe hardware */
|
|
fu_device_probe_invalidate(FU_DEVICE(device));
|
|
|
|
/* allow replacement */
|
|
g_set_object(&priv->usb_device, usb_device);
|
|
if (usb_device == NULL) {
|
|
g_clear_object(&priv->usb_device_locker);
|
|
return;
|
|
}
|
|
|
|
#ifdef HAVE_GUSB
|
|
/* set device ID automatically */
|
|
fu_device_set_physical_id(FU_DEVICE(device), g_usb_device_get_platform_id(usb_device));
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* fu_usb_device_find_udev_device:
|
|
* @device: a #FuUsbDevice
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Gets the matching #GUdevDevice for the #GUsbDevice.
|
|
*
|
|
* Returns: (transfer full): a #GUdevDevice, or NULL if unset or invalid
|
|
*
|
|
* Since: 1.3.2
|
|
**/
|
|
GUdevDevice *
|
|
fu_usb_device_find_udev_device(FuUsbDevice *device, GError **error)
|
|
{
|
|
#if defined(HAVE_GUDEV) && defined(HAVE_GUSB)
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(device);
|
|
g_autoptr(GList) devices = NULL;
|
|
g_autoptr(GUdevClient) gudev_client = g_udev_client_new(NULL);
|
|
|
|
g_return_val_if_fail(FU_IS_USB_DEVICE(device), NULL);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
|
|
|
|
/* find all tty devices */
|
|
devices = g_udev_client_query_by_subsystem(gudev_client, "usb");
|
|
for (GList *l = devices; l != NULL; l = l->next) {
|
|
GUdevDevice *dev = G_UDEV_DEVICE(l->data);
|
|
|
|
/* check correct device */
|
|
if (g_udev_device_get_sysfs_attr_as_int(dev, "busnum") !=
|
|
g_usb_device_get_bus(priv->usb_device))
|
|
continue;
|
|
if (g_udev_device_get_sysfs_attr_as_int(dev, "devnum") !=
|
|
g_usb_device_get_address(priv->usb_device))
|
|
continue;
|
|
|
|
/* success */
|
|
g_debug("USB device %u:%u is %s",
|
|
g_usb_device_get_bus(priv->usb_device),
|
|
g_usb_device_get_address(priv->usb_device),
|
|
g_udev_device_get_sysfs_path(dev));
|
|
return g_object_ref(dev);
|
|
}
|
|
|
|
/* failure */
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"could not find sysfs device for %u:%u",
|
|
g_usb_device_get_bus(priv->usb_device),
|
|
g_usb_device_get_address(priv->usb_device));
|
|
#else
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_NOT_SUPPORTED,
|
|
"Not supported as <gudev.h> is unavailable");
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* fu_usb_device_get_dev:
|
|
* @device: a #FuUsbDevice
|
|
*
|
|
* Gets the #GUsbDevice.
|
|
*
|
|
* Returns: (transfer none): a USB device, or %NULL
|
|
*
|
|
* Since: 1.0.2
|
|
**/
|
|
GUsbDevice *
|
|
fu_usb_device_get_dev(FuUsbDevice *device)
|
|
{
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(device);
|
|
g_return_val_if_fail(FU_IS_USB_DEVICE(device), NULL);
|
|
return priv->usb_device;
|
|
}
|
|
|
|
static void
|
|
fu_usb_device_incorporate(FuDevice *self, FuDevice *donor)
|
|
{
|
|
g_return_if_fail(FU_IS_USB_DEVICE(self));
|
|
g_return_if_fail(FU_IS_USB_DEVICE(donor));
|
|
fu_usb_device_set_dev(FU_USB_DEVICE(self), fu_usb_device_get_dev(FU_USB_DEVICE(donor)));
|
|
}
|
|
|
|
static gboolean
|
|
fu_udev_device_bind_driver(FuDevice *device,
|
|
const gchar *subsystem,
|
|
const gchar *driver,
|
|
GError **error)
|
|
{
|
|
FuUsbDevice *self = FU_USB_DEVICE(device);
|
|
g_autoptr(GUdevDevice) dev = NULL;
|
|
g_autoptr(FuUdevDevice) udev_device = NULL;
|
|
|
|
/* use udev for this */
|
|
dev = fu_usb_device_find_udev_device(self, error);
|
|
if (dev == NULL)
|
|
return FALSE;
|
|
udev_device = fu_udev_device_new(dev);
|
|
return fu_device_bind_driver(FU_DEVICE(udev_device), subsystem, driver, error);
|
|
}
|
|
|
|
static gboolean
|
|
fu_udev_device_unbind_driver(FuDevice *device, GError **error)
|
|
{
|
|
FuUsbDevice *self = FU_USB_DEVICE(device);
|
|
g_autoptr(GUdevDevice) dev = NULL;
|
|
g_autoptr(FuUdevDevice) udev_device = NULL;
|
|
|
|
/* use udev for this */
|
|
dev = fu_usb_device_find_udev_device(self, error);
|
|
if (dev == NULL)
|
|
return FALSE;
|
|
udev_device = fu_udev_device_new(dev);
|
|
return fu_device_unbind_driver(FU_DEVICE(udev_device), error);
|
|
}
|
|
|
|
/**
|
|
* fu_usb_device_new:
|
|
* @usb_device: a USB device
|
|
*
|
|
* Creates a new #FuUsbDevice.
|
|
*
|
|
* Returns: (transfer full): a #FuUsbDevice
|
|
*
|
|
* Since: 1.0.2
|
|
**/
|
|
FuUsbDevice *
|
|
fu_usb_device_new(GUsbDevice *usb_device)
|
|
{
|
|
FuUsbDevice *device = g_object_new(FU_TYPE_USB_DEVICE, NULL);
|
|
fu_usb_device_set_dev(device, usb_device);
|
|
return FU_USB_DEVICE(device);
|
|
}
|
|
|
|
#ifdef HAVE_GUSB
|
|
static const gchar *
|
|
fu_usb_device_class_code_to_string(GUsbDeviceClassCode code)
|
|
{
|
|
if (code == G_USB_DEVICE_CLASS_INTERFACE_DESC)
|
|
return "interface-desc";
|
|
if (code == G_USB_DEVICE_CLASS_AUDIO)
|
|
return "audio";
|
|
if (code == G_USB_DEVICE_CLASS_COMMUNICATIONS)
|
|
return "communications";
|
|
if (code == G_USB_DEVICE_CLASS_HID)
|
|
return "hid";
|
|
if (code == G_USB_DEVICE_CLASS_PHYSICAL)
|
|
return "physical";
|
|
if (code == G_USB_DEVICE_CLASS_IMAGE)
|
|
return "image";
|
|
if (code == G_USB_DEVICE_CLASS_PRINTER)
|
|
return "printer";
|
|
if (code == G_USB_DEVICE_CLASS_MASS_STORAGE)
|
|
return "mass-storage";
|
|
if (code == G_USB_DEVICE_CLASS_HUB)
|
|
return "hub";
|
|
if (code == G_USB_DEVICE_CLASS_CDC_DATA)
|
|
return "cdc-data";
|
|
if (code == G_USB_DEVICE_CLASS_SMART_CARD)
|
|
return "smart-card";
|
|
if (code == G_USB_DEVICE_CLASS_CONTENT_SECURITY)
|
|
return "content-security";
|
|
if (code == G_USB_DEVICE_CLASS_VIDEO)
|
|
return "video";
|
|
if (code == G_USB_DEVICE_CLASS_PERSONAL_HEALTHCARE)
|
|
return "personal-healthcare";
|
|
if (code == G_USB_DEVICE_CLASS_AUDIO_VIDEO)
|
|
return "audio-video";
|
|
if (code == G_USB_DEVICE_CLASS_BILLBOARD)
|
|
return "billboard";
|
|
if (code == G_USB_DEVICE_CLASS_DIAGNOSTIC)
|
|
return "diagnostic";
|
|
if (code == G_USB_DEVICE_CLASS_WIRELESS_CONTROLLER)
|
|
return "wireless-controller";
|
|
if (code == G_USB_DEVICE_CLASS_MISCELLANEOUS)
|
|
return "miscellaneous";
|
|
if (code == G_USB_DEVICE_CLASS_APPLICATION_SPECIFIC)
|
|
return "application-specific";
|
|
if (code == G_USB_DEVICE_CLASS_VENDOR_SPECIFIC)
|
|
return "vendor-specific";
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
fu_usb_device_to_string(FuDevice *device, guint idt, GString *str)
|
|
{
|
|
#ifdef HAVE_GUSB
|
|
FuUsbDevice *self = FU_USB_DEVICE(device);
|
|
FuUsbDevicePrivate *priv = GET_PRIVATE(self);
|
|
if (priv->usb_device != NULL) {
|
|
GUsbDeviceClassCode code = g_usb_device_get_device_class(priv->usb_device);
|
|
fu_common_string_append_kv(str,
|
|
idt,
|
|
"UsbDeviceClass",
|
|
fu_usb_device_class_code_to_string(code));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
fu_usb_device_class_init(FuUsbDeviceClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
|
FuDeviceClass *device_class = FU_DEVICE_CLASS(klass);
|
|
GParamSpec *pspec;
|
|
|
|
object_class->finalize = fu_usb_device_finalize;
|
|
object_class->get_property = fu_usb_device_get_property;
|
|
object_class->set_property = fu_usb_device_set_property;
|
|
device_class->open = fu_usb_device_open;
|
|
device_class->setup = fu_usb_device_setup;
|
|
device_class->close = fu_usb_device_close;
|
|
device_class->probe = fu_usb_device_probe;
|
|
device_class->to_string = fu_usb_device_to_string;
|
|
device_class->incorporate = fu_usb_device_incorporate;
|
|
device_class->bind_driver = fu_udev_device_bind_driver;
|
|
device_class->unbind_driver = fu_udev_device_unbind_driver;
|
|
|
|
pspec = g_param_spec_object("usb-device",
|
|
NULL,
|
|
NULL,
|
|
#ifdef HAVE_GUSB
|
|
G_USB_TYPE_DEVICE,
|
|
#else
|
|
G_TYPE_OBJECT,
|
|
#endif
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME);
|
|
g_object_class_install_property(object_class, PROP_USB_DEVICE, pspec);
|
|
}
|