<commit_before>/*
 * Copyright (C) 2017-2019 <NAME> <<EMAIL>>
 *
 * SPDX-License-Identifier: LGPL-2.1+
 */

#define G_LOG_DOMAIN				"FuUdevDevice"

#include "config.h"

#include <fcntl.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <glib/gstdio.h>

#include "fu-device-private.h"
#include "fu-udev-device-private.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;
	gchar			*subsystem;
	gchar			*device_file;
	gint			 fd;
	gboolean		 readonly;
} FuUdevDevicePrivate;

G_DEFINE_TYPE_WITH_PRIVATE (FuUdevDevice, fu_udev_device, FU_TYPE_DEVICE)

enum {
	PROP_0,
	PROP_UDEV_DEVICE,
	PROP_SUBSYSTEM,
	PROP_DEVICE_FILE,
	PROP_LAST
};

enum {
	SIGNAL_CHANGED,
	SIGNAL_LAST
};

static guint signals[SIGNAL_LAST] = { 0 };

#define GET_PRIVATE(o) (fu_udev_device_get_instance_private (o))

/**
 * 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 (GUdevDevice *udev_device, const gchar *name)
{
	return fu_common_strtoull (g_udev_device_get_sysfs_attr (udev_device, name));
}

static guint16
fu_udev_device_read_uint16 (const gchar *str)
{
	gchar buf[5] = { 0x0, 0x0, 0x0, 0x0, 0x0 };
	memcpy (buf, str, 4);
	return (guint16) g_ascii_strtoull (buf, NULL, 16);
}

static void
fu_udev_device_dump_internal (GUdevDevice *udev_device)
{
	const gchar * const *keys;

	keys = g_udev_device_get_property_keys (udev_device);
	for (guint i = 0; keys[i] != NULL; i++) {
		g_debug ("%s={%s}", keys[i],
			 g_udev_device_get_property (udev_device, keys[i]));
	}
	keys = g_udev_device_get_sysfs_attr_keys (udev_device);
	for (guint i = 0; keys[i] != NULL; i++) {
		g_debug ("%s=[%s]", keys[i],
			 g_udev_device_get_sysfs_attr (udev_device, keys[i]));
	}
}

void
fu_udev_device_dump (FuUdevDevice *self)
{
	FuUdevDevicePrivate *priv = GET_PRIVATE (self);
	if (priv->udev_device == NULL)
		return;
	fu_udev_device_dump_internal (priv->udev_device);
}

static gboolean
fu_udev_device_probe (FuDevice *device, GError **error)
{
	FuUdevDeviceClass *klass = FU_UDEV_DEVICE_GET_CLASS (device);
	FuUdevDevice *self = FU_UDEV_DEVICE (device);
	FuUdevDevicePrivate *priv = GET_PRIVATE (self);
	const gchar *tmp;
	g_autofree gchar *subsystem = NULL;
	g_autoptr(GUdevDevice) udev_parent = NULL;
	g_autoptr(GUdevDevice) parent_i2c = NULL;

	/* nothing to do */
	if (priv->udev_device == NULL)
		return TRUE;

	/* set ven:dev:rev */
	priv->vendor = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "vendor");
	priv->model = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "device");
	priv->revision = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "revision");

	/* fallback to the parent */
	udev_parent = g_udev_device_get_parent (priv->udev_device);
	if (udev_parent != NULL &&
	    priv->vendor == 0x0 && priv->model == 0x0 && priv->revision == 0x0) {
		priv->vendor = fu_udev_device_get_sysfs_attr_as_uint64 (udev_parent, "vendor");
		priv->model = fu_udev_device_get_sysfs_attr_as_uint64 (udev_parent, "device");
		priv->revision = fu_udev_device_get_sysfs_attr_as_uint64 (udev_parent, "revision");
	}

	/* hidraw helpfully encodes the information in a different place */
	if (udev_parent != NULL &&
	    priv->vendor == 0x0 && priv->model == 0x0 && priv->revision == 0x0 &&
	    g_strcmp0 (priv->subsystem, "hidraw") == 0) {
		tmp = g_udev_device_get_property (udev_parent, "HID_ID");
		if (tmp != NULL && strlen (tmp) == 22) {
			priv->vendor = fu_udev_device_read_uint16 (tmp + 10);
			priv->model = fu_udev_device_read_uint16 (tmp + 18);
		}
		tmp = g_udev_device_get_property (udev_parent, "HID_NAME");
		if (tmp != NULL) {
			if (fu_device_get_name (device) == NULL)
				fu_device_set_name (device, tmp);
		}
	}

	/* try harder to find a vendor name the user will recognise */
	if (udev_parent != NULL && fu_device_get_vendor (device) == NULL) {
		g_autoptr(GUdevDevice) device_tmp = g_object_ref (udev_parent);
		for (guint i = 0; i < 0xff; i++) {
			g_autoptr(GUdevDevice) parent = NULL;
			const gchar *id_vendor;
			id_vendor = g_udev_device_get_property (device_tmp,
								"ID_VENDOR_FROM_DATABASE");
			if (id_vendor != NULL) {
				fu_device_set_vendor (device, id_vendor);
				break;
			}
			parent = g_udev_device_get_parent (device_tmp);
			if (parent == NULL)
				break;
			g_set_object (&device_tmp, parent);
		}
	}

	/* set the version if the revision has been set */<commit_msg>udev-device: fix offset of vendor id of hidraw devices<commit_after>/*
 * Copyright (C) 2017-2019 <NAME> <<EMAIL>.com>
 *
 * SPDX-License-Identifier: LGPL-2.1+
 */

