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

#define G_LOG_DOMAIN				"FuDevice"

#include "config.h"

#include <string.h>
#include <glib-object.h>
#include <gio/gio.h>

#include "fu-common.h"
#include "fu-common-version.h"
#include "fu-device-private.h"
#include "fu-mutex.h"

#include "fwupd-common.h"
#include "fwupd-device-private.h"

/**
 * SECTION:fu-device
 * @short_description: a physical or logical device
 *
 * An object that represents a physical or logical device.
 *
 * See also: #FuDeviceLocker
 */

static void fu_device_finalize			 (GObject *object);

typedef struct {
	gchar				*alternate_id;
	gchar				*equivalent_id;
	gchar				*physical_id;
	gchar				*logical_id;
	FuDevice			*alternate;
	FuDevice			*parent;	/* noref */
	FuQuirks			*quirks;
	GHashTable			*metadata;
	GRWLock				 metadata_mutex;
	GPtrArray			*parent_guids;
	GRWLock				 parent_guids_mutex;
	GPtrArray			*children;
	guint				 remove_delay;	/* ms */
	FwupdStatus			 status;
	guint				 progress;
	guint				 order;
	guint				 priority;
	guint				 poll_id;
	gboolean			 done_probe;
	gboolean			 done_setup;
	guint64				 size_min;
	guint64				 size_max;
	gint				 open_refcount;	/* atomic */
	GType				 specialized_gtype;
	GPtrArray			*possible_plugins;
} FuDevicePrivate;

enum {
	PROP_0,
	PROP_STATUS,
	PROP_PROGRESS,
	PROP_PHYSICAL_ID,
	PROP_LOGICAL_ID,
	PROP_QUIRKS,
	PROP_PARENT,
	PROP_LAST
};

G_DEFINE_TYPE_WITH_PRIVATE (FuDevice, fu_device, FWUPD_TYPE_DEVICE)
#define GET_PRIVATE(o) (fu_device_get_instance_private (o))

