mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-02 05:47:46 +00:00
397 lines
9.3 KiB
C
397 lines
9.3 KiB
C
/*
|
|
* Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#define G_LOG_DOMAIN "FuBackend"
|
|
|
|
#include "config.h"
|
|
|
|
#include "fu-backend.h"
|
|
|
|
/**
|
|
* FuBackend:
|
|
*
|
|
* An device discovery backend, for instance USB, BlueZ or UDev.
|
|
*
|
|
* See also: [class@FuDevice]
|
|
*/
|
|
|
|
typedef struct {
|
|
FuContext *ctx;
|
|
gchar *name;
|
|
gboolean enabled;
|
|
gboolean done_setup;
|
|
GHashTable *devices; /* device_id : * FuDevice */
|
|
} FuBackendPrivate;
|
|
|
|
enum { SIGNAL_ADDED, SIGNAL_REMOVED, SIGNAL_CHANGED, SIGNAL_LAST };
|
|
|
|
enum { PROP_0, PROP_NAME, PROP_CONTEXT, PROP_LAST };
|
|
|
|
static guint signals[SIGNAL_LAST] = {0};
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE(FuBackend, fu_backend, G_TYPE_OBJECT)
|
|
#define GET_PRIVATE(o) (fu_backend_get_instance_private(o))
|
|
|
|
/**
|
|
* fu_backend_device_added:
|
|
* @self: a #FuBackend
|
|
* @device: a device
|
|
*
|
|
* Emits a signal that indicates the device has been added.
|
|
*
|
|
* Since: 1.6.1
|
|
**/
|
|
void
|
|
fu_backend_device_added(FuBackend *self, FuDevice *device)
|
|
{
|
|
FuBackendPrivate *priv = GET_PRIVATE(self);
|
|
g_return_if_fail(FU_IS_BACKEND(self));
|
|
g_return_if_fail(FU_IS_DEVICE(device));
|
|
|
|
/* assign context if unset */
|
|
if (fu_device_get_context(device) == NULL)
|
|
fu_device_set_context(device, priv->ctx);
|
|
|
|
/* add */
|
|
g_hash_table_insert(priv->devices,
|
|
g_strdup(fu_device_get_backend_id(device)),
|
|
g_object_ref(device));
|
|
g_signal_emit(self, signals[SIGNAL_ADDED], 0, device);
|
|
}
|
|
|
|
/**
|
|
* fu_backend_device_removed:
|
|
* @self: a #FuBackend
|
|
* @device: a device
|
|
*
|
|
* Emits a signal that indicates the device has been removed.
|
|
*
|
|
* Since: 1.6.1
|
|
**/
|
|
void
|
|
fu_backend_device_removed(FuBackend *self, FuDevice *device)
|
|
{
|
|
FuBackendPrivate *priv = GET_PRIVATE(self);
|
|
g_return_if_fail(FU_IS_BACKEND(self));
|
|
g_return_if_fail(FU_IS_DEVICE(device));
|
|
g_signal_emit(self, signals[SIGNAL_REMOVED], 0, device);
|
|
g_hash_table_remove(priv->devices, fu_device_get_backend_id(device));
|
|
}
|
|
|
|
/**
|
|
* fu_backend_device_changed:
|
|
* @self: a #FuBackend
|
|
* @device: a device
|
|
*
|
|
* Emits a signal that indicates the device has been changed.
|
|
*
|
|
* Since: 1.6.1
|
|
**/
|
|
void
|
|
fu_backend_device_changed(FuBackend *self, FuDevice *device)
|
|
{
|
|
g_return_if_fail(FU_IS_BACKEND(self));
|
|
g_return_if_fail(FU_IS_DEVICE(device));
|
|
g_signal_emit(self, signals[SIGNAL_CHANGED], 0, device);
|
|
}
|
|
|
|
/**
|
|
* fu_backend_setup:
|
|
* @self: a #FuBackend
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Sets up the backend ready for use, which typically calls the subclassed setup
|
|
* function. No devices should be added or removed at this point.
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.6.1
|
|
**/
|
|
gboolean
|
|
fu_backend_setup(FuBackend *self, GError **error)
|
|
{
|
|
FuBackendClass *klass = FU_BACKEND_GET_CLASS(self);
|
|
FuBackendPrivate *priv = GET_PRIVATE(self);
|
|
|
|
g_return_val_if_fail(FU_IS_BACKEND(self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
|
|
if (priv->done_setup)
|
|
return TRUE;
|
|
if (klass->setup != NULL) {
|
|
if (!klass->setup(self, error)) {
|
|
priv->enabled = FALSE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
priv->done_setup = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* fu_backend_coldplug:
|
|
* @self: a #FuBackend
|
|
* @error: (nullable): optional return location for an error
|
|
*
|
|
* Adds devices using the subclassed backend. If fu_backend_setup() has not
|
|
* already been called then it is run before this function automatically.
|
|
*
|
|
* Returns: %TRUE for success
|
|
*
|
|
* Since: 1.6.1
|
|
**/
|
|
gboolean
|
|
fu_backend_coldplug(FuBackend *self, GError **error)
|
|
{
|
|
FuBackendClass *klass = FU_BACKEND_GET_CLASS(self);
|
|
g_return_val_if_fail(FU_IS_BACKEND(self), FALSE);
|
|
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
|
|
if (!fu_backend_setup(self, error))
|
|
return FALSE;
|
|
if (klass->coldplug == NULL)
|
|
return TRUE;
|
|
return klass->coldplug(self, error);
|
|
}
|
|
|
|
/**
|
|
* fu_backend_get_name:
|
|
* @self: a #FuBackend
|
|
*
|
|
* Return the name of the backend, which is normally set by the subclass.
|
|
*
|
|
* Returns: backend name
|
|
*
|
|
* Since: 1.6.1
|
|
**/
|
|
const gchar *
|
|
fu_backend_get_name(FuBackend *self)
|
|
{
|
|
FuBackendPrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FU_IS_BACKEND(self), NULL);
|
|
return priv->name;
|
|
}
|
|
|
|
/**
|
|
* fu_backend_get_context:
|
|
* @self: a #FuBackend
|
|
*
|
|
* Gets the context for a backend.
|
|
*
|
|
* Returns: (transfer none): a #FuContext or %NULL if not set
|
|
*
|
|
* Since: 1.6.1
|
|
**/
|
|
FuContext *
|
|
fu_backend_get_context(FuBackend *self)
|
|
{
|
|
FuBackendPrivate *priv = GET_PRIVATE(self);
|
|
return priv->ctx;
|
|
}
|
|
|
|
/**
|
|
* fu_backend_get_enabled:
|
|
* @self: a #FuBackend
|
|
*
|
|
* Return the boolean value of a key if it's been configured
|
|
*
|
|
* Returns: %TRUE if the backend is enabled
|
|
*
|
|
* Since: 1.6.1
|
|
**/
|
|
gboolean
|
|
fu_backend_get_enabled(FuBackend *self)
|
|
{
|
|
FuBackendPrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FU_IS_BACKEND(self), FALSE);
|
|
return priv->enabled;
|
|
}
|
|
|
|
/**
|
|
* fu_backend_set_enabled:
|
|
* @self: a #FuBackend
|
|
* @enabled: enabled state
|
|
*
|
|
* Sets the backend enabled state.
|
|
*
|
|
* Since: 1.6.1
|
|
**/
|
|
void
|
|
fu_backend_set_enabled(FuBackend *self, gboolean enabled)
|
|
{
|
|
FuBackendPrivate *priv = GET_PRIVATE(self);
|
|
g_return_if_fail(FU_IS_BACKEND(self));
|
|
priv->enabled = FALSE;
|
|
}
|
|
|
|
/**
|
|
* fu_backend_lookup_by_id:
|
|
* @self: a #FuBackend
|
|
* @device_id: a DeviceID
|
|
*
|
|
* Gets a device previously added by the backend.
|
|
*
|
|
* Returns: (transfer none): device, or %NULL if not found
|
|
*
|
|
* Since: 1.6.1
|
|
**/
|
|
FuDevice *
|
|
fu_backend_lookup_by_id(FuBackend *self, const gchar *device_id)
|
|
{
|
|
FuBackendPrivate *priv = GET_PRIVATE(self);
|
|
g_return_val_if_fail(FU_IS_BACKEND(self), NULL);
|
|
return g_hash_table_lookup(priv->devices, device_id);
|
|
}
|
|
|
|
static gint
|
|
fu_backend_get_devices_sort_cb(gconstpointer a, gconstpointer b)
|
|
{
|
|
FuDevice *deva = *((FuDevice **)a);
|
|
FuDevice *devb = *((FuDevice **)b);
|
|
return g_strcmp0(fu_device_get_backend_id(deva), fu_device_get_backend_id(devb));
|
|
}
|
|
|
|
/**
|
|
* fu_backend_get_devices:
|
|
* @self: a #FuBackend
|
|
*
|
|
* Gets all the devices added by the backend.
|
|
*
|
|
* Returns: (transfer container) (element-type FuDevice): devices
|
|
*
|
|
* Since: 1.6.1
|
|
**/
|
|
GPtrArray *
|
|
fu_backend_get_devices(FuBackend *self)
|
|
{
|
|
FuBackendPrivate *priv = GET_PRIVATE(self);
|
|
g_autoptr(GList) values = NULL;
|
|
g_autoptr(GPtrArray) devices = NULL;
|
|
|
|
g_return_val_if_fail(FU_IS_BACKEND(self), NULL);
|
|
|
|
devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
|
|
values = g_hash_table_get_values(priv->devices);
|
|
for (GList *l = values; l != NULL; l = l->next)
|
|
g_ptr_array_add(devices, g_object_ref(l->data));
|
|
g_ptr_array_sort(devices, fu_backend_get_devices_sort_cb);
|
|
return g_steal_pointer(&devices);
|
|
}
|
|
|
|
static void
|
|
fu_backend_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
|
|
{
|
|
FuBackend *self = FU_BACKEND(object);
|
|
FuBackendPrivate *priv = GET_PRIVATE(self);
|
|
switch (prop_id) {
|
|
case PROP_NAME:
|
|
g_value_set_string(value, priv->name);
|
|
break;
|
|
case PROP_CONTEXT:
|
|
g_value_set_object(value, priv->ctx);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fu_backend_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
|
|
{
|
|
FuBackend *self = FU_BACKEND(object);
|
|
FuBackendPrivate *priv = GET_PRIVATE(self);
|
|
switch (prop_id) {
|
|
case PROP_NAME:
|
|
priv->name = g_value_dup_string(value);
|
|
break;
|
|
case PROP_CONTEXT:
|
|
g_set_object(&priv->ctx, g_value_get_object(value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fu_backend_init(FuBackend *self)
|
|
{
|
|
FuBackendPrivate *priv = GET_PRIVATE(self);
|
|
priv->enabled = TRUE;
|
|
priv->devices =
|
|
g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_object_unref);
|
|
}
|
|
|
|
static void
|
|
fu_backend_finalize(GObject *object)
|
|
{
|
|
FuBackend *self = FU_BACKEND(object);
|
|
FuBackendPrivate *priv = GET_PRIVATE(self);
|
|
if (priv->ctx != NULL)
|
|
g_object_unref(priv->ctx);
|
|
g_free(priv->name);
|
|
g_hash_table_unref(priv->devices);
|
|
G_OBJECT_CLASS(fu_backend_parent_class)->finalize(object);
|
|
}
|
|
|
|
static void
|
|
fu_backend_class_init(FuBackendClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
|
GParamSpec *pspec;
|
|
|
|
object_class->get_property = fu_backend_get_property;
|
|
object_class->set_property = fu_backend_set_property;
|
|
object_class->finalize = fu_backend_finalize;
|
|
|
|
pspec =
|
|
g_param_spec_string("name",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
|
|
g_object_class_install_property(object_class, PROP_NAME, pspec);
|
|
|
|
pspec =
|
|
g_param_spec_object("context",
|
|
NULL,
|
|
NULL,
|
|
FU_TYPE_CONTEXT,
|
|
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
|
|
g_object_class_install_property(object_class, PROP_CONTEXT, pspec);
|
|
|
|
signals[SIGNAL_ADDED] = g_signal_new("device-added",
|
|
G_TYPE_FROM_CLASS(object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
g_cclosure_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE,
|
|
1,
|
|
FU_TYPE_DEVICE);
|
|
signals[SIGNAL_REMOVED] = g_signal_new("device-removed",
|
|
G_TYPE_FROM_CLASS(object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
g_cclosure_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE,
|
|
1,
|
|
FU_TYPE_DEVICE);
|
|
signals[SIGNAL_CHANGED] = g_signal_new("device-changed",
|
|
G_TYPE_FROM_CLASS(object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
g_cclosure_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE,
|
|
1,
|
|
FU_TYPE_DEVICE);
|
|
}
|