diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in
index 16585d714..e9cc0a15e 100644
--- a/contrib/fwupd.spec.in
+++ b/contrib/fwupd.spec.in
@@ -208,6 +208,8 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg
%{_unitdir}/fwupd.service
%{_unitdir}/system-update.target.wants/
%dir %{_localstatedir}/lib/fwupd
+%dir %{_datadir}/fwupd/quirks.d
+%{_datadir}/fwupd/quirks.d/*.quirk
%{_localstatedir}/lib/fwupd/builder/README.md
%{_libdir}/libfwupd*.so.*
%{_libdir}/girepository-1.0/Fwupd-2.0.typelib
diff --git a/data/tests/quirks.d/tests.quirk b/data/tests/quirks.d/tests.quirk
new file mode 100644
index 000000000..0383be65c
--- /dev/null
+++ b/data/tests/quirks.d/tests.quirk
@@ -0,0 +1,12 @@
+[fwupd-plugin-test]
+
+USB\VID_0A5C&PID_6412=ignore-runtime
+
+# this is an empty key
+USB\VID_FFFF&PID_FFFF=
+
+# this is a key with a space
+ACME Inc.=awesome
+
+# this is a wildcard
+CORP*=town
diff --git a/docs/libfwupd/libfwupd-docs.xml b/docs/libfwupd/libfwupd-docs.xml
index 41054479d..1f46e7f77 100644
--- a/docs/libfwupd/libfwupd-docs.xml
+++ b/docs/libfwupd/libfwupd-docs.xml
@@ -45,6 +45,7 @@
+
diff --git a/docs/libfwupd/libfwupd.types b/docs/libfwupd/libfwupd.types
index 3ebd04da7..0fbcf8158 100644
--- a/docs/libfwupd/libfwupd.types
+++ b/docs/libfwupd/libfwupd.types
@@ -1,5 +1,6 @@
fwupd_client_get_type
fwupd_device_get_type
+fwupd_quirks_get_type
fwupd_release_get_type
fwupd_remote_get_type
fwupd_result_get_type
diff --git a/src/fu-engine.c b/src/fu-engine.c
index d6b926bfe..2e5166ca4 100644
--- a/src/fu-engine.c
+++ b/src/fu-engine.c
@@ -75,6 +75,7 @@ struct _FuEngine
GPtrArray *supported_guids;
FuSmbios *smbios;
FuHwids *hwids;
+ FuQuirks *quirks;
};
enum {
@@ -2662,6 +2663,7 @@ fu_engine_load_plugins (FuEngine *self, GError **error)
fu_plugin_set_hwids (plugin, self->hwids);
fu_plugin_set_smbios (plugin, self->smbios);
fu_plugin_set_supported (plugin, self->supported_guids);
+ fu_plugin_set_quirks (plugin, self->quirks);
g_debug ("adding plugin %s", filename);
if (!fu_plugin_open (plugin, filename, &error_local)) {
g_warning ("failed to open plugin %s: %s",
@@ -2867,6 +2869,7 @@ fu_engine_cleanup_state (GError **error)
gboolean
fu_engine_load (FuEngine *self, GError **error)
{
+ g_autoptr(GError) error_quirks = NULL;
g_autoptr(GError) error_hwids = NULL;
g_autoptr(GError) error_smbios = NULL;
@@ -2879,6 +2882,10 @@ fu_engine_load (FuEngine *self, GError **error)
return FALSE;
}
+ /* load the quirk files */
+ if (!fu_quirks_load (self->quirks, &error_quirks))
+ g_warning ("Failed to load quirks: %s", error_quirks->message);
+
/* load AppStream metadata */
as_store_add_filter (self->store, AS_APP_KIND_FIRMWARE);
if (!fu_engine_load_metadata_store (self, error)) {
@@ -2967,6 +2974,7 @@ fu_engine_init (FuEngine *self)
self->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_engine_item_free);
self->smbios = fu_smbios_new ();
self->hwids = fu_hwids_new ();
+ self->quirks = fu_quirks_new ();
self->pending = fu_pending_new ();
self->profile = as_profile_new ();
self->store = as_store_new ();
@@ -2989,6 +2997,7 @@ fu_engine_finalize (GObject *obj)
g_hash_table_unref (self->plugins_hash);
g_object_unref (self->config);
g_object_unref (self->smbios);
+ g_object_unref (self->quirks);
g_object_unref (self->hwids);
g_object_unref (self->pending);
g_object_unref (self->profile);
diff --git a/src/fu-plugin-private.h b/src/fu-plugin-private.h
index b1a36dbd8..9096b3c94 100644
--- a/src/fu-plugin-private.h
+++ b/src/fu-plugin-private.h
@@ -22,6 +22,7 @@
#ifndef __FU_PLUGIN_PRIVATE_H
#define __FU_PLUGIN_PRIVATE_H
+#include "fu-quirks.h"
#include "fu-plugin.h"
#include "fu-smbios.h"
@@ -37,6 +38,8 @@ void fu_plugin_set_hwids (FuPlugin *plugin,
FuHwids *hwids);
void fu_plugin_set_supported (FuPlugin *plugin,
GPtrArray *supported_guids);
+void fu_plugin_set_quirks (FuPlugin *plugin,
+ FuQuirks *quirks);
void fu_plugin_set_smbios (FuPlugin *plugin,
FuSmbios *smbios);
guint fu_plugin_get_order (FuPlugin *plugin);
diff --git a/src/fu-plugin.c b/src/fu-plugin.c
index c74a158f3..24f767bd1 100644
--- a/src/fu-plugin.c
+++ b/src/fu-plugin.c
@@ -56,6 +56,7 @@ typedef struct {
GPtrArray *rules[FU_PLUGIN_RULE_LAST];
gchar *name;
FuHwids *hwids;
+ FuQuirks *quirks;
GPtrArray *supported_guids;
FuSmbios *smbios;
GHashTable *devices; /* platform_id:GObject */
@@ -657,6 +658,78 @@ fu_plugin_set_supported (FuPlugin *plugin, GPtrArray *supported_guids)
priv->supported_guids = g_ptr_array_ref (supported_guids);
}
+void
+fu_plugin_set_quirks (FuPlugin *plugin, FuQuirks *quirks)
+{
+ FuPluginPrivate *priv = GET_PRIVATE (plugin);
+ g_set_object (&priv->quirks, quirks);
+}
+
+/**
+ * fu_plugin_get_quirks:
+ * @plugin: A #FuPlugin
+ *
+ * Returns the hardware database object. This can be used to discover device
+ * quirks or other device-specific settings.
+ *
+ * Returns: (transfer none): a #FuQuirks, or %NULL if not set
+ *
+ * Since: 1.0.1
+ **/
+FuQuirks *
+fu_plugin_get_quirks (FuPlugin *plugin)
+{
+ FuPluginPrivate *priv = GET_PRIVATE (plugin);
+ return priv->quirks;
+}
+
+/**
+ * fu_plugin_lookup_quirk_by_id:
+ * @plugin: A #FuPlugin
+ * @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks"
+ * @id: An ID to match the entry, e.g. "012345"
+ *
+ * Looks up an entry in the hardware database using a string value.
+ *
+ * Returns: (transfer none): values from the database, or %NULL if not found
+ *
+ * Since: 1.0.1
+ **/
+const gchar *
+fu_plugin_lookup_quirk_by_id (FuPlugin *plugin, const gchar *prefix, const gchar *id)
+{
+ FuPluginPrivate *priv = GET_PRIVATE (plugin);
+ g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
+
+ /* wildcard */
+ if (g_strstr_len (id, -1, "*") != NULL)
+ return fu_quirks_lookup_by_glob (priv->quirks, prefix, id);
+
+ /* exact ID */
+ return fu_quirks_lookup_by_id (priv->quirks, prefix, id);
+}
+
+/**
+ * fu_plugin_lookup_quirk_by_usb_device:
+ * @plugin: A #FuPlugin
+ * @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks"
+ * @dev: A #GUsbDevice
+ *
+ * Looks up an entry in the hardware database using various keys generated
+ * from @dev.
+ *
+ * Returns: (transfer none): values from the database, or %NULL if not found
+ *
+ * Since: 1.0.1
+ **/
+const gchar *
+fu_plugin_lookup_quirk_by_usb_device (FuPlugin *plugin, const gchar *prefix, GUsbDevice *dev)
+{
+ FuPluginPrivate *priv = GET_PRIVATE (plugin);
+ g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
+ return fu_quirks_lookup_by_usb_device (priv->quirks, prefix, dev);
+}
+
/**
* fu_plugin_get_supported:
* @plugin: A #FuPlugin
@@ -1386,6 +1459,8 @@ fu_plugin_finalize (GObject *object)
g_object_unref (priv->usb_ctx);
if (priv->hwids != NULL)
g_object_unref (priv->hwids);
+ if (priv->quirks != NULL)
+ g_object_unref (priv->quirks);
if (priv->supported_guids != NULL)
g_ptr_array_unref (priv->supported_guids);
if (priv->smbios != NULL)
diff --git a/src/fu-plugin.h b/src/fu-plugin.h
index 232973e27..b7cb87993 100644
--- a/src/fu-plugin.h
+++ b/src/fu-plugin.h
@@ -30,6 +30,7 @@
#include "fu-common.h"
#include "fu-device.h"
#include "fu-device-locker.h"
+#include "fu-quirks.h"
#include "fu-hwids.h"
G_BEGIN_DECLS
@@ -135,6 +136,13 @@ GBytes *fu_plugin_get_smbios_data (FuPlugin *plugin,
void fu_plugin_add_rule (FuPlugin *plugin,
FuPluginRule rule,
const gchar *name);
+FuQuirks *fu_plugin_get_quirks (FuPlugin *plugin);
+const gchar *fu_plugin_lookup_quirk_by_id (FuPlugin *plugin,
+ const gchar *prefix,
+ const gchar *id);
+const gchar *fu_plugin_lookup_quirk_by_usb_device (FuPlugin *plugin,
+ const gchar *prefix,
+ GUsbDevice *dev);
G_END_DECLS
diff --git a/src/fu-quirks.c b/src/fu-quirks.c
new file mode 100644
index 000000000..b02229d47
--- /dev/null
+++ b/src/fu-quirks.c
@@ -0,0 +1,369 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2017 Richard Hughes
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+
+#include "fu-quirks.h"
+
+#include "fwupd-error.h"
+#include "fwupd-remote-private.h"
+
+/**
+ * SECTION:fu-quirks
+ * @short_description: device quirks
+ *
+ * Quirks can be used to modify device behaviour.
+ * When fwupd is installed in long-term support distros it's very hard to
+ * backport new versions as new hardware is released.
+ *
+ * There are several reasons why we can't just include the mapping and quirk
+ * information in the AppStream metadata:
+ *
+ * * The extra data is hugely specific to the installed fwupd plugin versions
+ * * The device-id is per-device, and the mapping is usually per-plugin
+ * * Often the information is needed before the FuDevice is created
+ * * There are security implications in allowing plugins to handle new devices
+ *
+ * The idea with quirks is that the end user can drop an additional (or replace
+ * an existing) file in a .d director with a simple format and the hardware will
+ * magically start working. This assumes no new quirks are required, as this would
+ * obviously need code changes, but allows us to get most existing devices working
+ * in an easy way without the user compiling anything.
+ *
+ * See also: #FuDevice, #FuPlugin
+ */
+
+static void fu_quirks_finalize (GObject *obj);
+
+struct _FuQuirks
+{
+ GObject parent_instance;
+ GPtrArray *monitors;
+ GHashTable *hash; /* of prefix/id:string */
+};
+
+G_DEFINE_TYPE (FuQuirks, fu_quirks, G_TYPE_OBJECT)
+
+static void
+fu_quirks_monitor_changed_cb (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data)
+{
+ FuQuirks *self = FU_QUIRKS (user_data);
+ g_autoptr(GError) error = NULL;
+ g_autofree gchar *filename = g_file_get_path (file);
+ g_debug ("%s changed, reloading all configs", filename);
+ if (!fu_quirks_load (self, &error))
+ g_warning ("failed to rescan quirks: %s", error->message);
+}
+
+static gboolean
+fu_quirks_add_inotify (FuQuirks *self, const gchar *filename, GError **error)
+{
+ GFileMonitor *monitor;
+ g_autoptr(GFile) file = g_file_new_for_path (filename);
+
+ /* set up a notify watch */
+ monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, error);
+ if (monitor == NULL)
+ return FALSE;
+ g_signal_connect (monitor, "changed",
+ G_CALLBACK (fu_quirks_monitor_changed_cb), self);
+ g_ptr_array_add (self->monitors, monitor);
+ return TRUE;
+}
+
+/**
+ * fu_quirks_lookup_by_id:
+ * @self: A #FuPlugin
+ * @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks"
+ * @id: An ID to match the entry, e.g. "012345"
+ *
+ * Looks up an entry in the hardware database using a string value.
+ *
+ * Returns: (transfer none): values from the database, or %NULL if not found
+ *
+ * Since: 1.0.1
+ **/
+const gchar *
+fu_quirks_lookup_by_id (FuQuirks *self, const gchar *prefix, const gchar *id)
+{
+ g_autofree gchar *key = NULL;
+
+ g_return_val_if_fail (FU_IS_QUIRKS (self), NULL);
+ g_return_val_if_fail (prefix != NULL, NULL);
+ g_return_val_if_fail (id != NULL, NULL);
+
+ key = g_strdup_printf ("%s/%s", prefix, id);
+ return g_hash_table_lookup (self->hash, key);
+}
+
+/**
+ * fu_quirks_lookup_by_glob:
+ * @self: A #FuPlugin
+ * @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks"
+ * @glob: An glob to match the entry, e.g. "foo*bar?baz"
+ *
+ * Looks up an entry in the hardware database using a key glob.
+ * NOTE: This is *much* slower than using fu_quirks_lookup_by_id() as each key
+ * in the quirk database is compared.
+ *
+ * Returns: (transfer none): values from the database, or %NULL if not found
+ *
+ * Since: 1.0.1
+ **/
+const gchar *
+fu_quirks_lookup_by_glob (FuQuirks *self, const gchar *prefix, const gchar *glob)
+{
+ g_autoptr(GList) keys = NULL;
+ gsize prefix_len;
+
+ g_return_val_if_fail (FU_IS_QUIRKS (self), NULL);
+ g_return_val_if_fail (prefix != NULL, NULL);
+ g_return_val_if_fail (glob != NULL, NULL);
+
+ prefix_len = strlen (prefix);
+ keys = g_hash_table_get_keys (self->hash);
+ for (GList *l = keys; l != NULL; l = l->next) {
+ const gchar *id = l->data;
+ if (strncmp (id, prefix, prefix_len) != 0)
+ continue;
+ id += prefix_len + 1;
+ if (fnmatch (glob, id, 0) == 0)
+ return fu_quirks_lookup_by_id (self, prefix, id);
+ if (fnmatch (id, glob, 0) == 0)
+ return fu_quirks_lookup_by_id (self, prefix, id);
+ }
+ return NULL;
+}
+
+/**
+ * fu_quirks_lookup_by_usb_device:
+ * @self: A #FuPlugin
+ * @prefix: A string prefix that matches the quirks file basename, e.g. "dfu-quirks"
+ * @dev: A #GUsbDevice
+ *
+ * Looks up an entry in the hardware database using various keys generated
+ * from @dev.
+ *
+ * Returns: (transfer none): values from the database, or %NULL if not found
+ *
+ * Since: 1.0.1
+ **/
+const gchar *
+fu_quirks_lookup_by_usb_device (FuQuirks *self, const gchar *prefix, GUsbDevice *dev)
+{
+ const gchar *tmp;
+ g_autofree gchar *key1 = NULL;
+ g_autofree gchar *key2 = NULL;
+ g_autofree gchar *key3 = NULL;
+
+ g_return_val_if_fail (FU_IS_QUIRKS (self), NULL);
+ g_return_val_if_fail (prefix != NULL, NULL);
+ g_return_val_if_fail (G_USB_IS_DEVICE (dev), NULL);
+
+ /* prefer an exact match, VID:PID:REV */
+ key1 = g_strdup_printf ("USB\\VID_%04X&PID_%04X&REV_%04X",
+ g_usb_device_get_vid (dev),
+ g_usb_device_get_pid (dev),
+ g_usb_device_get_release (dev));
+ tmp = fu_quirks_lookup_by_id (self, prefix, key1);
+ if (tmp != NULL)
+ return tmp;
+
+ /* VID:PID */
+ key2 = g_strdup_printf ("USB\\VID_%04X&PID_%04X",
+ g_usb_device_get_vid (dev),
+ g_usb_device_get_pid (dev));
+ tmp = fu_quirks_lookup_by_id (self, prefix, key2);
+ if (tmp != NULL)
+ return tmp;
+
+ /* VID */
+ key3 = g_strdup_printf ("USB\\VID_%04X", g_usb_device_get_vid (dev));
+ return fu_quirks_lookup_by_id (self, prefix, key3);
+}
+
+static gboolean
+fu_quirks_add_quirks_from_filename (FuQuirks *self, const gchar *filename, GError **error)
+{
+ g_autoptr(GKeyFile) kf = g_key_file_new ();
+ g_auto(GStrv) groups = NULL;
+
+ /* load keyfile */
+ if (!g_key_file_load_from_file (kf, filename, G_KEY_FILE_NONE, error))
+ return FALSE;
+
+ /* add each set of groups and keys */
+ groups = g_key_file_get_groups (kf, NULL);
+ for (guint i = 0; groups[i] != NULL; i++) {
+ g_auto(GStrv) keys = NULL;
+ keys = g_key_file_get_keys (kf, groups[i], NULL, error);
+ if (keys == NULL)
+ return FALSE;
+ for (guint j = 0; keys[j] != NULL; j++) {
+ g_autofree gchar *tmp = NULL;
+ tmp = g_key_file_get_string (kf, groups[i], keys[j], error);
+ if (tmp == NULL)
+ return FALSE;
+ g_hash_table_insert (self->hash,
+ g_strdup_printf ("%s/%s", groups[i], keys[j]),
+ g_steal_pointer (&tmp));
+ }
+ }
+ g_debug ("now %u quirk entries", g_hash_table_size (self->hash));
+ return TRUE;
+}
+
+static gint
+fu_quirks_filename_sort_cb (gconstpointer a, gconstpointer b)
+{
+ const gchar *stra = *((const gchar **) a);
+ const gchar *strb = *((const gchar **) b);
+ return g_strcmp0 (stra, strb);
+}
+
+static gboolean
+fu_quirks_add_quirks_for_path (FuQuirks *self, const gchar *path, GError **error)
+{
+ const gchar *tmp;
+ g_autofree gchar *path_hw = NULL;
+ g_autoptr(GDir) dir = NULL;
+ g_autoptr(GPtrArray) filenames = g_ptr_array_new_with_free_func (g_free);
+
+ /* add valid files to the array */
+ path_hw = g_build_filename (path, "quirks.d", NULL);
+ if (!g_file_test (path_hw, G_FILE_TEST_EXISTS)) {
+ g_debug ("no %s, skipping", path_hw);
+ return TRUE;
+ }
+ dir = g_dir_open (path_hw, 0, error);
+ if (dir == NULL)
+ return FALSE;
+ while ((tmp = g_dir_read_name (dir)) != NULL) {
+ if (!g_str_has_suffix (tmp, ".quirk")) {
+ g_debug ("skipping invalid file %s", tmp);
+ continue;
+ }
+ g_ptr_array_add (filenames, g_build_filename (path_hw, tmp, NULL));
+ }
+
+ /* sort */
+ g_ptr_array_sort (filenames, fu_quirks_filename_sort_cb);
+
+ /* process files */
+ for (guint i = 0; i < filenames->len; i++) {
+ const gchar *filename = g_ptr_array_index (filenames, i);
+
+ /* load from keyfile */
+ g_debug ("loading quirks from %s", filename);
+ if (!fu_quirks_add_quirks_from_filename (self, filename, error)) {
+ g_prefix_error (error, "failed to load %s: ", filename);
+ return FALSE;
+ }
+
+ /* watch the file for changes */
+ if (!fu_quirks_add_inotify (self, filename, error))
+ return FALSE;
+ }
+
+ /* success */
+ return TRUE;
+}
+
+/**
+ * fu_quirks_load: (skip)
+ * @self: A #FuQuirks
+ * @error: A #GError, or %NULL
+ *
+ * Loads the various files that define the hardware quirks used in plugins.
+ *
+ * Returns: %TRUE for success
+ *
+ * Since: 1.0.1
+ **/
+gboolean
+fu_quirks_load (FuQuirks *self, GError **error)
+{
+ g_autofree gchar *localstate_fwupd = NULL;
+ g_return_val_if_fail (FU_IS_QUIRKS (self), FALSE);
+
+ /* ensure empty in case we're called from a monitor change */
+ g_ptr_array_set_size (self->monitors, 0);
+ g_hash_table_remove_all (self->hash);
+
+ /* system datadir */
+ if (!fu_quirks_add_quirks_for_path (self, FWUPDDATADIR, error))
+ return FALSE;
+
+ /* something we can write when using Ostree */
+ localstate_fwupd = g_build_filename (LOCALSTATEDIR, "lib", "fwupd", NULL);
+ if (!fu_quirks_add_quirks_for_path (self, localstate_fwupd, error))
+ return FALSE;
+
+ /* success */
+ return TRUE;
+}
+
+static void
+fu_quirks_class_init (FuQuirksClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = fu_quirks_finalize;
+}
+
+static void
+fu_quirks_init (FuQuirks *self)
+{
+ self->monitors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+ self->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+}
+
+static void
+fu_quirks_finalize (GObject *obj)
+{
+ FuQuirks *self = FU_QUIRKS (obj);
+ g_ptr_array_unref (self->monitors);
+ g_hash_table_unref (self->hash);
+ G_OBJECT_CLASS (fu_quirks_parent_class)->finalize (obj);
+}
+
+/**
+ * fu_quirks_new: (skip)
+ *
+ * Creates a new quirks object.
+ *
+ * Return value: a new #FuQuirks
+ **/
+FuQuirks *
+fu_quirks_new (void)
+{
+ FuQuirks *self;
+ self = g_object_new (FU_TYPE_QUIRKS, NULL);
+ return FU_QUIRKS (self);
+}
diff --git a/src/fu-quirks.h b/src/fu-quirks.h
index d91824b58..50d60c0e0 100644
--- a/src/fu-quirks.h
+++ b/src/fu-quirks.h
@@ -1,6 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2016 Mario Limonciello
+ * Copyright (C) 2017 Richard Hughes
*
* Licensed under the GNU General Public License Version 2
*
@@ -22,6 +23,30 @@
#ifndef __FU_QUIRKS_H
#define __FU_QUIRKS_H
+G_BEGIN_DECLS
+
+#include
+#include
+
+#define FU_TYPE_QUIRKS (fu_quirks_get_type ())
+G_DECLARE_FINAL_TYPE (FuQuirks, fu_quirks, FU, QUIRKS, GObject)
+
+FuQuirks *fu_quirks_new (void);
+gboolean fu_quirks_load (FuQuirks *self,
+ GError **error);
+const gchar *fu_quirks_lookup_by_id (FuQuirks *self,
+ const gchar *prefix,
+ const gchar *id);
+const gchar *fu_quirks_lookup_by_glob (FuQuirks *self,
+ const gchar *prefix,
+ const gchar *glob);
+const gchar *fu_quirks_lookup_by_usb_device (FuQuirks *self,
+ const gchar *prefix,
+ GUsbDevice *dev);
+
+#include
+
+/* FIXME: port to above */
typedef struct {
const gchar *sys_vendor;
const gchar *identifier;
@@ -35,5 +60,6 @@ static const FuVendorQuirks quirk_table[] = {
{ NULL, NULL, AS_VERSION_PARSE_FLAG_NONE }
};
-#endif /* __FU_QUIRKS_H */
+G_END_DECLS
+#endif /* __FU_QUIRKS_H */
diff --git a/src/fu-self-test.c b/src/fu-self-test.c
index 08990d6be..44207ff01 100644
--- a/src/fu-self-test.c
+++ b/src/fu-self-test.c
@@ -29,8 +29,10 @@
#include
#include
+#include "fu-config.h"
#include "fu-device-private.h"
#include "fu-engine.h"
+#include "fu-quirks.h"
#include "fu-keyring.h"
#include "fu-pending.h"
#include "fu-plugin-private.h"
@@ -413,6 +415,45 @@ _plugin_device_register_cb (FuPlugin *plugin, FuDevice *device, gpointer user_da
fu_plugin_runner_device_register (plugin, device);
}
+static void
+fu_plugin_quirks_func (void)
+{
+ const gchar *tmp;
+ gboolean ret;
+ g_autoptr(FuQuirks) quirks = fu_quirks_new ();
+ g_autoptr(FuPlugin) plugin = fu_plugin_new ();
+ g_autoptr(GError) error = NULL;
+
+ ret = fu_quirks_load (quirks, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ fu_plugin_set_quirks (plugin, quirks);
+
+ /* exact */
+ tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "USB\\VID_0A5C&PID_6412");
+ g_assert_cmpstr (tmp, ==, "ignore-runtime");
+ tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "ACME Inc.");
+ g_assert_cmpstr (tmp, ==, "awesome");
+ tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "CORP*");
+ g_assert_cmpstr (tmp, ==, "town");
+ tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "USB\\VID_FFFF&PID_FFFF");
+ g_assert_cmpstr (tmp, ==, "");
+ tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-Unfound", "baz");
+ g_assert_cmpstr (tmp, ==, NULL);
+ tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-tests", "unfound");
+ g_assert_cmpstr (tmp, ==, NULL);
+ tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-unfound", "unfound");
+ g_assert_cmpstr (tmp, ==, NULL);
+
+ /* glob */
+ tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "ACME*");
+ g_assert_cmpstr (tmp, ==, "awesome");
+ tmp = fu_quirks_lookup_by_glob (quirks, "fwupd-plugin-test", "CORPORATION");
+ g_assert_cmpstr (tmp, ==, "town");
+ tmp = fu_plugin_lookup_quirk_by_id (plugin, "fwupd-plugin-test", "unfound*");
+ g_assert_cmpstr (tmp, ==, NULL);
+}
+
static void
fu_plugin_module_func (void)
{
@@ -927,6 +968,7 @@ main (int argc, char **argv)
g_test_add_func ("/fwupd/pending", fu_pending_func);
g_test_add_func ("/fwupd/plugin{delay}", fu_plugin_delay_func);
g_test_add_func ("/fwupd/plugin{module}", fu_plugin_module_func);
+ g_test_add_func ("/fwupd/plugin{quirks}", fu_plugin_quirks_func);
g_test_add_func ("/fwupd/keyring{gpg}", fu_keyring_gpg_func);
g_test_add_func ("/fwupd/keyring{pkcs7}", fu_keyring_pkcs7_func);
g_test_add_func ("/fwupd/common{spawn)", fu_common_spawn_func);
diff --git a/src/meson.build b/src/meson.build
index 4ceb6a16c..c1dce65d8 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -28,6 +28,7 @@ libfwupdprivate = static_library(
'fu-pending.c',
'fu-plugin.c',
'fu-progressbar.c',
+ 'fu-quirks.c',
'fu-smbios.c',
'fu-test.c',
],
@@ -50,6 +51,7 @@ libfwupdprivate = static_library(
cargs,
'-DLOCALSTATEDIR="' + localstatedir + '"',
'-DSYSFSFIRMWAREDIR="/sys/firmware"',
+ '-DFWUPDDATADIR="' + join_paths(get_option('prefix'), get_option('datadir'), 'fwupd') + '"',
'-DFU_OFFLINE_DESTDIR=""',
],
)
@@ -129,6 +131,7 @@ executable(
'fu-keyring.c',
'fu-pending.c',
'fu-plugin.c',
+ 'fu-quirks.c',
'fu-smbios.c',
],
include_directories : [
@@ -155,6 +158,7 @@ executable(
'-DPLUGINDIR="' + plugin_dir + '"',
'-DSYSFSFIRMWAREDIR="/sys/firmware"',
'-DSYSCONFDIR="' + default_sysconfdir + '"',
+ '-DFWUPDDATADIR="' + join_paths(get_option('prefix'), get_option('datadir'), 'fwupd') + '"',
'-DFWUPDCONFIGDIR="' + join_paths(default_sysconfdir, 'fwupd') + '"',
'-DFU_OFFLINE_DESTDIR=""',
],
@@ -187,6 +191,7 @@ if get_option('enable-tests')
'fu-keyring-result.c',
'fu-plugin.c',
'fu-progressbar.c',
+ 'fu-quirks.c',
'fu-smbios.c',
'fu-test.c',
],
@@ -220,6 +225,7 @@ if get_option('enable-tests')
'-DFU_OFFLINE_DESTDIR="/tmp/fwupd-self-test"',
'-DPLUGINDIR="' + testdatadir_src + '"',
'-DSYSFSFIRMWAREDIR="' + testdatadir_src + '"',
+ '-DFWUPDDATADIR="' + testdatadir_src + '"',
'-DSYSCONFDIR="' + testdatadir_src + '"',
'-DFWUPDCONFIGDIR="' + testdatadir_src + '"',
],
@@ -239,6 +245,8 @@ if get_option('enable-introspection')
'fu-device-locker.h',
'fu-plugin.c',
'fu-plugin.h',
+ 'fu-quirks.c',
+ 'fu-quirks.h',
],
nsversion : '1.0',
namespace : 'Fu',