static void
fu_device_get_property (GObject *object, guint prop_id,
			GValue *value, GParamSpec *pspec)
{
	FuDevice *self = FU_DEVICE (object);
	FuDevicePrivate *priv = GET_PRIVATE (self);
	switch (prop_id) {
	case PROP_STATUS:
		g_value_set_uint (value, priv->status);
		break;
	case PROP_PROGRESS:
		g_value_set_uint (value, priv->progress);
		break;
	case PROP_PHYSICAL_ID:
		g_value_set_string (value, priv->physical_id);
		break;
	case PROP_LOGICAL_ID:
		g_value_set_string (value, priv->logical_id);
		break;
	case PROP_QUIRKS:
		g_value_set_object (value, priv->quirks);
		break;
	case PROP_PARENT:
		g_value_set_object (value, priv->parent);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
fu_device_set_property (GObject *object, guint prop_id,
			const GValue *value, GParamSpec *pspec)
{
	FuDevice *self = FU_DEVICE (object);
	switch (prop_id) {
	case PROP_STATUS:
		fu_device_set_status (self, g_value_get_uint (value));
		break;
	case PROP_PROGRESS:
		fu_device_set_progress (self, g_value_get_uint (value));
		break;
	case PROP_PHYSICAL_ID:
		fu_device_set_physical_id (self, g_value_get_string (value));
		break;
	case PROP_LOGICAL_ID:
		fu_device_set_logical_id (self, g_value_get_string (value));
		break;
	case PROP_QUIRKS:
		fu_device_set_quirks (self, g_value_get_object (value));
		break;
	case PROP_PARENT:
		fu_device_set_parent (self, g_value_get_object (value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

/**
 * fu_device_get_possible_plugins:
 * @self: A #FuDevice
 *
 * Gets the list of possible plugin names, typically added from quirk files.
 *
 * Returns: (element-type utf8) (transfer container): plugin names
 *
 * Since: 1.3.3
 **/
GPtrArray *
fu_device_get_possible_plugins (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	return g_ptr_array_ref (priv->possible_plugins);
}

/**
 * fu_device_add_possible_plugin:
 * @self: A #FuDevice
 * @plugin: A plugin name, e.g. `dfu`
 *
 * Adds a plugin name to the list of plugins that *might* be able to handle this
 * device. This is tyically called from a quirk handler.
 *
 * Since: 1.3.3
 **/
static void
fu_device_add_possible_plugin (FuDevice *self, const gchar *plugin)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_ptr_array_add (priv->possible_plugins, g_strdup (plugin));
}

/**
 * fu_device_poll:
 * @self: A #FuDevice
 * @error: A #GError, or %NULL
 *
 * Polls a device, typically querying the hardware for status.
 *
 * Returns: %TRUE for success
 *
 * Since: 1.1.2
 **/
gboolean
fu_device_poll (FuDevice *self, GError **error)
{
	FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self);

	g_return_val_if_fail (FU_IS_DEVICE (self), FALSE);
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	/* subclassed */
	if (klass->poll != NULL) {
		if (!klass->poll (self, error))
			return FALSE;
	}
	return TRUE;
}

static gboolean
fu_device_poll_cb (gpointer user_data)
{
	FuDevice *self = FU_DEVICE (user_data);
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_autoptr(GError) error_local = NULL;
	if (!fu_device_poll (self, &error_local)) {
		g_warning ("disabling polling: %s", error_local->message);
		priv->poll_id = 0;
		return G_SOURCE_REMOVE;
	}
	return G_SOURCE_CONTINUE;
}

/**
 * fu_device_set_poll_interval:
 * @self: a #FuPlugin
 * @interval: duration in ms, or 0 to disable
 *
 * Polls the hardware every interval period. If the subclassed `->poll()` method
 * returns %FALSE then a warning is printed to the console and the poll is
 * disabled until the next call to fu_device_set_poll_interval().
 *
 * Since: 1.1.2
 **/
void
fu_device_set_poll_interval (FuDevice *self, guint interval)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);

	g_return_if_fail (FU_IS_DEVICE (self));

	if (priv->poll_id != 0) {
		g_source_remove (priv->poll_id);
		priv->poll_id = 0;
	}
	if (interval == 0)
		return;
	if (interval % 1000 == 0) {
		priv->poll_id = g_timeout_add_seconds (interval / 1000,
						       fu_device_poll_cb,
						       self);
	} else {
		priv->poll_id = g_timeout_add (interval, fu_device_poll_cb, self);
	}
}

/**
 * fu_device_get_order:
 * @self: a #FuPlugin
 *
 * Gets the device order, where higher numbers are installed after lower
 * numbers.
 *
 * Returns: the integer value
 *
 * Since: 1.0.8
 **/
guint
fu_device_get_order (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), 0);
	return priv->order;
}

/**
 * fu_device_set_order:
 * @self: a #FuDevice
 * @order: a integer value
 *
 * Sets the device order, where higher numbers are installed after lower
 * numbers.
 *
 * Since: 1.0.8
 **/
void
fu_device_set_order (FuDevice *self, guint order)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_if_fail (FU_IS_DEVICE (self));
	priv->order = order;
}

/**
 * fu_device_get_priority:
 * @self: a #FuPlugin
 *
 * Gets the device priority, where higher numbers are better.
 *
 * Returns: the integer value
 *
 * Since: 1.1.1
 **/
guint
fu_device_get_priority (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), 0);
	return priv->priority;
}

/**
 * fu_device_set_priority:
 * @self: a #FuDevice
 * @priority: a integer value
 *
 * Sets the device priority, where higher numbers are better.
 *
 * Since: 1.1.1
 **/
void
fu_device_set_priority (FuDevice *self, guint priority)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_if_fail (FU_IS_DEVICE (self));
	priv->priority = priority;
}