#define G_LOG_DOMAIN				"FuUdevDevice"

#include "config.h"

#include <fcntl.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <glib/gstdio.h>

#include "fu-device-private.h"
#include "fu-udev-device-private.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;
	gchar			*subsystem;
	gchar			*device_file;
	gint			 fd;
	gboolean		 readonly;
} FuUdevDevicePrivate;

G_DEFINE_TYPE_WITH_PRIVATE (FuUdevDevice, fu_udev_device, FU_TYPE_DEVICE)

enum {
	PROP_0,
	PROP_UDEV_DEVICE,
	PROP_SUBSYSTEM,
	PROP_DEVICE_FILE,
	PROP_LAST
};

enum {
	SIGNAL_CHANGED,
	SIGNAL_LAST
};

static guint signals[SIGNAL_LAST] = { 0 };

#define GET_PRIVATE(o) (fu_udev_device_get_instance_private (o))

/**
 * 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 (GUdevDevice *udev_device, const gchar *name)
{
	return fu_common_strtoull (g_udev_device_get_sysfs_attr (udev_device, name));
}

static guint16
fu_udev_device_read_uint16 (const gchar *str)
{
	gchar buf[5] = { 0x0, 0x0, 0x0, 0x0, 0x0 };
	memcpy (buf, str, 4);
	return (guint16) g_ascii_strtoull (buf, NULL, 16);
}

static void
fu_udev_device_dump_internal (GUdevDevice *udev_device)
{
	const gchar * const *keys;

	keys = g_udev_device_get_property_keys (udev_device);
	for (guint i = 0; keys[i] != NULL; i++) {
		g_debug ("%s={%s}", keys[i],
			 g_udev_device_get_property (udev_device, keys[i]));
	}
	keys = g_udev_device_get_sysfs_attr_keys (udev_device);
	for (guint i = 0; keys[i] != NULL; i++) {
		g_debug ("%s=[%s]", keys[i],
			 g_udev_device_get_sysfs_attr (udev_device, keys[i]));
	}
}

void
fu_udev_device_dump (FuUdevDevice *self)
{
	FuUdevDevicePrivate *priv = GET_PRIVATE (self);
	if (priv->udev_device == NULL)
		return;
	fu_udev_device_dump_internal (priv->udev_device);
}

static gboolean
fu_udev_device_probe (FuDevice *device, GError **error)
{
	FuUdevDeviceClass *klass = FU_UDEV_DEVICE_GET_CLASS (device);
	FuUdevDevice *self = FU_UDEV_DEVICE (device);
	FuUdevDevicePrivate *priv = GET_PRIVATE (self);
	const gchar *tmp;
	g_autofree gchar *subsystem = NULL;
	g_autoptr(GUdevDevice) udev_parent = NULL;
	g_autoptr(GUdevDevice) parent_i2c = NULL;

	/* nothing to do */
	if (priv->udev_device == NULL)
		return TRUE;

	/* set ven:dev:rev */
	priv->vendor = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "vendor");
	priv->model = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "device");
	priv->revision = fu_udev_device_get_sysfs_attr_as_uint64 (priv->udev_device, "revision");

	/* fallback to the parent */
	udev_parent = g_udev_device_get_parent (priv->udev_device);
	if (udev_parent != NULL &&
	    priv->vendor == 0x0 && priv->model == 0x0 && priv->revision == 0x0) {
		priv->vendor = fu_udev_device_get_sysfs_attr_as_uint64 (udev_parent, "vendor");
		priv->model = fu_udev_device_get_sysfs_attr_as_uint64 (udev_parent, "device");
		priv->revision = fu_udev_device_get_sysfs_attr_as_uint64 (udev_parent, "revision");
	}

	/* hidraw helpfully encodes the information in a different place */
	if (udev_parent != NULL &&
	    priv->vendor == 0x0 && priv->model == 0x0 && priv->revision == 0x0 &&
	    g_strcmp0 (priv->subsystem, "hidraw") == 0) {
		tmp = g_udev_device_get_property (udev_parent, "HID_ID");
		if (tmp != NULL && strlen (tmp) == 22) {
			priv->vendor = fu_udev_device_read_uint16 (tmp + 9);
			priv->model = fu_udev_device_read_uint16 (tmp + 18);
		}
		tmp = g_udev_device_get_property (udev_parent, "HID_NAME");
		if (tmp != NULL) {
			if (fu_device_get_name (device) == NULL)
				fu_device_set_name (device, tmp);
		}
	}

	/* try harder to find a vendor name the user will recognise */
	if (udev_parent != NULL && fu_device_get_vendor (device) == NULL) {
		g_autoptr(GUdevDevice) device_tmp = g_object_ref (udev_parent);
		for (guint i = 0; i < 0xff; i++) {
			g_autoptr(GUdevDevice) parent = NULL;
			const gchar *id_vendor;
			id_vendor = g_udev_device_get_property (device_tmp,
								"ID_VENDOR_FROM_DATABASE");
			if (id_vendor != NULL) {
				fu_device_set_vendor (device, id_vendor);
				break;
			}
			parent = g_udev_device_get_parent (device_tmp);
			if (parent == NULL)
				break;
			g_set_object (&device_tmp, parent);
		}
	}

	/* set the version if the revision has been set */
{'fraction_non_alphanumeric': 0.13027245206861757, 'fraction_numerical': 0.01725529767911201, 'mean_word_length': 4.826572604350382, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5246539', 'n_tokens_mistral': 4311, 'n_tokens_neox': 4015, 'n_words': 822}