mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-15 02:54:47 +00:00
Add a FuI2cDevice to abstract I²C devices
This commit is contained in:
parent
2a5122b4a0
commit
99f40fa7a7
236
libfwupdplugin/fu-i2c-device.c
Normal file
236
libfwupdplugin/fu-i2c-device.c
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "FuI2cDevice"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#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);
|
||||
}
|
30
libfwupdplugin/fu-i2c-device.h
Normal file
30
libfwupdplugin/fu-i2c-device.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#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;
|
@ -23,6 +23,7 @@
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#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));
|
||||
}
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <libfwupdplugin/fu-firmware.h>
|
||||
#include <libfwupdplugin/fu-firmware-common.h>
|
||||
#include <libfwupdplugin/fu-fmap-firmware.h>
|
||||
#include <libfwupdplugin/fu-i2c-device.h>
|
||||
#include <libfwupdplugin/fu-ihex-firmware.h>
|
||||
#include <libfwupdplugin/fu-io-channel.h>
|
||||
#include <libfwupdplugin/fu-plugin.h>
|
||||
|
@ -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: *;
|
||||
|
@ -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',
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user