/**
 * fu_device_get_equivalent_id:
 * @self: A #FuDevice
 *
 * Gets any equivalent ID for a device
 *
 * Returns: (transfer none): a #gchar or NULL
 *
 * Since: 0.6.1
 **/
const gchar *
fu_device_get_equivalent_id (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), NULL);
	return priv->equivalent_id;
}

/**
 * fu_device_set_equivalent_id:
 * @self: A #FuDevice
 * @equivalent_id: A string
 *
 * Sets any equivalent ID for a device
 *
 * Since: 0.6.1
 **/
void
fu_device_set_equivalent_id (FuDevice *self, const gchar *equivalent_id)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_if_fail (FU_IS_DEVICE (self));
	g_free (priv->equivalent_id);
	priv->equivalent_id = g_strdup (equivalent_id);
}

/**
 * fu_device_get_alternate_id:
 * @self: A #FuDevice
 *
 * Gets any alternate device ID. An alternate device may be linked to the primary
 * device in some way.
 *
 * Returns: (transfer none): a #FuDevice or %NULL
 *
 * Since: 1.1.0
 **/
const gchar *
fu_device_get_alternate_id (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), NULL);
	return priv->alternate_id;
}

/**
 * fu_device_set_alternate_id:
 * @self: A #FuDevice
 * @alternate_id: Another #FuDevice
 *
 * Sets any alternate device ID. An alternate device may be linked to the primary
 * device in some way.
 *
 * Since: 1.1.0
 **/
void
fu_device_set_alternate_id (FuDevice *self, const gchar *alternate_id)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_if_fail (FU_IS_DEVICE (self));
	g_free (priv->alternate_id);
	priv->alternate_id = g_strdup (alternate_id);
}

/**
 * fu_device_get_alternate:
 * @self: A #FuDevice
 *
 * Gets any alternate device. An alternate device may be linked to the primary
 * device in some way.
 *
 * The alternate object will be matched from the ID set in fu_device_set_alternate_id()
 * and will be assigned by the daemon. This means if the ID is not found as an
 * added device, then this function will return %NULL.
 *
 * Returns: (transfer none): a #FuDevice or %NULL
 *
 * Since: 0.7.2
 **/
FuDevice *
fu_device_get_alternate (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), NULL);
	return priv->alternate;
}

/**
 * fu_device_set_alternate:
 * @self: A #FuDevice
 * @alternate: Another #FuDevice
 *
 * Sets any alternate device. An alternate device may be linked to the primary
 * device in some way.
 *
 * This function is only usable by the daemon, not directly from plugins.
 *
 * Since: 0.7.2
 **/
void
fu_device_set_alternate (FuDevice *self, FuDevice *alternate)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_if_fail (FU_IS_DEVICE (self));
	g_set_object (&priv->alternate, alternate);
}

/**
 * fu_device_get_parent:
 * @self: A #FuDevice
 *
 * Gets any parent device. An parent device is logically "above" the current
 * device and this may be reflected in client tools.
 *
 * This information also allows the plugin to optionally verify the parent
 * device, for instance checking the parent device firmware version.
 *
 * The parent object is not refcounted and if destroyed this function will then
 * return %NULL.
 *
 * Returns: (transfer none): a #FuDevice or %NULL
 *
 * Since: 1.0.8
 **/
FuDevice *
fu_device_get_parent (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), NULL);
	return priv->parent;
}

/**
 * fu_device_get_root:
 * @self: A #FuDevice
 *
 * Gets the root parent device. A parent device is logically "above" the current
 * device and this may be reflected in client tools.
 *
 * If there is no parent device defined, then @self is returned.
 *
 * Returns: (transfer full): a #FuDevice
 *
 * Since: 1.4.0
 **/
FuDevice *
fu_device_get_root (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), NULL);
	while (priv->parent != NULL) {
		self = priv->parent;
		priv = GET_PRIVATE (self);
	}
	return g_object_ref (self);
}

