blob: 4359d0bdd54ac2464186e37a40474b3517b1925d [file] [log] [blame]
Richard Hughesd0905142016-03-13 09:46:49 +00001/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2016 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include "config.h"
23
Richard Hughescff38bc2016-12-12 12:03:37 +000024#include <fwupd.h>
25#include <gmodule.h>
26#include <appstream-glib.h>
27#include <errno.h>
28#include <string.h>
29#include <gio/gunixinputstream.h>
Mario Limonciello6d0aa3d2017-02-28 08:22:27 -060030#ifdef HAVE_VALGRIND
Richard Hughes576c0122017-02-24 09:47:00 +000031#include <valgrind.h>
Mario Limonciello6d0aa3d2017-02-28 08:22:27 -060032#endif /* HAVE_VALGRIND */
Richard Hughesd0905142016-03-13 09:46:49 +000033
Richard Hughescff38bc2016-12-12 12:03:37 +000034#include "fu-plugin-private.h"
35#include "fu-pending.h"
Richard Hughesd0905142016-03-13 09:46:49 +000036
Richard Hughesb0829032017-01-10 09:27:08 +000037#define FU_PLUGIN_COLDPLUG_DELAY_MAXIMUM 3000u /* ms */
38
Richard Hughescff38bc2016-12-12 12:03:37 +000039static void fu_plugin_finalize (GObject *object);
40
41typedef struct {
42 GModule *module;
43 GUsbContext *usb_ctx;
44 gboolean enabled;
45 gchar *name;
Richard Hughesb8f8db22017-04-25 15:56:00 +010046 GHashTable *hwids; /* hwid:1 */
Richard Hughescff38bc2016-12-12 12:03:37 +000047 GHashTable *devices; /* platform_id:GObject */
Richard Hughesae3d65f2016-12-16 09:38:01 +000048 GHashTable *devices_delay; /* FuDevice:FuPluginHelper */
Richard Hughescff38bc2016-12-12 12:03:37 +000049 FuPluginData *data;
50} FuPluginPrivate;
51
52enum {
53 SIGNAL_DEVICE_ADDED,
54 SIGNAL_DEVICE_REMOVED,
55 SIGNAL_STATUS_CHANGED,
56 SIGNAL_PERCENTAGE_CHANGED,
Richard Hughes362d6d72017-01-07 21:42:14 +000057 SIGNAL_RECOLDPLUG,
Richard Hughesb0829032017-01-10 09:27:08 +000058 SIGNAL_SET_COLDPLUG_DELAY,
Richard Hughescff38bc2016-12-12 12:03:37 +000059 SIGNAL_LAST
60};
61
62static guint signals[SIGNAL_LAST] = { 0 };
63
64G_DEFINE_TYPE_WITH_PRIVATE (FuPlugin, fu_plugin, G_TYPE_OBJECT)
65#define GET_PRIVATE(o) (fu_plugin_get_instance_private (o))
66
67typedef const gchar *(*FuPluginGetNameFunc) (void);
68typedef void (*FuPluginInitFunc) (FuPlugin *plugin);
69typedef gboolean (*FuPluginStartupFunc) (FuPlugin *plugin,
70 GError **error);
71typedef gboolean (*FuPluginDeviceFunc) (FuPlugin *plugin,
72 FuDevice *device,
73 GError **error);
74typedef gboolean (*FuPluginVerifyFunc) (FuPlugin *plugin,
75 FuDevice *device,
76 FuPluginVerifyFlags flags,
77 GError **error);
78typedef gboolean (*FuPluginUpdateFunc) (FuPlugin *plugin,
79 FuDevice *device,
80 GBytes *blob_fw,
81 FwupdInstallFlags flags,
82 GError **error);
83
Richard Hughes57d18222017-01-10 16:02:59 +000084/**
85 * fu_plugin_get_name:
86 * @plugin: A #FuPlugin
87 *
88 * Gets the plugin name.
89 *
90 * Returns: a plugin name, or %NULL for unknown.
91 *
92 * Since: 0.8.0
93 **/
Richard Hughescff38bc2016-12-12 12:03:37 +000094const gchar *
95fu_plugin_get_name (FuPlugin *plugin)
Richard Hughesd0905142016-03-13 09:46:49 +000096{
Richard Hughescff38bc2016-12-12 12:03:37 +000097 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +000098 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +000099 return priv->name;
100}
Richard Hughesd0905142016-03-13 09:46:49 +0000101
Richard Hughes57d18222017-01-10 16:02:59 +0000102/**
103 * fu_plugin_cache_lookup:
104 * @plugin: A #FuPlugin
105 * @id: the key
106 *
107 * Finds an object in the per-plugin cache.
108 *
109 * Returns: (transfer none): a #GObject, or %NULL for unfound.
110 *
111 * Since: 0.8.0
112 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000113gpointer
114fu_plugin_cache_lookup (FuPlugin *plugin, const gchar *id)
115{
116 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000117 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
118 g_return_val_if_fail (id != NULL, NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000119 return g_hash_table_lookup (priv->devices, id);
120}
Richard Hughesd0905142016-03-13 09:46:49 +0000121
Richard Hughes57d18222017-01-10 16:02:59 +0000122/**
123 * fu_plugin_cache_add:
124 * @plugin: A #FuPlugin
125 * @id: the key
126 * @dev: a #GObject, typically a #FuDevice
127 *
128 * Adds an object to the per-plugin cache.
129 *
130 * Since: 0.8.0
131 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000132void
133fu_plugin_cache_add (FuPlugin *plugin, const gchar *id, gpointer dev)
134{
135 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000136 g_return_if_fail (FU_IS_PLUGIN (plugin));
137 g_return_if_fail (id != NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000138 g_hash_table_insert (priv->devices, g_strdup (id), g_object_ref (dev));
139}
140
Richard Hughes57d18222017-01-10 16:02:59 +0000141/**
142 * fu_plugin_cache_remove:
143 * @plugin: A #FuPlugin
144 * @id: the key
145 *
146 * Removes an object from the per-plugin cache.
147 *
148 * Since: 0.8.0
149 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000150void
151fu_plugin_cache_remove (FuPlugin *plugin, const gchar *id)
152{
153 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000154 g_return_if_fail (FU_IS_PLUGIN (plugin));
155 g_return_if_fail (id != NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000156 g_hash_table_remove (priv->devices, id);
157}
158
Richard Hughes57d18222017-01-10 16:02:59 +0000159/**
160 * fu_plugin_get_data:
161 * @plugin: A #FuPlugin
162 *
163 * Gets the per-plugin allocated private data.
164 *
165 * Returns: (transfer full): a pointer to a structure, or %NULL for unset.
166 *
167 * Since: 0.8.0
168 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000169FuPluginData *
170fu_plugin_get_data (FuPlugin *plugin)
171{
172 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000173 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000174 return priv->data;
175}
176
Richard Hughes57d18222017-01-10 16:02:59 +0000177/**
178 * fu_plugin_alloc_data:
179 * @plugin: A #FuPlugin
180 * @data_sz: the size to allocate
181 *
182 * Allocates the per-plugin allocated private data.
183 *
184 * Returns: (transfer full): a pointer to a structure, or %NULL for unset.
185 *
186 * Since: 0.8.0
187 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000188FuPluginData *
189fu_plugin_alloc_data (FuPlugin *plugin, gsize data_sz)
190{
191 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000192 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
Richard Hughes44dee882017-01-11 08:31:10 +0000193 if (priv->data != NULL) {
194 g_critical ("fu_plugin_alloc_data() already used by plugin");
195 return priv->data;
196 }
Richard Hughescff38bc2016-12-12 12:03:37 +0000197 priv->data = g_malloc0 (data_sz);
198 return priv->data;
Richard Hughesd0905142016-03-13 09:46:49 +0000199}
200
Richard Hughes57d18222017-01-10 16:02:59 +0000201/**
202 * fu_plugin_get_usb_context:
203 * @plugin: A #FuPlugin
204 *
205 * Gets the shared USB context that all plugins can use.
206 *
207 * Returns: (transfer none): a #GUsbContext.
208 *
209 * Since: 0.8.0
210 **/
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000211GUsbContext *
212fu_plugin_get_usb_context (FuPlugin *plugin)
213{
Richard Hughescff38bc2016-12-12 12:03:37 +0000214 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000215 g_return_val_if_fail (FU_IS_PLUGIN (plugin), NULL);
Richard Hughescff38bc2016-12-12 12:03:37 +0000216 return priv->usb_ctx;
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000217}
218
219void
220fu_plugin_set_usb_context (FuPlugin *plugin, GUsbContext *usb_ctx)
221{
Richard Hughescff38bc2016-12-12 12:03:37 +0000222 FuPluginPrivate *priv = GET_PRIVATE (plugin);
223 g_set_object (&priv->usb_ctx, usb_ctx);
224}
225
Richard Hughes57d18222017-01-10 16:02:59 +0000226/**
227 * fu_plugin_get_enabled:
228 * @plugin: A #FuPlugin
229 *
230 * Returns if the plugin is enabled.
231 *
232 * Returns: %TRUE if the plugin is currently enabled.
233 *
234 * Since: 0.8.0
235 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000236gboolean
237fu_plugin_get_enabled (FuPlugin *plugin)
238{
239 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000240 g_return_val_if_fail (FU_IS_PLUGIN (plugin), FALSE);
Richard Hughescff38bc2016-12-12 12:03:37 +0000241 return priv->enabled;
Richard Hughesbc93e4a2016-12-08 17:29:51 +0000242}
243
Richard Hughes57d18222017-01-10 16:02:59 +0000244/**
245 * fu_plugin_set_enabled:
246 * @plugin: A #FuPlugin
247 * @enabled: the enabled value
248 *
249 * Enables or disables a plugin. Plugins can self-disable at any point.
250 *
251 * Since: 0.8.0
252 **/
Richard Hughesd0905142016-03-13 09:46:49 +0000253void
Richard Hughescff38bc2016-12-12 12:03:37 +0000254fu_plugin_set_enabled (FuPlugin *plugin, gboolean enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000255{
Richard Hughescff38bc2016-12-12 12:03:37 +0000256 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughesccd78a92017-01-11 16:57:41 +0000257 g_return_if_fail (FU_IS_PLUGIN (plugin));
Richard Hughescff38bc2016-12-12 12:03:37 +0000258 priv->enabled = enabled;
259}
260
261gboolean
262fu_plugin_open (FuPlugin *plugin, const gchar *filename, GError **error)
263{
264 FuPluginPrivate *priv = GET_PRIVATE (plugin);
265 FuPluginInitFunc func = NULL;
266 gchar *str;
267
268 priv->module = g_module_open (filename, 0);
269 if (priv->module == NULL) {
270 g_set_error (error,
271 G_IO_ERROR,
272 G_IO_ERROR_FAILED,
273 "failed to open plugin: %s",
274 g_module_error ());
275 return FALSE;
276 }
277
278 /* set automatically */
279 str = g_strstr_len (filename, -1, "libfu_plugin_");
280 if (str != NULL) {
281 priv->name = g_strdup (str + 13);
282 g_strdelimit (priv->name, ".", '\0');
283 }
Richard Hughesd0905142016-03-13 09:46:49 +0000284
285 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000286 g_module_symbol (priv->module, "fu_plugin_init", (gpointer *) &func);
287 if (func != NULL) {
288 g_debug ("performing init() on %s", filename);
Richard Hughesd0905142016-03-13 09:46:49 +0000289 func (plugin);
290 }
291
Richard Hughescff38bc2016-12-12 12:03:37 +0000292 return TRUE;
293}
294
Richard Hughes57d18222017-01-10 16:02:59 +0000295/**
296 * fu_plugin_device_add:
297 * @plugin: A #FuPlugin
298 * @device: A #FuDevice
299 *
300 * Asks the daemon to add a device to the exported list. If this device ID
301 * has already been added by a different plugin then this request will be
302 * ignored.
303 *
304 * Plugins should use fu_plugin_device_add_delay() if they are not capable of
305 * actually flashing an image to the hardware so that higher-priority plugins
306 * can add the device themselves.
307 *
308 * Since: 0.8.0
309 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000310void
311fu_plugin_device_add (FuPlugin *plugin, FuDevice *device)
312{
Richard Hughesccd78a92017-01-11 16:57:41 +0000313 g_return_if_fail (FU_IS_PLUGIN (plugin));
314 g_return_if_fail (FU_IS_DEVICE (device));
315
Richard Hughescff38bc2016-12-12 12:03:37 +0000316 g_debug ("emit added from %s: %s",
317 fu_plugin_get_name (plugin),
318 fu_device_get_id (device));
319 fu_device_set_created (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
320 fu_device_set_plugin (device, fu_plugin_get_name (plugin));
321 g_signal_emit (plugin, signals[SIGNAL_DEVICE_ADDED], 0, device);
322}
323
Richard Hughesae3d65f2016-12-16 09:38:01 +0000324typedef struct {
325 FuPlugin *plugin;
326 FuDevice *device;
327 guint timeout_id;
Richard Hughesd4184cf2016-12-21 16:05:17 +0000328 GHashTable *devices;
Richard Hughesae3d65f2016-12-16 09:38:01 +0000329} FuPluginHelper;
330
331static void
332fu_plugin_helper_free (FuPluginHelper *helper)
333{
334 g_object_unref (helper->plugin);
335 g_object_unref (helper->device);
Richard Hughesd4184cf2016-12-21 16:05:17 +0000336 g_hash_table_unref (helper->devices);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000337 g_free (helper);
338}
339
340static gboolean
341fu_plugin_device_add_delay_cb (gpointer user_data)
342{
343 FuPluginHelper *helper = (FuPluginHelper *) user_data;
Richard Hughesf0a799e2017-01-17 20:13:30 +0000344 g_hash_table_remove (helper->devices, helper->device);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000345 fu_plugin_device_add (helper->plugin, helper->device);
346 fu_plugin_helper_free (helper);
347 return FALSE;
348}
349
Richard Hughes57d18222017-01-10 16:02:59 +0000350/**
Richard Hughesf0a799e2017-01-17 20:13:30 +0000351 * fu_plugin_has_device_delay:
352 * @plugin: A #FuPlugin
353 *
354 * Returns if the device has a pending device that is waiting to be added.
355 *
356 * Returns: %TRUE if a device is waiting to be added
357 *
358 * Since: 0.8.0
359 **/
360gboolean
361fu_plugin_has_device_delay (FuPlugin *plugin)
362{
363 FuPluginPrivate *priv = GET_PRIVATE (plugin);
364 return g_hash_table_size (priv->devices_delay) > 0;
365}
366
367/**
Richard Hughes57d18222017-01-10 16:02:59 +0000368 * fu_plugin_device_add_delay:
369 * @plugin: A #FuPlugin
370 * @device: A #FuDevice
371 *
372 * Asks the daemon to add a device to the exported list after a small delay.
373 *
374 * Since: 0.8.0
375 **/
Richard Hughesae3d65f2016-12-16 09:38:01 +0000376void
377fu_plugin_device_add_delay (FuPlugin *plugin, FuDevice *device)
378{
379 FuPluginPrivate *priv = GET_PRIVATE (plugin);
380 FuPluginHelper *helper;
Richard Hughesccd78a92017-01-11 16:57:41 +0000381
382 g_return_if_fail (FU_IS_PLUGIN (plugin));
383 g_return_if_fail (FU_IS_DEVICE (device));
384
Richard Hughes6ad951d2017-02-03 10:12:12 +0000385 /* already waiting for add */
386 helper = g_hash_table_lookup (priv->devices_delay, device);
387 if (helper != NULL) {
388 g_warning ("ignoring add-delay as device %s already pending",
389 fu_device_get_id (device));
390 return;
391 }
392
393 /* add after a small delay */
Richard Hughesae3d65f2016-12-16 09:38:01 +0000394 g_debug ("waiting a small time for other plugins");
395 helper = g_new0 (FuPluginHelper, 1);
396 helper->plugin = g_object_ref (plugin);
397 helper->device = g_object_ref (device);
398 helper->timeout_id = g_timeout_add (500, fu_plugin_device_add_delay_cb, helper);
Richard Hughesd4184cf2016-12-21 16:05:17 +0000399 helper->devices = g_hash_table_ref (priv->devices_delay);
400 g_hash_table_insert (helper->devices, device, helper);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000401}
402
Richard Hughes57d18222017-01-10 16:02:59 +0000403/**
404 * fu_plugin_device_add:
405 * @plugin: A #FuPlugin
406 * @device: A #FuDevice
407 *
408 * Asks the daemon to remove a device from the exported list.
409 *
410 * Since: 0.8.0
411 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000412void
413fu_plugin_device_remove (FuPlugin *plugin, FuDevice *device)
414{
Richard Hughesae3d65f2016-12-16 09:38:01 +0000415 FuPluginPrivate *priv = GET_PRIVATE (plugin);
416 FuPluginHelper *helper;
417
Richard Hughesccd78a92017-01-11 16:57:41 +0000418 g_return_if_fail (FU_IS_PLUGIN (plugin));
419 g_return_if_fail (FU_IS_DEVICE (device));
420
Richard Hughesae3d65f2016-12-16 09:38:01 +0000421 /* waiting for add */
422 helper = g_hash_table_lookup (priv->devices_delay, device);
423 if (helper != NULL) {
424 g_debug ("ignoring remove from delayed addition");
425 g_source_remove (helper->timeout_id);
Richard Hughesd4184cf2016-12-21 16:05:17 +0000426 g_hash_table_remove (priv->devices_delay, helper->device);
Richard Hughesae3d65f2016-12-16 09:38:01 +0000427 fu_plugin_helper_free (helper);
428 return;
429 }
430
Richard Hughescff38bc2016-12-12 12:03:37 +0000431 g_debug ("emit removed from %s: %s",
432 fu_plugin_get_name (plugin),
433 fu_device_get_id (device));
434 g_signal_emit (plugin, signals[SIGNAL_DEVICE_REMOVED], 0, device);
435}
436
Richard Hughes57d18222017-01-10 16:02:59 +0000437/**
438 * fu_plugin_set_status:
439 * @plugin: A #FuPlugin
440 * @status: A #FwupdStatus, e.g. #FWUPD_STATUS_DECOMPRESSING
441 *
442 * Sets the global state of the daemon according to the current plugin action.
443 *
444 * Since: 0.8.0
445 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000446void
447fu_plugin_set_status (FuPlugin *plugin, FwupdStatus status)
448{
Richard Hughesccd78a92017-01-11 16:57:41 +0000449 g_return_if_fail (FU_IS_PLUGIN (plugin));
Richard Hughescff38bc2016-12-12 12:03:37 +0000450 g_signal_emit (plugin, signals[SIGNAL_STATUS_CHANGED], 0, status);
451}
452
Richard Hughes57d18222017-01-10 16:02:59 +0000453/**
454 * fu_plugin_set_percentage:
455 * @plugin: A #FuPlugin
456 * @percentage: the percentage complete
457 *
458 * Sets the global completion of the daemon according to the current plugin
459 * action.
460 *
461 * Since: 0.8.0
462 **/
Richard Hughescff38bc2016-12-12 12:03:37 +0000463void
464fu_plugin_set_percentage (FuPlugin *plugin, guint percentage)
465{
Richard Hughesccd78a92017-01-11 16:57:41 +0000466 g_return_if_fail (FU_IS_PLUGIN (plugin));
467 g_return_if_fail (percentage <= 100);
Richard Hughescff38bc2016-12-12 12:03:37 +0000468 g_signal_emit (plugin, signals[SIGNAL_PERCENTAGE_CHANGED], 0,
469 percentage);
Richard Hughesd0905142016-03-13 09:46:49 +0000470}
471
Richard Hughes362d6d72017-01-07 21:42:14 +0000472/**
473 * fu_plugin_recoldplug:
474 * @plugin: A #FuPlugin
475 *
476 * Ask all the plugins to coldplug all devices, which will include the prepare()
477 * and cleanup() phases. Duplicate devices added will be ignored.
478 *
479 * Since: 0.8.0
480 **/
481void
482fu_plugin_recoldplug (FuPlugin *plugin)
483{
Richard Hughesccd78a92017-01-11 16:57:41 +0000484 g_return_if_fail (FU_IS_PLUGIN (plugin));
Richard Hughes362d6d72017-01-07 21:42:14 +0000485 g_signal_emit (plugin, signals[SIGNAL_RECOLDPLUG], 0);
486}
487
Richard Hughesb0829032017-01-10 09:27:08 +0000488/**
Richard Hughesb8f8db22017-04-25 15:56:00 +0100489 * fu_plugin_check_hwid:
490 * @plugin: A #FuPlugin
491 * @hwid: A Hardware ID GUID, e.g. "6de5d951-d755-576b-bd09-c5cf66b27234"
492 *
493 * Checks to see if a specific hardware ID exists. All hardware IDs on a
494 * specific system can be shown using the `fwupdmgr hwids` command.
495 *
496 * Since: 0.9.1
497 **/
498gboolean
499fu_plugin_check_hwid (FuPlugin *plugin, const gchar *hwid)
500{
501 FuPluginPrivate *priv = GET_PRIVATE (plugin);
502 if (priv->hwids == NULL)
503 return FALSE;
504 return g_hash_table_lookup (priv->hwids, hwid) != NULL;
505}
506
507void
508fu_plugin_set_hwids (FuPlugin *plugin, GHashTable *hwids)
509{
510 FuPluginPrivate *priv = GET_PRIVATE (plugin);
511 if (priv->hwids != NULL)
512 g_hash_table_unref (priv->hwids);
513 priv->hwids = g_hash_table_ref (hwids);
514}
515
516/**
Richard Hughesb0829032017-01-10 09:27:08 +0000517 * fu_plugin_set_coldplug_delay:
518 * @plugin: A #FuPlugin
519 * @duration: A delay in milliseconds
520 *
521 * Set the minimum time that should be waited inbetween the call to
522 * fu_plugin_coldplug_prepare() and fu_plugin_coldplug(). This is usually going
523 * to be the minimum hardware initialisation time from a datasheet.
524 *
525 * It is better to use this function rather than using a sleep() in the plugin
526 * itself as then only one delay is done in the daemon rather than waiting for
527 * each coldplug prepare in a serial way.
528 *
529 * Additionally, very long delays should be avoided as the daemon will be
530 * blocked from processing requests whilst the coldplug delay is being
531 * performed.
532 *
533 * Since: 0.8.0
534 **/
535void
536fu_plugin_set_coldplug_delay (FuPlugin *plugin, guint duration)
537{
538 g_return_if_fail (FU_IS_PLUGIN (plugin));
539 g_return_if_fail (duration > 0);
540
541 /* check sanity */
542 if (duration > FU_PLUGIN_COLDPLUG_DELAY_MAXIMUM) {
543 g_warning ("duration of %ums is crazy, truncating to %ums",
544 duration,
545 FU_PLUGIN_COLDPLUG_DELAY_MAXIMUM);
546 duration = FU_PLUGIN_COLDPLUG_DELAY_MAXIMUM;
547 }
548
549 /* emit */
550 g_signal_emit (plugin, signals[SIGNAL_SET_COLDPLUG_DELAY], 0, duration);
551}
552
Richard Hughesd0905142016-03-13 09:46:49 +0000553gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000554fu_plugin_runner_startup (FuPlugin *plugin, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000555{
Richard Hughescff38bc2016-12-12 12:03:37 +0000556 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000557 FuPluginStartupFunc func = NULL;
Richard Hughesd0905142016-03-13 09:46:49 +0000558
559 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000560 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000561 return TRUE;
562
563 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000564 g_module_symbol (priv->module, "fu_plugin_startup", (gpointer *) &func);
565 if (func == NULL)
Richard Hughesd0905142016-03-13 09:46:49 +0000566 return TRUE;
Richard Hughescff38bc2016-12-12 12:03:37 +0000567 g_debug ("performing startup() on %s", priv->name);
Richard Hughesd0905142016-03-13 09:46:49 +0000568 if (!func (plugin, error)) {
Richard Hughescff38bc2016-12-12 12:03:37 +0000569 g_prefix_error (error, "failed to startup %s: ", priv->name);
570 return FALSE;
571 }
572 return TRUE;
573}
574
575static gboolean
576fu_plugin_runner_offline_invalidate (GError **error)
577{
578 g_autoptr(GError) error_local = NULL;
579 g_autoptr(GFile) file1 = NULL;
580
581 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
582
583 file1 = g_file_new_for_path (FU_OFFLINE_TRIGGER_FILENAME);
584 if (!g_file_query_exists (file1, NULL))
585 return TRUE;
586 if (!g_file_delete (file1, NULL, &error_local)) {
587 g_set_error (error,
588 FWUPD_ERROR,
589 FWUPD_ERROR_INTERNAL,
590 "Cannot delete %s: %s",
591 FU_OFFLINE_TRIGGER_FILENAME,
592 error_local->message);
593 return FALSE;
594 }
595 return TRUE;
596}
597
598static gboolean
599fu_plugin_runner_offline_setup (GError **error)
600{
601 gint rc;
602
603 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
604
605 /* create symlink for the systemd-system-update-generator */
606 rc = symlink ("/var/lib/fwupd", FU_OFFLINE_TRIGGER_FILENAME);
607 if (rc < 0) {
608 g_set_error (error,
609 FWUPD_ERROR,
610 FWUPD_ERROR_INTERNAL,
611 "Failed to create symlink %s to %s: %s",
612 FU_OFFLINE_TRIGGER_FILENAME,
613 "/var/lib", strerror (errno));
Richard Hughesd0905142016-03-13 09:46:49 +0000614 return FALSE;
615 }
616 return TRUE;
617}
618
Richard Hughesd0905142016-03-13 09:46:49 +0000619gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000620fu_plugin_runner_coldplug (FuPlugin *plugin, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000621{
Richard Hughescff38bc2016-12-12 12:03:37 +0000622 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000623 FuPluginStartupFunc func = NULL;
Richard Hughesd0905142016-03-13 09:46:49 +0000624
625 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000626 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000627 return TRUE;
628
629 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000630 g_module_symbol (priv->module, "fu_plugin_coldplug", (gpointer *) &func);
631 if (func == NULL)
Richard Hughesd0905142016-03-13 09:46:49 +0000632 return TRUE;
Richard Hughescff38bc2016-12-12 12:03:37 +0000633 g_debug ("performing coldplug() on %s", priv->name);
634 if (!func (plugin, error)) {
635 g_prefix_error (error, "failed to coldplug %s: ", priv->name);
636 return FALSE;
637 }
638 return TRUE;
639}
640
Richard Hughes7b8b2022016-12-12 16:15:03 +0000641gboolean
Richard Hughes46487c92017-01-07 21:26:34 +0000642fu_plugin_runner_coldplug_prepare (FuPlugin *plugin, GError **error)
643{
644 FuPluginPrivate *priv = GET_PRIVATE (plugin);
645 FuPluginStartupFunc func = NULL;
646
647 /* not enabled */
648 if (!priv->enabled)
649 return TRUE;
650
651 /* optional */
652 g_module_symbol (priv->module, "fu_plugin_coldplug_prepare", (gpointer *) &func);
653 if (func == NULL)
654 return TRUE;
655 g_debug ("performing coldplug_prepare() on %s", priv->name);
656 if (!func (plugin, error)) {
657 g_prefix_error (error, "failed to prepare for coldplug %s: ", priv->name);
658 return FALSE;
659 }
660 return TRUE;
661}
662
663gboolean
664fu_plugin_runner_coldplug_cleanup (FuPlugin *plugin, GError **error)
665{
666 FuPluginPrivate *priv = GET_PRIVATE (plugin);
667 FuPluginStartupFunc func = NULL;
668
669 /* not enabled */
670 if (!priv->enabled)
671 return TRUE;
672
673 /* optional */
674 g_module_symbol (priv->module, "fu_plugin_coldplug_cleanup", (gpointer *) &func);
675 if (func == NULL)
676 return TRUE;
677 g_debug ("performing coldplug_cleanup() on %s", priv->name);
678 if (!func (plugin, error)) {
679 g_prefix_error (error, "failed to cleanup coldplug %s: ", priv->name);
680 return FALSE;
681 }
682 return TRUE;
683}
684
685gboolean
Richard Hughes7b8b2022016-12-12 16:15:03 +0000686fu_plugin_runner_update_prepare (FuPlugin *plugin, FuDevice *device, GError **error)
687{
688 FuPluginPrivate *priv = GET_PRIVATE (plugin);
689 FuPluginDeviceFunc func = NULL;
690
691 /* not enabled */
692 if (!priv->enabled)
693 return TRUE;
694
695 /* optional */
696 g_module_symbol (priv->module, "fu_plugin_update_prepare", (gpointer *) &func);
697 if (func == NULL)
698 return TRUE;
699 g_debug ("performing update_prepare() on %s", priv->name);
700 if (!func (plugin, device, error)) {
701 g_prefix_error (error, "failed to prepare for update %s: ", priv->name);
702 return FALSE;
703 }
704 return TRUE;
705}
706
707gboolean
708fu_plugin_runner_update_cleanup (FuPlugin *plugin, FuDevice *device, GError **error)
709{
710 FuPluginPrivate *priv = GET_PRIVATE (plugin);
711 FuPluginDeviceFunc func = NULL;
712
713 /* not enabled */
714 if (!priv->enabled)
715 return TRUE;
716
717 /* optional */
718 g_module_symbol (priv->module, "fu_plugin_update_cleanup", (gpointer *) &func);
719 if (func == NULL)
720 return TRUE;
721 g_debug ("performing update_cleanup() on %s", priv->name);
722 if (!func (plugin, device, error)) {
723 g_prefix_error (error, "failed to cleanup update %s: ", priv->name);
724 return FALSE;
725 }
726 return TRUE;
727}
728
Richard Hughescff38bc2016-12-12 12:03:37 +0000729static gboolean
730fu_plugin_runner_schedule_update (FuPlugin *plugin,
731 FuDevice *device,
732 GBytes *blob_cab,
733 GError **error)
734{
735 gchar tmpname[] = {"XXXXXX.cap"};
736 g_autofree gchar *dirname = NULL;
737 g_autofree gchar *filename = NULL;
738 g_autoptr(FwupdResult) res_tmp = NULL;
739 g_autoptr(FuPending) pending = NULL;
740 g_autoptr(GFile) file = NULL;
741
742 /* id already exists */
743 pending = fu_pending_new ();
744 res_tmp = fu_pending_get_device (pending, fu_device_get_id (device), NULL);
745 if (res_tmp != NULL) {
746 g_set_error (error,
747 FWUPD_ERROR,
748 FWUPD_ERROR_ALREADY_PENDING,
749 "%s is already scheduled to be updated",
750 fu_device_get_id (device));
751 return FALSE;
752 }
753
754 /* create directory */
755 dirname = g_build_filename (LOCALSTATEDIR, "lib", "fwupd", NULL);
756 file = g_file_new_for_path (dirname);
757 if (!g_file_query_exists (file, NULL)) {
758 if (!g_file_make_directory_with_parents (file, NULL, error))
759 return FALSE;
760 }
761
762 /* get a random filename */
763 for (guint i = 0; i < 6; i++)
764 tmpname[i] = (gchar) g_random_int_range ('A', 'Z');
765 filename = g_build_filename (dirname, tmpname, NULL);
766
767 /* just copy to the temp file */
768 fu_plugin_set_status (plugin, FWUPD_STATUS_SCHEDULING);
769 if (!g_file_set_contents (filename,
770 g_bytes_get_data (blob_cab, NULL),
771 (gssize) g_bytes_get_size (blob_cab),
772 error))
773 return FALSE;
774
775 /* schedule for next boot */
776 g_debug ("schedule %s to be installed to %s on next boot",
777 filename, fu_device_get_id (device));
778 fu_device_set_update_filename (device, filename);
779
780 /* add to database */
781 if (!fu_pending_add_device (pending, FWUPD_RESULT (device), error))
782 return FALSE;
783
784 /* next boot we run offline */
785 return fu_plugin_runner_offline_setup (error);
786}
787
788gboolean
789fu_plugin_runner_verify (FuPlugin *plugin,
790 FuDevice *device,
791 FuPluginVerifyFlags flags,
792 GError **error)
793{
794 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000795 FuPluginVerifyFunc func = NULL;
Richard Hughesababbb72017-06-15 20:18:36 +0100796 GPtrArray *checksums;
Richard Hughescff38bc2016-12-12 12:03:37 +0000797
798 /* not enabled */
799 if (!priv->enabled)
800 return TRUE;
801
Richard Hughesababbb72017-06-15 20:18:36 +0100802 /* clear any existing verification checksums */
803 checksums = fu_device_get_checksums (device);
804 g_ptr_array_set_size (checksums, 0);
805
Richard Hughescff38bc2016-12-12 12:03:37 +0000806 /* optional */
807 g_module_symbol (priv->module, "fu_plugin_verify", (gpointer *) &func);
808 if (func == NULL)
809 return TRUE;
810 g_debug ("performing verify() on %s", priv->name);
811 if (!func (plugin, device, flags, error)) {
812 g_prefix_error (error, "failed to verify %s: ", priv->name);
Richard Hughesd0905142016-03-13 09:46:49 +0000813 return FALSE;
814 }
815 return TRUE;
816}
817
Richard Hughesd0905142016-03-13 09:46:49 +0000818gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000819fu_plugin_runner_unlock (FuPlugin *plugin, FuDevice *device, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000820{
Richard Hughescff38bc2016-12-12 12:03:37 +0000821 guint64 flags;
822 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000823 FuPluginDeviceFunc func = NULL;
Richard Hughesd0905142016-03-13 09:46:49 +0000824
825 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000826 if (!priv->enabled)
827 return TRUE;
828
829 /* final check */
830 flags = fu_device_get_flags (device);
831 if ((flags & FWUPD_DEVICE_FLAG_LOCKED) == 0) {
832 g_set_error (error,
833 FWUPD_ERROR,
834 FWUPD_ERROR_NOT_SUPPORTED,
835 "Device %s is not locked",
836 fu_device_get_id (device));
837 return FALSE;
838 }
839
840 /* optional */
841 g_module_symbol (priv->module, "fu_plugin_unlock", (gpointer *) &func);
842 if (func != NULL) {
843 g_debug ("performing unlock() on %s", priv->name);
844 if (!func (plugin, device, error)) {
Richard Hughes7b8b2022016-12-12 16:15:03 +0000845 g_prefix_error (error, "failed to unlock %s: ", priv->name);
Richard Hughescff38bc2016-12-12 12:03:37 +0000846 return FALSE;
847 }
848 }
849
850 /* update with correct flags */
851 flags = fu_device_get_flags (device);
852 fu_device_set_flags (device, flags &= ~FWUPD_DEVICE_FLAG_LOCKED);
853 fu_device_set_modified (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
854 return TRUE;
855}
856
857gboolean
858fu_plugin_runner_update (FuPlugin *plugin,
859 FuDevice *device,
860 GBytes *blob_cab,
861 GBytes *blob_fw,
862 FwupdInstallFlags flags,
863 GError **error)
864{
865 FuPluginPrivate *priv = GET_PRIVATE (plugin);
866 FuPluginUpdateFunc func_online;
867 FuPluginUpdateFunc func_offline;
868 g_autoptr(FuPending) pending = NULL;
869 g_autoptr(FwupdResult) res_pending = NULL;
870 GError *error_update = NULL;
Richard Hughesf556d372017-06-15 19:49:18 +0100871 GPtrArray *checksums;
Richard Hughescff38bc2016-12-12 12:03:37 +0000872
873 /* not enabled */
874 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000875 return TRUE;
876
877 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000878 g_module_symbol (priv->module, "fu_plugin_update_online", (gpointer *) &func_online);
879 g_module_symbol (priv->module, "fu_plugin_update_offline", (gpointer *) &func_offline);
Richard Hughesd0905142016-03-13 09:46:49 +0000880
Richard Hughescff38bc2016-12-12 12:03:37 +0000881 /* schedule for next reboot, or handle in the plugin */
882 if (flags & FWUPD_INSTALL_FLAG_OFFLINE) {
883 if (func_offline == NULL) {
884 return fu_plugin_runner_schedule_update (plugin,
885 device,
886 blob_cab,
887 error);
888 }
889 return func_offline (plugin, device, blob_fw, flags, error);
890 }
891
892 /* cancel the pending action */
893 if (!fu_plugin_runner_offline_invalidate (error))
894 return FALSE;
895
896 /* online */
897 if (func_online == NULL) {
898 g_set_error_literal (error,
899 FWUPD_ERROR,
900 FWUPD_ERROR_NOT_SUPPORTED,
901 "No online update possible");
Richard Hughesd0905142016-03-13 09:46:49 +0000902 return FALSE;
903 }
Richard Hughescff38bc2016-12-12 12:03:37 +0000904 pending = fu_pending_new ();
905 res_pending = fu_pending_get_device (pending, fu_device_get_id (device), NULL);
906 if (!func_online (plugin, device, blob_fw, flags, &error_update)) {
907 /* save the error to the database */
908 if (res_pending != NULL) {
909 fu_pending_set_error_msg (pending, FWUPD_RESULT (device),
910 error_update->message, NULL);
911 }
912 g_propagate_error (error, error_update);
913 return FALSE;
914 }
915
Richard Hughesf556d372017-06-15 19:49:18 +0100916 /* no longer valid */
917 checksums = fu_device_get_checksums (device);
918 g_ptr_array_set_size (checksums, 0);
919
Richard Hughescff38bc2016-12-12 12:03:37 +0000920 /* cleanup */
921 if (res_pending != NULL) {
922 const gchar *tmp;
Richard Hughes1642b3b2017-06-05 17:40:08 +0100923 FwupdRelease *rel = fwupd_result_get_release (res_pending);
Richard Hughescff38bc2016-12-12 12:03:37 +0000924
925 /* update pending database */
926 fu_pending_set_state (pending, FWUPD_RESULT (device),
927 FWUPD_UPDATE_STATE_SUCCESS, NULL);
928
929 /* delete cab file */
Richard Hughes1642b3b2017-06-05 17:40:08 +0100930 tmp = fwupd_release_get_filename (rel);
Richard Hughescff38bc2016-12-12 12:03:37 +0000931 if (tmp != NULL && g_str_has_prefix (tmp, LIBEXECDIR)) {
932 g_autoptr(GError) error_local = NULL;
933 g_autoptr(GFile) file = NULL;
934 file = g_file_new_for_path (tmp);
935 if (!g_file_delete (file, NULL, &error_local)) {
936 g_set_error (error,
937 FWUPD_ERROR,
938 FWUPD_ERROR_INVALID_FILE,
939 "Failed to delete %s: %s",
940 tmp, error_local->message);
941 return FALSE;
942 }
943 }
944 }
Richard Hughesd0905142016-03-13 09:46:49 +0000945 return TRUE;
946}
Richard Hughescff38bc2016-12-12 12:03:37 +0000947
948gboolean
949fu_plugin_runner_clear_results (FuPlugin *plugin, FuDevice *device, GError **error)
950{
951 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000952 FuPluginDeviceFunc func = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +0000953 g_autoptr(GError) error_local = NULL;
954 g_autoptr(FwupdResult) res_pending = NULL;
955 g_autoptr(FuPending) pending = NULL;
956
957 /* not enabled */
958 if (!priv->enabled)
959 return TRUE;
960
961 /* use the plugin if the vfunc is provided */
962 g_module_symbol (priv->module, "fu_plugin_clear_result", (gpointer *) &func);
963 if (func != NULL) {
964 g_debug ("performing clear_result() on %s", priv->name);
965 if (!func (plugin, device, error)) {
966 g_prefix_error (error, "failed to clear_result %s: ", priv->name);
967 return FALSE;
968 }
969 return TRUE;
970 }
971
972 /* handled using the database */
973 pending = fu_pending_new ();
974 res_pending = fu_pending_get_device (pending,
975 fu_device_get_id (device),
976 &error_local);
977 if (res_pending == NULL) {
978 g_set_error (error,
979 FWUPD_ERROR,
980 FWUPD_ERROR_INVALID_FILE,
981 "Failed to find %s in pending database: %s",
982 fu_device_get_id (device),
983 error_local->message);
984 return FALSE;
985 }
986
987 /* remove from pending database */
988 return fu_pending_remove_device (pending, FWUPD_RESULT (device), error);
989}
990
991gboolean
992fu_plugin_runner_get_results (FuPlugin *plugin, FuDevice *device, GError **error)
993{
994 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000995 FuPluginDeviceFunc func = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +0000996 FwupdUpdateState update_state;
Richard Hughes1642b3b2017-06-05 17:40:08 +0100997 FwupdRelease *rel;
998 FwupdDevice *dev;
Richard Hughescff38bc2016-12-12 12:03:37 +0000999 const gchar *tmp;
1000 g_autoptr(GError) error_local = NULL;
1001 g_autoptr(FwupdResult) res_pending = NULL;
1002 g_autoptr(FuPending) pending = NULL;
1003
1004 /* not enabled */
1005 if (!priv->enabled)
1006 return TRUE;
1007
1008 /* use the plugin if the vfunc is provided */
1009 g_module_symbol (priv->module, "fu_plugin_get_results", (gpointer *) &func);
1010 if (func != NULL) {
1011 g_debug ("performing get_results() on %s", priv->name);
1012 if (!func (plugin, device, error)) {
1013 g_prefix_error (error, "failed to get_results %s: ", priv->name);
1014 return FALSE;
1015 }
1016 return TRUE;
1017 }
1018
1019 /* handled using the database */
1020 pending = fu_pending_new ();
1021 res_pending = fu_pending_get_device (pending,
1022 fu_device_get_id (device),
1023 &error_local);
1024 if (res_pending == NULL) {
1025 g_set_error (error,
1026 FWUPD_ERROR,
1027 FWUPD_ERROR_NOTHING_TO_DO,
1028 "Failed to find %s in pending database: %s",
1029 fu_device_get_id (device),
1030 error_local->message);
1031 return FALSE;
1032 }
1033
1034 /* copy the important parts from the pending device to the real one */
1035 update_state = fwupd_result_get_update_state (res_pending);
1036 if (update_state == FWUPD_UPDATE_STATE_UNKNOWN ||
1037 update_state == FWUPD_UPDATE_STATE_PENDING) {
1038 g_set_error (error,
1039 FWUPD_ERROR,
1040 FWUPD_ERROR_NOTHING_TO_DO,
1041 "Device %s has not been updated offline yet",
1042 fu_device_get_id (device));
1043 return FALSE;
1044 }
1045
1046 /* copy */
1047 fu_device_set_update_state (device, update_state);
1048 tmp = fwupd_result_get_update_error (res_pending);
1049 if (tmp != NULL)
1050 fu_device_set_update_error (device, tmp);
Richard Hughes1642b3b2017-06-05 17:40:08 +01001051 dev = fwupd_result_get_device (res_pending);
1052 tmp = fwupd_device_get_version (dev);
Richard Hughescff38bc2016-12-12 12:03:37 +00001053 if (tmp != NULL)
1054 fu_device_set_version (device, tmp);
Richard Hughes1642b3b2017-06-05 17:40:08 +01001055 rel = fwupd_result_get_release (res_pending);
1056 tmp = fwupd_release_get_version (rel);
Richard Hughescff38bc2016-12-12 12:03:37 +00001057 if (tmp != NULL)
1058 fu_device_set_update_version (device, tmp);
1059 return TRUE;
1060}
1061
1062static void
1063fu_plugin_class_init (FuPluginClass *klass)
1064{
1065 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1066 object_class->finalize = fu_plugin_finalize;
1067 signals[SIGNAL_DEVICE_ADDED] =
1068 g_signal_new ("device-added",
1069 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1070 G_STRUCT_OFFSET (FuPluginClass, device_added),
1071 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1072 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
1073 signals[SIGNAL_DEVICE_REMOVED] =
1074 g_signal_new ("device-removed",
1075 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1076 G_STRUCT_OFFSET (FuPluginClass, device_removed),
1077 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1078 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
1079 signals[SIGNAL_STATUS_CHANGED] =
1080 g_signal_new ("status-changed",
1081 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1082 G_STRUCT_OFFSET (FuPluginClass, status_changed),
1083 NULL, NULL, g_cclosure_marshal_VOID__UINT,
1084 G_TYPE_NONE, 1, G_TYPE_UINT);
1085 signals[SIGNAL_PERCENTAGE_CHANGED] =
1086 g_signal_new ("percentage-changed",
1087 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1088 G_STRUCT_OFFSET (FuPluginClass, percentage_changed),
1089 NULL, NULL, g_cclosure_marshal_VOID__UINT,
1090 G_TYPE_NONE, 1, G_TYPE_UINT);
Richard Hughes362d6d72017-01-07 21:42:14 +00001091 signals[SIGNAL_RECOLDPLUG] =
1092 g_signal_new ("recoldplug",
1093 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1094 G_STRUCT_OFFSET (FuPluginClass, recoldplug),
1095 NULL, NULL, g_cclosure_marshal_VOID__VOID,
1096 G_TYPE_NONE, 0);
Richard Hughesb0829032017-01-10 09:27:08 +00001097 signals[SIGNAL_SET_COLDPLUG_DELAY] =
1098 g_signal_new ("set-coldplug-delay",
1099 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1100 G_STRUCT_OFFSET (FuPluginClass, set_coldplug_delay),
1101 NULL, NULL, g_cclosure_marshal_VOID__UINT,
1102 G_TYPE_NONE, 1, G_TYPE_UINT);
Richard Hughescff38bc2016-12-12 12:03:37 +00001103}
1104
1105static void
1106fu_plugin_init (FuPlugin *plugin)
1107{
1108 FuPluginPrivate *priv = GET_PRIVATE (plugin);
1109 priv->enabled = TRUE;
1110 priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
1111 g_free, (GDestroyNotify) g_object_unref);
Richard Hughesae3d65f2016-12-16 09:38:01 +00001112 priv->devices_delay = g_hash_table_new (g_str_hash, g_str_equal);
Richard Hughescff38bc2016-12-12 12:03:37 +00001113}
1114
1115static void
1116fu_plugin_finalize (GObject *object)
1117{
1118 FuPlugin *plugin = FU_PLUGIN (object);
1119 FuPluginPrivate *priv = GET_PRIVATE (plugin);
1120 FuPluginInitFunc func = NULL;
1121
1122 /* optional */
1123 if (priv->module != NULL) {
1124 g_module_symbol (priv->module, "fu_plugin_destroy", (gpointer *) &func);
1125 if (func != NULL) {
1126 g_debug ("performing destroy() on %s", priv->name);
1127 func (plugin);
1128 }
1129 }
1130
1131 if (priv->usb_ctx != NULL)
1132 g_object_unref (priv->usb_ctx);
Richard Hughesb8f8db22017-04-25 15:56:00 +01001133 if (priv->hwids != NULL)
1134 g_hash_table_unref (priv->hwids);
Richard Hughes576c0122017-02-24 09:47:00 +00001135#ifndef RUNNING_ON_VALGRIND
Richard Hughescff38bc2016-12-12 12:03:37 +00001136 if (priv->module != NULL)
1137 g_module_close (priv->module);
Richard Hughes576c0122017-02-24 09:47:00 +00001138#endif
Richard Hughescff38bc2016-12-12 12:03:37 +00001139 g_hash_table_unref (priv->devices);
Richard Hughesae3d65f2016-12-16 09:38:01 +00001140 g_hash_table_unref (priv->devices_delay);
Richard Hughescff38bc2016-12-12 12:03:37 +00001141 g_free (priv->name);
1142 g_free (priv->data);
1143
1144 G_OBJECT_CLASS (fu_plugin_parent_class)->finalize (object);
1145}
1146
1147FuPlugin *
1148fu_plugin_new (void)
1149{
1150 FuPlugin *plugin;
1151 plugin = g_object_new (FU_TYPE_PLUGIN, NULL);
1152 return plugin;
1153}