/* * Copyright (C) 2021 Richard Hughes * * 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); }