/**
 * fu_device_set_parent:
 * @self: A #FuDevice
 * @parent: A #FuDevice
 *
 * Sets any parent device. An parent device is logically "above" the current
 * device and this may be reflected in client tools.
 *
 * This information also allows the plugin to optionally verify the parent
 * device, for instance checking the parent device firmware version.
 *
 * Since: 1.0.8
 **/
void
fu_device_set_parent (FuDevice *self, FuDevice *parent)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);

	g_return_if_fail (FU_IS_DEVICE (self));

	if (priv->parent != NULL)
		g_object_remove_weak_pointer (G_OBJECT (priv->parent), (gpointer *) &priv->parent);
	if (parent != NULL)
		g_object_add_weak_pointer (G_OBJECT (parent), (gpointer *) &priv->parent);
	priv->parent = parent;

	/* this is what goes over D-Bus */
	fwupd_device_set_parent_id (FWUPD_DEVICE (self),
				    parent != NULL ? fu_device_get_id (parent) : NULL);
}

/**
 * fu_device_get_children:
 * @self: A #FuDevice
 *
 * Gets any child devices. A child device is logically "below" the current
 * device and this may be reflected in client tools.
 *
 * Returns: (transfer none) (element-type FuDevice): child devices
 *
 * Since: 1.0.8
 **/
GPtrArray *
fu_device_get_children (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), NULL);
	return priv->children;
}

/**
 * fu_device_add_child:
 * @self: A #FuDevice
 * @child: Another #FuDevice
 *
 * Sets any child device. An child device is logically linked to the primary
 * device in some way.
 *
 * Since: 1.0.8
 **/
void
fu_device_add_child (FuDevice *self, FuDevice *child)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_if_fail (FU_IS_DEVICE (self));
	g_return_if_fail (FU_IS_DEVICE (child));

	/* add if the child does not already exist */
	for (guint i = 0; i < priv->children->len; i++) {
		FuDevice *devtmp = g_ptr_array_index (priv->children, i);
		if (devtmp == child)
			return;
	}
	g_ptr_array_add (priv->children, g_object_ref (child));

	/* copy from main device if unset */
	if (fu_device_get_physical_id (child) == NULL &&
	    fu_device_get_physical_id (self) != NULL)
		fu_device_set_physical_id (child, fu_device_get_physical_id (self));
	if (fu_device_get_vendor (child) == NULL)
		fu_device_set_vendor (child, fu_device_get_vendor (self));
	if (fu_device_get_vendor_id (child) == NULL)
		fu_device_set_vendor_id (child, fu_device_get_vendor_id (self));
	if (fu_device_get_icons(child)->len == 0) {
		GPtrArray *icons = fu_device_get_icons (self);
		for (guint i = 0; i < icons->len; i++) {
			const gchar *icon_name = g_ptr_array_index (icons, i);
			fu_device_add_icon (child, icon_name);
		}
	}

	/* ensure the parent is also set on the child */
	fu_device_set_parent (child, self);

	/* order devices so they are updated in the correct sequence */
	if (fu_device_has_flag (child, FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST)) {
		if (priv->order >= fu_device_get_order (child))
			fu_device_set_order (child, priv->order + 1);
	} else {
		if (priv->order <= fu_device_get_order (child))
			priv->order = fu_device_get_order (child) + 1;
	}
}

/**
 * fu_device_get_parent_guids:
 * @self: A #FuDevice
 *
 * Gets any parent device GUIDs. If a device is added to the daemon that matches
 * any GUIDs added from fu_device_add_parent_guid() then this device is marked the parent of @self.
 *
 * Returns: (transfer none) (element-type utf8): a list of GUIDs<commit_msg>Ensure that the DeviceID is set for child devices<commit_after>/*
 * Copyright (C) 2015-2018 <NAME> <<EMAIL>>
 *
 * SPDX-License-Identifier: LGPL-2.1+
 */

#define G_LOG_DOMAIN				"FuDevice"

#include "config.h"

#include <string.h>
#include <glib-object.h>
#include <gio/gio.h>

