Add FuUdevDevice as a derivable helper object

There are currently three plugins that use GUdev rather than libusb, and there
are several more in the works. Add a helper object so we can make the plugins
smaller and simpler, much like we already do with FuUsbDevice.
This commit is contained in:
Richard Hughes 2018-08-24 20:15:14 +01:00
parent bb764e99a0
commit 0216d83b71
3 changed files with 422 additions and 0 deletions

381
src/fu-udev-device.c Normal file
View File

@ -0,0 +1,381 @@
/*
* Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <appstream-glib.h>
#include "fu-udev-device.h"
/**
* SECTION:fu-udev-device
* @short_description: a udev device
*
* An object that represents a udev device.
*
* See also: #FuDevice
*/
typedef struct
{
GUdevDevice *udev_device;
guint16 vendor;
guint16 model;
guint8 revision;
} FuUdevDevicePrivate;
G_DEFINE_TYPE_WITH_PRIVATE (FuUdevDevice, fu_udev_device, FU_TYPE_DEVICE)
enum {
PROP_0,
PROP_UDEV_DEVICE,
PROP_LAST
};
enum {
SIGNAL_CHANGED,
SIGNAL_LAST
};
static guint signals[SIGNAL_LAST] = { 0 };
#define GET_PRIVATE(o) (fu_udev_device_get_instance_private (o))
static void
fu_udev_device_apply_quirks (FuUdevDevice *self)
{
FuQuirks *quirks = fu_device_get_quirks (FU_DEVICE (self));
GUdevDevice *udev_device = fu_udev_device_get_dev (self);
const gchar *tmp;
/* not set */
if (quirks == NULL)
return;
/* flags */
tmp = fu_quirks_lookup_by_udev_device (quirks, udev_device, FU_QUIRKS_FLAGS);
if (tmp != NULL)
fu_device_set_custom_flags (FU_DEVICE (self), tmp);
/* name */
tmp = fu_quirks_lookup_by_udev_device (quirks, udev_device, FU_QUIRKS_NAME);
if (tmp != NULL)
fu_device_set_name (FU_DEVICE (self), tmp);
/* summary */
tmp = fu_quirks_lookup_by_udev_device (quirks, udev_device, FU_QUIRKS_SUMMARY);
if (tmp != NULL)
fu_device_set_summary (FU_DEVICE (self), tmp);
/* vendor */
tmp = fu_quirks_lookup_by_udev_device (quirks, udev_device, FU_QUIRKS_VENDOR);
if (tmp != NULL)
fu_device_set_vendor (FU_DEVICE (self), tmp);
/* version */
tmp = fu_quirks_lookup_by_udev_device (quirks, udev_device, FU_QUIRKS_VERSION);
if (tmp != NULL)
fu_device_set_version (FU_DEVICE (self), tmp);
/* icon */
tmp = fu_quirks_lookup_by_udev_device (quirks, udev_device, FU_QUIRKS_ICON);
if (tmp != NULL)
fu_device_add_icon (FU_DEVICE (self), tmp);
/* GUID */
tmp = fu_quirks_lookup_by_udev_device (quirks, udev_device, FU_QUIRKS_GUID);
if (tmp != NULL)
fu_device_add_guid (FU_DEVICE (self), tmp);
}
static void
fu_udev_device_notify_quirks_cb (FuUdevDevice *self, GParamSpec *pspec, gpointer user_data)
{
fu_udev_device_apply_quirks (self);
}
/**
* fu_udev_device_emit_changed:
* @self: A #FuUdevDevice
*
* Emits the ::changed signal for the object.
*
* Since: 1.1.2
**/
void
fu_udev_device_emit_changed (FuUdevDevice *self)
{
g_return_if_fail (FU_IS_UDEV_DEVICE (self));
g_debug ("FuUdevDevice emit changed");
g_signal_emit (self, signals[SIGNAL_CHANGED], 0);
}
static guint64
fu_udev_device_get_sysfs_attr_as_uint64 (FuUdevDevice *self, const gchar *name)
{
FuUdevDevicePrivate *priv = GET_PRIVATE (self);
const gchar *str;
guint base = 10;
str = g_udev_device_get_sysfs_attr (priv->udev_device, name);
if (str == NULL)
return 0;
if (g_str_has_prefix (str, "0x")) {
str += 2;
base = 16;
}
return (guint16) g_ascii_strtoull (str, NULL, base);
}
static void
fu_udev_device_set_dev (FuUdevDevice *self, GUdevDevice *udev_device)
{
FuUdevDevicePrivate *priv = GET_PRIVATE (self);
const gchar *tmp;
g_autofree gchar *devid1 = NULL;
g_autofree gchar *devid2 = NULL;
g_autofree gchar *subsystem = NULL;
g_return_if_fail (FU_IS_UDEV_DEVICE (self));
/* set new device */
g_set_object (&priv->udev_device, udev_device);
if (priv->udev_device == NULL)
return;
/* set ven:dev:rev */
priv->vendor = fu_udev_device_get_sysfs_attr_as_uint64 (self, "vendor");
priv->model = fu_udev_device_get_sysfs_attr_as_uint64 (self, "device");
priv->revision = fu_udev_device_get_sysfs_attr_as_uint64 (self, "revision");
/* add both device IDs */
subsystem = g_ascii_strup (fu_udev_device_get_subsystem (self), -1);
devid1 = g_strdup_printf ("%s\\VEN_%04X&DEV_%04X",
subsystem, priv->vendor, priv->model);
fu_device_add_guid (FU_DEVICE (self), devid1);
devid2 = g_strdup_printf ("%s\\VEN_%04X&DEV_%04X&REV_%02X",
subsystem, priv->vendor, priv->model, priv->revision);
fu_device_add_guid (FU_DEVICE (self), devid2);
/* set the version if the revision has been set */
if (priv->revision != 0x00) {
g_autofree gchar *version = g_strdup_printf ("%02x", priv->revision);
fu_device_set_version (FU_DEVICE (self), version);
}
/* set model */
tmp = g_udev_device_get_property (udev_device, "FWUPD_MODEL");
if (tmp == NULL)
tmp = g_udev_device_get_property (udev_device, "ID_MODEL_FROM_DATABASE");
if (tmp != NULL)
fu_device_set_name (FU_DEVICE (self), tmp);
/* set vendor */
tmp = g_udev_device_get_property (udev_device, "FWUPD_VENDOR");
if (tmp == NULL)
tmp = g_udev_device_get_property (udev_device, "ID_VENDOR_FROM_DATABASE");
if (tmp != NULL)
fu_device_set_vendor (FU_DEVICE (self), tmp);
/* set vendor ID */
tmp = g_udev_device_get_subsystem (udev_device);
if (tmp != NULL) {
g_autofree gchar *subsys = g_ascii_strup (tmp, -1);
g_autofree gchar *vendor_id = NULL;
vendor_id = g_strdup_printf ("%s:0x%04X", subsys, (guint) priv->vendor);
fu_device_set_vendor_id (FU_DEVICE (self), vendor_id);
}
/* set udev platform ID automatically */
fu_device_set_platform_id (FU_DEVICE (self),
g_udev_device_get_sysfs_path (udev_device));
/* set the quirks again */
fu_udev_device_apply_quirks (self);
}
/**
* fu_udev_device_get_dev:
* @self: A #FuUdevDevice
*
* Gets the #GUdevDevice.
*
* Returns: (transfer none): a #GUdevDevice, or %NULL
*
* Since: 1.1.2
**/
GUdevDevice *
fu_udev_device_get_dev (FuUdevDevice *self)
{
FuUdevDevicePrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), NULL);
return priv->udev_device;
}
/**
* fu_udev_device_get_subsystem:
* @udev_device: A #GUdevDevice
*
* Gets the device subsystem, e.g. "pci".
*
* Returns: a subsystem, or NULL if unset or invalid
*
* Since: 1.1.2
**/
const gchar *
fu_udev_device_get_subsystem (FuUdevDevice *self)
{
FuUdevDevicePrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), NULL);
return g_udev_device_get_subsystem (priv->udev_device);
}
/**
* fu_udev_device_get_vendor:
* @udev_device: A #GUdevDevice
*
* Gets the device vendor code.
*
* Returns: a vendor code, or 0 if unset or invalid
*
* Since: 1.1.2
**/
guint16
fu_udev_device_get_vendor (FuUdevDevice *self)
{
FuUdevDevicePrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), 0x0000);
return priv->vendor;
}
/**
* fu_udev_device_get_model:
* @udev_device: A #GUdevDevice
*
* Gets the device device code.
*
* Returns: a vendor code, or 0 if unset or invalid
*
* Since: 1.1.2
**/
guint16
fu_udev_device_get_model (FuUdevDevice *self)
{
FuUdevDevicePrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), 0x0000);
return priv->model;
}
/**
* fu_udev_device_get_revision:
* @udev_device: A #GUdevDevice
*
* Gets the device revision.
*
* Returns: a vendor code, or 0 if unset or invalid
*
* Since: 1.1.2
**/
guint8
fu_udev_device_get_revision (FuUdevDevice *self)
{
FuUdevDevicePrivate *priv = GET_PRIVATE (self);
g_return_val_if_fail (FU_IS_UDEV_DEVICE (self), 0x00);
return priv->revision;
}
static void
fu_udev_device_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
FuUdevDevice *self = FU_UDEV_DEVICE (object);
FuUdevDevicePrivate *priv = GET_PRIVATE (self);
switch (prop_id) {
case PROP_UDEV_DEVICE:
g_value_set_object (value, priv->udev_device);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
fu_udev_device_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
FuUdevDevice *self = FU_UDEV_DEVICE (object);
switch (prop_id) {
case PROP_UDEV_DEVICE:
fu_udev_device_set_dev (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
fu_udev_device_finalize (GObject *object)
{
FuUdevDevice *self = FU_UDEV_DEVICE (object);
FuUdevDevicePrivate *priv = GET_PRIVATE (self);
if (priv->udev_device != NULL)
g_object_unref (priv->udev_device);
G_OBJECT_CLASS (fu_udev_device_parent_class)->finalize (object);
}
static void
fu_udev_device_init (FuUdevDevice *self)
{
g_signal_connect (self, "notify::quirks",
G_CALLBACK (fu_udev_device_notify_quirks_cb), NULL);
}
static void
fu_udev_device_class_init (FuUdevDeviceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
object_class->finalize = fu_udev_device_finalize;
object_class->get_property = fu_udev_device_get_property;
object_class->set_property = fu_udev_device_set_property;
signals[SIGNAL_CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
pspec = g_param_spec_object ("udev-device", NULL, NULL,
G_UDEV_TYPE_DEVICE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_UDEV_DEVICE, pspec);
}
/**
* fu_udev_device_new:
* @udev_device: A #GUdevDevice
*
* Creates a new #FuUdevDevice.
*
* Returns: (transfer full): a #FuUdevDevice
*
* Since: 1.1.2
**/
FuDevice *
fu_udev_device_new (GUdevDevice *udev_device)
{
FuUdevDevice *self = g_object_new (FU_TYPE_UDEV_DEVICE,
"udev-device", udev_device,
NULL);
return FU_DEVICE (self);
}

36
src/fu-udev-device.h Normal file
View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef __FU_UDEV_DEVICE_H
#define __FU_UDEV_DEVICE_H
#include <glib-object.h>
#include <gudev/gudev.h>
#include "fu-plugin.h"
G_BEGIN_DECLS
#define FU_TYPE_UDEV_DEVICE (fu_udev_device_get_type ())
G_DECLARE_DERIVABLE_TYPE (FuUdevDevice, fu_udev_device, FU, UDEV_DEVICE, FuDevice)
struct _FuUdevDeviceClass
{
FuDeviceClass parent_class;
gpointer __reserved[31];
};
FuDevice *fu_udev_device_new (GUdevDevice *udev_device);
void fu_udev_device_emit_changed (FuUdevDevice *self);
GUdevDevice *fu_udev_device_get_dev (FuUdevDevice *self);
const gchar *fu_udev_device_get_subsystem (FuUdevDevice *self);
guint16 fu_udev_device_get_vendor (FuUdevDevice *self);
guint16 fu_udev_device_get_model (FuUdevDevice *self);
guint8 fu_udev_device_get_revision (FuUdevDevice *self);
G_END_DECLS
#endif /* __FU_UDEV_DEVICE_H */

View File

@ -38,6 +38,7 @@ libfwupdprivate = static_library(
'fu-quirks.c',
'fu-smbios.c',
'fu-test.c',
'fu-udev-device.c',
'fu-usb-device.c',
],
include_directories : [
@ -127,6 +128,7 @@ fwupdtool = executable(
'fu-plugin-list.c',
'fu-quirks.c',
'fu-smbios.c',
'fu-udev-device.c',
'fu-usb-device.c',
'fu-util-common.c',
],
@ -204,6 +206,7 @@ executable(
'fu-plugin-list.c',
'fu-quirks.c',
'fu-smbios.c',
'fu-udev-device.c',
'fu-usb-device.c',
],
include_directories : [
@ -270,6 +273,7 @@ if get_option('tests')
'fu-quirks.c',
'fu-smbios.c',
'fu-test.c',
'fu-udev-device.c',
'fu-usb-device.c',
],
include_directories : [
@ -320,6 +324,7 @@ if get_option('introspection')
'fu-plugin.h',
'fu-quirks.c',
'fu-quirks.h',
'fu-udev-device.c',
'fu-usb-device.c',
],
nsversion : '1.0',