From 99f40fa7a71777ee8c8837b4cec7cb2d2b9ba7c0 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 18 May 2021 12:38:45 +0100 Subject: [PATCH] =?UTF-8?q?Add=20a=20FuI2cDevice=20to=20abstract=20I=C2=B2?= =?UTF-8?q?C=20devices?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libfwupdplugin/fu-i2c-device.c | 236 ++++++++++++++++++++++++++++++++ libfwupdplugin/fu-i2c-device.h | 30 ++++ libfwupdplugin/fu-udev-device.c | 37 ++--- libfwupdplugin/fwupdplugin.h | 1 + libfwupdplugin/fwupdplugin.map | 4 + libfwupdplugin/meson.build | 2 + 6 files changed, 285 insertions(+), 25 deletions(-) create mode 100644 libfwupdplugin/fu-i2c-device.c create mode 100644 libfwupdplugin/fu-i2c-device.h diff --git a/libfwupdplugin/fu-i2c-device.c b/libfwupdplugin/fu-i2c-device.c new file mode 100644 index 000000000..3d3e272c2 --- /dev/null +++ b/libfwupdplugin/fu-i2c-device.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#define G_LOG_DOMAIN "FuI2cDevice" + +#include "config.h" + +#include +#include +#include +#include +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "fu-i2c-device.h" + +/** + * SECTION:fu-i2c-device + * @short_description: a I²C device + * + * An object that represents a I²C device. + * + * See also: #FuUdevDevice + */ + +typedef struct +{ + FuUdevDevice *udev_device; + guint bus_number; +} FuI2cDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (FuI2cDevice, fu_i2c_device, FU_TYPE_UDEV_DEVICE) + +enum { + PROP_0, + PROP_INTERFACE, + PROP_LAST +}; + +#define GET_PRIVATE(o) (fu_i2c_device_get_instance_private (o)) + +static void +fu_i2c_device_to_string (FuDevice *device, guint idt, GString *str) +{ + FuI2cDevice *self = FU_I2C_DEVICE (device); + FuI2cDevicePrivate *priv = GET_PRIVATE (self); + fu_common_string_append_kx (str, idt, "BusNumber", priv->bus_number); +} + +static void +fu_i2c_device_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + FuI2cDevice *self = FU_I2C_DEVICE (object); + FuI2cDevicePrivate *priv = GET_PRIVATE (self); + switch (prop_id) { + case PROP_INTERFACE: + g_value_set_uint (value, priv->bus_number); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +fu_i2c_device_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + FuI2cDevice *self = FU_I2C_DEVICE (object); + FuI2cDevicePrivate *priv = GET_PRIVATE (self); + switch (prop_id) { + case PROP_INTERFACE: + priv->bus_number = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +fu_i2c_device_open (FuDevice *device, GError **error) +{ +#ifdef HAVE_GUDEV + FuI2cDevice *self = FU_I2C_DEVICE (device); + FuI2cDevicePrivate *priv = GET_PRIVATE (self); + gint bus_fd; + g_autofree gchar *bus_path = NULL; + + /* open the bus, not the device represented by self */ + bus_path = g_strdup_printf ("/dev/i2c-%u", priv->bus_number); + if ((bus_fd = g_open (bus_path, O_RDWR, 0)) == -1) { + g_set_error (error, + G_IO_ERROR, +#ifdef HAVE_ERRNO_H + g_io_error_from_errno (errno), +#else + G_IO_ERROR_FAILED, +#endif + "failed to open %s read-write", + bus_path); + return FALSE; + } + fu_udev_device_set_fd (FU_UDEV_DEVICE (self), bus_fd); + fu_udev_device_set_flags (FU_UDEV_DEVICE (self), FU_UDEV_DEVICE_FLAG_NONE); +#endif + + /* FuUdevDevice->open */ + return FU_DEVICE_CLASS (fu_i2c_device_parent_class)->open (device, error); +} + +static gboolean +fu_i2c_device_probe (FuDevice *device, GError **error) +{ +#ifdef HAVE_GUDEV + FuI2cDevice *self = FU_I2C_DEVICE (device); + FuI2cDevicePrivate *priv = GET_PRIVATE (self); + GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); + const gchar *tmp; + g_autoptr(GMatchInfo) info = NULL; + g_autoptr(GRegex) regex = NULL; +#endif + + /* FuUdevDevice->probe */ + if (!FU_DEVICE_CLASS (fu_i2c_device_parent_class)->probe (device, error)) + return FALSE; + +#ifdef HAVE_GUDEV + /* i2c devices all expose a name */ + tmp = g_udev_device_get_sysfs_attr (udev_device, "name"); + if (tmp != NULL) { + g_autofree gchar *devid = NULL; + g_autofree gchar *name_safe = g_strdup (tmp); + g_strdelimit (name_safe, " /\\\"", '-'); + devid = g_strdup_printf ("I2C\\NAME_%s", name_safe); + fu_device_add_instance_id (FU_DEVICE (self), devid); + } + + /* get bus number out of sysfs path */ + regex = g_regex_new ("/i2c-([0-9]+)/", 0, 0, error); + if (regex == NULL) + return FALSE; + tmp = g_udev_device_get_sysfs_path (udev_device); + if (!g_regex_match_full (regex, tmp, -1, 0, 0, &info, error)) + return FALSE; + priv->bus_number = g_ascii_strtoll (g_match_info_fetch (info, 1), + NULL, 10); +#endif + + /* success */ + return TRUE; +} + +/** + * fu_i2c_device_get_bus_number: + * @self: a #FuI2cDevice + * + * Gets the I²C bus number. + * + * Returns: integer + * + * Since: 1.6.1 + **/ +guint +fu_i2c_device_get_bus_number (FuI2cDevice *self) +{ + FuI2cDevicePrivate *priv = GET_PRIVATE (self); + g_return_val_if_fail (FU_I2C_DEVICE (self), G_MAXUINT); + return priv->bus_number; +} + +/** + * fu_i2c_device_write: + * @self: a #FuI2cDevice + * @data: value + * @error: (nullable): optional return location for an error + * + * Write a single byte to the I²C device. + * + * Returns: %TRUE for success + * + * Since: 1.6.1 + **/ +gboolean +fu_i2c_device_write (FuI2cDevice *self, guint8 data, GError **error) +{ + return fu_udev_device_pwrite_full (FU_UDEV_DEVICE (self), 0x0, &data, 0x01, error); +} + +/** + * fu_i2c_device_read: + * @self: a #FuI2cDevice + * @data: (out): value + * @error: (nullable): optional return location for an error + * + * Read a single byte from the I²C device. + * + * Returns: %TRUE for success + * + * Since: 1.6.1 + **/ +gboolean +fu_i2c_device_read (FuI2cDevice *self, guint8 *data, GError **error) +{ + return fu_udev_device_pread_full (FU_UDEV_DEVICE (self), 0x0, data, 0x1, error); +} + +static void +fu_i2c_device_init (FuI2cDevice *self) +{ +} + +static void +fu_i2c_device_class_init (FuI2cDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + object_class->get_property = fu_i2c_device_get_property; + object_class->set_property = fu_i2c_device_set_property; + klass_device->open = fu_i2c_device_open; + klass_device->probe = fu_i2c_device_probe; + klass_device->to_string = fu_i2c_device_to_string; + + pspec = g_param_spec_uint ("bus-number", NULL, NULL, + 0x0, G_MAXUINT, 0x0, + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME); + g_object_class_install_property (object_class, PROP_INTERFACE, pspec); +} diff --git a/libfwupdplugin/fu-i2c-device.h b/libfwupdplugin/fu-i2c-device.h new file mode 100644 index 000000000..d7c4b6915 --- /dev/null +++ b/libfwupdplugin/fu-i2c-device.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2021 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#include "fu-udev-device.h" + +#define FU_TYPE_I2C_DEVICE (fu_i2c_device_get_type ()) +G_DECLARE_DERIVABLE_TYPE (FuI2cDevice, fu_i2c_device, FU, I2C_DEVICE, FuUdevDevice) + +struct _FuI2cDeviceClass +{ + FuUdevDeviceClass parent_class; + gpointer __reserved[31]; +}; + +guint fu_i2c_device_get_bus_number (FuI2cDevice *self); +gboolean fu_i2c_device_read (FuI2cDevice *self, + guint8 *data, + GError **error) + G_GNUC_WARN_UNUSED_RESULT; +gboolean fu_i2c_device_write (FuI2cDevice *self, + guint8 data, + GError **error) + G_GNUC_WARN_UNUSED_RESULT; diff --git a/libfwupdplugin/fu-udev-device.c b/libfwupdplugin/fu-udev-device.c index 785aea740..ed709afa9 100644 --- a/libfwupdplugin/fu-udev-device.c +++ b/libfwupdplugin/fu-udev-device.c @@ -23,6 +23,7 @@ #include #include "fu-device-private.h" +#include "fu-i2c-device.h" #include "fu-udev-device-private.h" /** @@ -227,21 +228,6 @@ fu_udev_device_get_vendor_fallback (GUdevDevice *udev_device) #endif #ifdef HAVE_GUDEV -static gboolean -fu_udev_device_probe_i2c_dev (FuUdevDevice *self, GError **error) -{ - FuUdevDevicePrivate *priv = GET_PRIVATE (self); - const gchar *name = g_udev_device_get_sysfs_attr (priv->udev_device, "name"); - if (name != NULL) { - g_autofree gchar *devid = NULL; - g_autofree gchar *name_safe = g_strdup (name); - g_strdelimit (name_safe, " /\\\"", '-'); - devid = g_strdup_printf ("I2C\\NAME_%s", name_safe); - fu_device_add_instance_id (FU_DEVICE (self), devid); - } - return TRUE; -} - static gboolean fu_udev_device_probe_serio (FuUdevDevice *self, GError **error) { @@ -471,12 +457,6 @@ fu_udev_device_probe (FuDevice *device, GError **error) FU_DEVICE_INSTANCE_FLAG_ONLY_QUIRKS); } - /* i2c devices all expose a name */ - if (g_strcmp0 (g_udev_device_get_subsystem (priv->udev_device), "i2c-dev") == 0) { - if (!fu_udev_device_probe_i2c_dev (self, error)) - return FALSE; - } - /* add firmware_id */ if (g_strcmp0 (g_udev_device_get_subsystem (priv->udev_device), "serio") == 0) { if (!fu_udev_device_probe_serio (self, error)) @@ -1929,8 +1909,15 @@ fu_udev_device_class_init (FuUdevDeviceClass *klass) FuUdevDevice * fu_udev_device_new (GUdevDevice *udev_device) { - FuUdevDevice *self = g_object_new (FU_TYPE_UDEV_DEVICE, - "udev-device", udev_device, - NULL); - return FU_UDEV_DEVICE (self); +#ifdef HAVE_GUDEV + /* create the correct object depending on the subsystem */ + if (g_strcmp0 (g_udev_device_get_subsystem (udev_device), "i2c-dev") == 0) { + return FU_UDEV_DEVICE (g_object_new (FU_TYPE_I2C_DEVICE, + "udev-device", udev_device, + NULL)); + } +#endif + return FU_UDEV_DEVICE (g_object_new (FU_TYPE_UDEV_DEVICE, + "udev-device", udev_device, + NULL)); } diff --git a/libfwupdplugin/fwupdplugin.h b/libfwupdplugin/fwupdplugin.h index 4039e6b42..a7062b9dc 100644 --- a/libfwupdplugin/fwupdplugin.h +++ b/libfwupdplugin/fwupdplugin.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/libfwupdplugin/fwupdplugin.map b/libfwupdplugin/fwupdplugin.map index 2efcfa769..9b861cb73 100644 --- a/libfwupdplugin/fwupdplugin.map +++ b/libfwupdplugin/fwupdplugin.map @@ -794,6 +794,10 @@ LIBFWUPDPLUGIN_1.6.0 { LIBFWUPDPLUGIN_1.6.1 { global: + fu_i2c_device_get_bus_number; + fu_i2c_device_get_type; + fu_i2c_device_read; + fu_i2c_device_write; fu_srec_firmware_record_get_type; fu_version_string; local: *; diff --git a/libfwupdplugin/meson.build b/libfwupdplugin/meson.build index a834343d5..705e71f0b 100644 --- a/libfwupdplugin/meson.build +++ b/libfwupdplugin/meson.build @@ -28,6 +28,7 @@ fwupdplugin_src = [ 'fu-efi-signature-list.c', 'fu-efivar.c', 'fu-udev-device.c', + 'fu-i2c-device.c', 'fu-usb-device.c', 'fu-hid-device.c', 'fu-version.c', @@ -65,6 +66,7 @@ fwupdplugin_headers = [ 'fu-efi-signature-list.h', 'fu-efivar.h', 'fu-udev-device.h', + 'fu-i2c-device.h', 'fu-usb-device.h', 'fu-hid-device.h', ]