#include "fu-common.h"
#include "fu-common-version.h"
#include "fu-device-private.h"
#include "fu-mutex.h"

#include "fwupd-common.h"
#include "fwupd-device-private.h"

/**
 * SECTION:fu-device
 * @short_description: a physical or logical device
 *
 * An object that represents a physical or logical device.
 *
 * See also: #FuDeviceLocker
 */

static void fu_device_finalize			 (GObject *object);

typedef struct {
	gchar				*alternate_id;
	gchar				*equivalent_id;
	gchar				*physical_id;
	gchar				*logical_id;
	FuDevice			*alternate;
	FuDevice			*parent;	/* noref */
	FuQuirks			*quirks;
	GHashTable			*metadata;
	GRWLock				 metadata_mutex;
	GPtrArray			*parent_guids;
	GRWLock				 parent_guids_mutex;
	GPtrArray			*children;
	guint				 remove_delay;	/* ms */
	FwupdStatus			 status;
	guint				 progress;
	guint				 order;
	guint				 priority;
	guint				 poll_id;
	gboolean			 done_probe;
	gboolean			 done_setup;
	guint64				 size_min;
	guint64				 size_max;
	gint				 open_refcount;	/* atomic */
	GType				 specialized_gtype;
	GPtrArray			*possible_plugins;
} FuDevicePrivate;

enum {
	PROP_0,
	PROP_STATUS,
	PROP_PROGRESS,
	PROP_PHYSICAL_ID,
	PROP_LOGICAL_ID,
	PROP_QUIRKS,
	PROP_PARENT,
	PROP_LAST
};

G_DEFINE_TYPE_WITH_PRIVATE (FuDevice, fu_device, FWUPD_TYPE_DEVICE)
#define GET_PRIVATE(o) (fu_device_get_instance_private (o))

static void
fu_device_get_property (GObject *object, guint prop_id,
			GValue *value, GParamSpec *pspec)
{
	FuDevice *self = FU_DEVICE (object);
	FuDevicePrivate *priv = GET_PRIVATE (self);
	switch (prop_id) {
	case PROP_STATUS:
		g_value_set_uint (value, priv->status);
		break;
	case PROP_PROGRESS:
		g_value_set_uint (value, priv->progress);
		break;
	case PROP_PHYSICAL_ID:
		g_value_set_string (value, priv->physical_id);
		break;
	case PROP_LOGICAL_ID:
		g_value_set_string (value, priv->logical_id);
		break;
	case PROP_QUIRKS:
		g_value_set_object (value, priv->quirks);
		break;
	case PROP_PARENT:
		g_value_set_object (value, priv->parent);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
fu_device_set_property (GObject *object, guint prop_id,
			const GValue *value, GParamSpec *pspec)
{
	FuDevice *self = FU_DEVICE (object);
	switch (prop_id) {
	case PROP_STATUS:
		fu_device_set_status (self, g_value_get_uint (value));
		break;
	case PROP_PROGRESS:
		fu_device_set_progress (self, g_value_get_uint (value));
		break;
	case PROP_PHYSICAL_ID:
		fu_device_set_physical_id (self, g_value_get_string (value));
		break;
	case PROP_LOGICAL_ID:
		fu_device_set_logical_id (self, g_value_get_string (value));
		break;
	case PROP_QUIRKS:
		fu_device_set_quirks (self, g_value_get_object (value));
		break;
	case PROP_PARENT:
		fu_device_set_parent (self, g_value_get_object (value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

/**
 * fu_device_get_possible_plugins:
 * @self: A #FuDevice
 *
 * Gets the list of possible plugin names, typically added from quirk files.
 *
 * Returns: (element-type utf8) (transfer container): plugin names
 *
 * Since: 1.3.3
 **/
GPtrArray *
fu_device_get_possible_plugins (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	return g_ptr_array_ref (priv->possible_plugins);
}

/**
 * fu_device_add_possible_plugin:
 * @self: A #FuDevice
 * @plugin: A plugin name, e.g. `dfu`
 *
 * Adds a plugin name to the list of plugins that *might* be able to handle this
 * device. This is tyically called from a quirk handler.
 *
 * Since: 1.3.3
 **/
static void
fu_device_add_possible_plugin (FuDevice *self, const gchar *plugin)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_ptr_array_add (priv->possible_plugins, g_strdup (plugin));
}

/**
 * fu_device_poll:
 * @self: A #FuDevice
 * @error: A #GError, or %NULL
 *
 * Polls a device, typically querying the hardware for status.
 *
 * Returns: %TRUE for success
 *
 * Since: 1.1.2
 **/
gboolean
fu_device_poll (FuDevice *self, GError **error)
{
	FuDeviceClass *klass = FU_DEVICE_GET_CLASS (self);

	g_return_val_if_fail (FU_IS_DEVICE (self), FALSE);
	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

	/* subclassed */
	if (klass->poll != NULL) {
		if (!klass->poll (self, error))
			return FALSE;
	}
	return TRUE;
}

static gboolean
fu_device_poll_cb (gpointer user_data)
{
	FuDevice *self = FU_DEVICE (user_data);
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_autoptr(GError) error_local = NULL;
	if (!fu_device_poll (self, &error_local)) {
		g_warning ("disabling polling: %s", error_local->message);
		priv->poll_id = 0;
		return G_SOURCE_REMOVE;
	}
	return G_SOURCE_CONTINUE;
}

/**
 * fu_device_set_poll_interval:
 * @self: a #FuPlugin
 * @interval: duration in ms, or 0 to disable
 *
 * Polls the hardware every interval period. If the subclassed `->poll()` method
 * returns %FALSE then a warning is printed to the console and the poll is
 * disabled until the next call to fu_device_set_poll_interval().
 *
 * Since: 1.1.2
 **/
void
fu_device_set_poll_interval (FuDevice *self, guint interval)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);

	g_return_if_fail (FU_IS_DEVICE (self));

	if (priv->poll_id != 0) {
		g_source_remove (priv->poll_id);
		priv->poll_id = 0;
	}
	if (interval == 0)
		return;
	if (interval % 1000 == 0) {
		priv->poll_id = g_timeout_add_seconds (interval / 1000,
						       fu_device_poll_cb,
						       self);
	} else {
		priv->poll_id = g_timeout_add (interval, fu_device_poll_cb, self);
	}
}

/**
 * fu_device_get_order:
 * @self: a #FuPlugin
 *
 * Gets the device order, where higher numbers are installed after lower
 * numbers.
 *
 * Returns: the integer value
 *
 * Since: 1.0.8
 **/
guint
fu_device_get_order (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), 0);
	return priv->order;
}

/**
 * fu_device_set_order:
 * @self: a #FuDevice
 * @order: a integer value
 *
 * Sets the device order, where higher numbers are installed after lower
 * numbers.
 *
 * Since: 1.0.8
 **/
void
fu_device_set_order (FuDevice *self, guint order)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_if_fail (FU_IS_DEVICE (self));
	priv->order = order;
}

/**
 * fu_device_get_priority:
 * @self: a #FuPlugin
 *
 * Gets the device priority, where higher numbers are better.
 *
 * Returns: the integer value
 *
 * Since: 1.1.1
 **/
guint
fu_device_get_priority (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), 0);
	return priv->priority;
}

/**
 * fu_device_set_priority:
 * @self: a #FuDevice
 * @priority: a integer value
 *
 * Sets the device priority, where higher numbers are better.
 *
 * Since: 1.1.1
 **/
void
fu_device_set_priority (FuDevice *self, guint priority)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_if_fail (FU_IS_DEVICE (self));
	priv->priority = priority;
}

/**
 * fu_device_get_equivalent_id:
 * @self: A #FuDevice
 *
 * Gets any equivalent ID for a device
 *
 * Returns: (transfer none): a #gchar or NULL
 *
 * Since: 0.6.1
 **/
const gchar *
fu_device_get_equivalent_id (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), NULL);
	return priv->equivalent_id;
}

/**
 * fu_device_set_equivalent_id:
 * @self: A #FuDevice
 * @equivalent_id: A string
 *
 * Sets any equivalent ID for a device
 *
 * Since: 0.6.1
 **/
void
fu_device_set_equivalent_id (FuDevice *self, const gchar *equivalent_id)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_if_fail (FU_IS_DEVICE (self));
	g_free (priv->equivalent_id);
	priv->equivalent_id = g_strdup (equivalent_id);
}

/**
 * fu_device_get_alternate_id:
 * @self: A #FuDevice
 *
 * Gets any alternate device ID. An alternate device may be linked to the primary
 * device in some way.
 *
 * Returns: (transfer none): a #FuDevice or %NULL
 *
 * Since: 1.1.0
 **/
const gchar *
fu_device_get_alternate_id (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), NULL);
	return priv->alternate_id;
}

/**
 * fu_device_set_alternate_id:
 * @self: A #FuDevice
 * @alternate_id: Another #FuDevice
 *
 * Sets any alternate device ID. An alternate device may be linked to the primary
 * device in some way.
 *
 * Since: 1.1.0
 **/
void
fu_device_set_alternate_id (FuDevice *self, const gchar *alternate_id)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_if_fail (FU_IS_DEVICE (self));
	g_free (priv->alternate_id);
	priv->alternate_id = g_strdup (alternate_id);
}

/**
 * fu_device_get_alternate:
 * @self: A #FuDevice
 *
 * Gets any alternate device. An alternate device may be linked to the primary
 * device in some way.
 *
 * The alternate object will be matched from the ID set in fu_device_set_alternate_id()
 * and will be assigned by the daemon. This means if the ID is not found as an
 * added device, then this function will return %NULL.
 *
 * Returns: (transfer none): a #FuDevice or %NULL
 *
 * Since: 0.7.2
 **/
FuDevice *
fu_device_get_alternate (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), NULL);
	return priv->alternate;
}

/**
 * fu_device_set_alternate:
 * @self: A #FuDevice
 * @alternate: Another #FuDevice
 *
 * Sets any alternate device. An alternate device may be linked to the primary
 * device in some way.
 *
 * This function is only usable by the daemon, not directly from plugins.
 *
 * Since: 0.7.2
 **/
void
fu_device_set_alternate (FuDevice *self, FuDevice *alternate)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_if_fail (FU_IS_DEVICE (self));
	g_set_object (&priv->alternate, alternate);
}

/**
 * fu_device_get_parent:
 * @self: A #FuDevice
 *
 * Gets any parent device. An parent device is logically "above" the current
 * device and this may be reflected in client tools.
 *
 * This information also allows the plugin to optionally verify the parent
 * device, for instance checking the parent device firmware version.
 *
 * The parent object is not refcounted and if destroyed this function will then
 * return %NULL.
 *
 * Returns: (transfer none): a #FuDevice or %NULL
 *
 * Since: 1.0.8
 **/
FuDevice *
fu_device_get_parent (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), NULL);
	return priv->parent;
}

/**
 * fu_device_get_root:
 * @self: A #FuDevice
 *
 * Gets the root parent device. A parent device is logically "above" the current
 * device and this may be reflected in client tools.
 *
 * If there is no parent device defined, then @self is returned.
 *
 * Returns: (transfer full): a #FuDevice
 *
 * Since: 1.4.0
 **/
FuDevice *
fu_device_get_root (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), NULL);
	while (priv->parent != NULL) {
		self = priv->parent;
		priv = GET_PRIVATE (self);
	}
	return g_object_ref (self);
}

/**
 * fu_device_set_parent:
 * @self: A #FuDevice
 * @parent: A #FuDevice
 *
 * Sets any parent device. An parent device is logically "above" the current
 * device and this may be reflected in client tools.
 *
 * This information also allows the plugin to optionally verify the parent
 * device, for instance checking the parent device firmware version.
 *
 * Since: 1.0.8
 **/
void
fu_device_set_parent (FuDevice *self, FuDevice *parent)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);

	g_return_if_fail (FU_IS_DEVICE (self));

	if (priv->parent != NULL)
		g_object_remove_weak_pointer (G_OBJECT (priv->parent), (gpointer *) &priv->parent);
	if (parent != NULL)
		g_object_add_weak_pointer (G_OBJECT (parent), (gpointer *) &priv->parent);
	priv->parent = parent;

	/* this is what goes over D-Bus */
	fwupd_device_set_parent_id (FWUPD_DEVICE (self),
				    parent != NULL ? fu_device_get_id (parent) : NULL);
}

/**
 * fu_device_get_children:
 * @self: A #FuDevice
 *
 * Gets any child devices. A child device is logically "below" the current
 * device and this may be reflected in client tools.
 *
 * Returns: (transfer none) (element-type FuDevice): child devices
 *
 * Since: 1.0.8
 **/
GPtrArray *
fu_device_get_children (FuDevice *self)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_val_if_fail (FU_IS_DEVICE (self), NULL);
	return priv->children;
}

/**
 * fu_device_add_child:
 * @self: A #FuDevice
 * @child: Another #FuDevice
 *
 * Sets any child device. An child device is logically linked to the primary
 * device in some way.
 *
 * Since: 1.0.8
 **/
void
fu_device_add_child (FuDevice *self, FuDevice *child)
{
	FuDevicePrivate *priv = GET_PRIVATE (self);
	g_return_if_fail (FU_IS_DEVICE (self));
	g_return_if_fail (FU_IS_DEVICE (child));

	/* add if the child does not already exist */
	for (guint i = 0; i < priv->children->len; i++) {
		FuDevice *devtmp = g_ptr_array_index (priv->children, i);
		if (devtmp == child)
			return;
	}
	g_ptr_array_add (priv->children, g_object_ref (child));

	/* copy from main device if unset */
	if (fu_device_get_physical_id (child) == NULL &&
	    fu_device_get_physical_id (self) != NULL)
		fu_device_set_physical_id (child, fu_device_get_physical_id (self));
	if (fu_device_get_vendor (child) == NULL)
		fu_device_set_vendor (child, fu_device_get_vendor (self));
	if (fu_device_get_vendor_id (child) == NULL)
		fu_device_set_vendor_id (child, fu_device_get_vendor_id (self));
	if (fu_device_get_icons(child)->len == 0) {
		GPtrArray *icons = fu_device_get_icons (self);
		for (guint i = 0; i < icons->len; i++) {
			const gchar *icon_name = g_ptr_array_index (icons, i);
			fu_device_add_icon (child, icon_name);
		}
	}

	/* ensure the ID is converted */
	fu_device_ensure_id (child, NULL);

	/* ensure the parent is also set on the child */
	fu_device_set_parent (child, self);

	/* order devices so they are updated in the correct sequence */
	if (fu_device_has_flag (child, FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST)) {
		if (priv->order >= fu_device_get_order (child))
			fu_device_set_order (child, priv->order + 1);
	} else {
		if (priv->order <= fu_device_get_order (child))
			priv->order = fu_device_get_order (child) + 1;
	}
}

/**
 * fu_device_get_parent_guids:
 * @self: A #FuDevice
 *
 * Gets any parent device GUIDs. If a device is added to the daemon that matches
 * any GUIDs added from fu_device_add_parent_guid() then this device is marked the parent of @self.
 *
 * Returns: (transfer none) (element-type utf8): a list of GUIDs
{'fraction_non_alphanumeric': 0.11459856496752474, 'fraction_numerical': 0.006461046689563709, 'mean_word_length': 4.3488541287741, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 115, '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': '5276327', 'n_tokens_mistral': 11317, 'n_tokens_neox': 10610, 'n_words': 2935}