blob: 4724846b7cd372b5bc393c4fad73202e38e8a238 [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 Hughescff38bc2016-12-12 12:03:37 +0000796
797 /* not enabled */
798 if (!priv->enabled)
799 return TRUE;
800
801 /* optional */
802 g_module_symbol (priv->module, "fu_plugin_verify", (gpointer *) &func);
803 if (func == NULL)
804 return TRUE;
805 g_debug ("performing verify() on %s", priv->name);
806 if (!func (plugin, device, flags, error)) {
807 g_prefix_error (error, "failed to verify %s: ", priv->name);
Richard Hughesd0905142016-03-13 09:46:49 +0000808 return FALSE;
809 }
810 return TRUE;
811}
812
Richard Hughesd0905142016-03-13 09:46:49 +0000813gboolean
Richard Hughescff38bc2016-12-12 12:03:37 +0000814fu_plugin_runner_unlock (FuPlugin *plugin, FuDevice *device, GError **error)
Richard Hughesd0905142016-03-13 09:46:49 +0000815{
Richard Hughescff38bc2016-12-12 12:03:37 +0000816 guint64 flags;
817 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000818 FuPluginDeviceFunc func = NULL;
Richard Hughesd0905142016-03-13 09:46:49 +0000819
820 /* not enabled */
Richard Hughescff38bc2016-12-12 12:03:37 +0000821 if (!priv->enabled)
822 return TRUE;
823
824 /* final check */
825 flags = fu_device_get_flags (device);
826 if ((flags & FWUPD_DEVICE_FLAG_LOCKED) == 0) {
827 g_set_error (error,
828 FWUPD_ERROR,
829 FWUPD_ERROR_NOT_SUPPORTED,
830 "Device %s is not locked",
831 fu_device_get_id (device));
832 return FALSE;
833 }
834
835 /* optional */
836 g_module_symbol (priv->module, "fu_plugin_unlock", (gpointer *) &func);
837 if (func != NULL) {
838 g_debug ("performing unlock() on %s", priv->name);
839 if (!func (plugin, device, error)) {
Richard Hughes7b8b2022016-12-12 16:15:03 +0000840 g_prefix_error (error, "failed to unlock %s: ", priv->name);
Richard Hughescff38bc2016-12-12 12:03:37 +0000841 return FALSE;
842 }
843 }
844
845 /* update with correct flags */
846 flags = fu_device_get_flags (device);
847 fu_device_set_flags (device, flags &= ~FWUPD_DEVICE_FLAG_LOCKED);
848 fu_device_set_modified (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
849 return TRUE;
850}
851
852gboolean
853fu_plugin_runner_update (FuPlugin *plugin,
854 FuDevice *device,
855 GBytes *blob_cab,
856 GBytes *blob_fw,
857 FwupdInstallFlags flags,
858 GError **error)
859{
860 FuPluginPrivate *priv = GET_PRIVATE (plugin);
861 FuPluginUpdateFunc func_online;
862 FuPluginUpdateFunc func_offline;
863 g_autoptr(FuPending) pending = NULL;
864 g_autoptr(FwupdResult) res_pending = NULL;
865 GError *error_update = NULL;
Richard Hughesf556d372017-06-15 19:49:18 +0100866 GPtrArray *checksums;
Richard Hughescff38bc2016-12-12 12:03:37 +0000867
868 /* not enabled */
869 if (!priv->enabled)
Richard Hughesd0905142016-03-13 09:46:49 +0000870 return TRUE;
871
872 /* optional */
Richard Hughescff38bc2016-12-12 12:03:37 +0000873 g_module_symbol (priv->module, "fu_plugin_update_online", (gpointer *) &func_online);
874 g_module_symbol (priv->module, "fu_plugin_update_offline", (gpointer *) &func_offline);
Richard Hughesd0905142016-03-13 09:46:49 +0000875
Richard Hughescff38bc2016-12-12 12:03:37 +0000876 /* schedule for next reboot, or handle in the plugin */
877 if (flags & FWUPD_INSTALL_FLAG_OFFLINE) {
878 if (func_offline == NULL) {
879 return fu_plugin_runner_schedule_update (plugin,
880 device,
881 blob_cab,
882 error);
883 }
884 return func_offline (plugin, device, blob_fw, flags, error);
885 }
886
887 /* cancel the pending action */
888 if (!fu_plugin_runner_offline_invalidate (error))
889 return FALSE;
890
891 /* online */
892 if (func_online == NULL) {
893 g_set_error_literal (error,
894 FWUPD_ERROR,
895 FWUPD_ERROR_NOT_SUPPORTED,
896 "No online update possible");
Richard Hughesd0905142016-03-13 09:46:49 +0000897 return FALSE;
898 }
Richard Hughescff38bc2016-12-12 12:03:37 +0000899 pending = fu_pending_new ();
900 res_pending = fu_pending_get_device (pending, fu_device_get_id (device), NULL);
901 if (!func_online (plugin, device, blob_fw, flags, &error_update)) {
902 /* save the error to the database */
903 if (res_pending != NULL) {
904 fu_pending_set_error_msg (pending, FWUPD_RESULT (device),
905 error_update->message, NULL);
906 }
907 g_propagate_error (error, error_update);
908 return FALSE;
909 }
910
Richard Hughesf556d372017-06-15 19:49:18 +0100911 /* no longer valid */
912 checksums = fu_device_get_checksums (device);
913 g_ptr_array_set_size (checksums, 0);
914
Richard Hughescff38bc2016-12-12 12:03:37 +0000915 /* cleanup */
916 if (res_pending != NULL) {
917 const gchar *tmp;
Richard Hughes1642b3b2017-06-05 17:40:08 +0100918 FwupdRelease *rel = fwupd_result_get_release (res_pending);
Richard Hughescff38bc2016-12-12 12:03:37 +0000919
920 /* update pending database */
921 fu_pending_set_state (pending, FWUPD_RESULT (device),
922 FWUPD_UPDATE_STATE_SUCCESS, NULL);
923
924 /* delete cab file */
Richard Hughes1642b3b2017-06-05 17:40:08 +0100925 tmp = fwupd_release_get_filename (rel);
Richard Hughescff38bc2016-12-12 12:03:37 +0000926 if (tmp != NULL && g_str_has_prefix (tmp, LIBEXECDIR)) {
927 g_autoptr(GError) error_local = NULL;
928 g_autoptr(GFile) file = NULL;
929 file = g_file_new_for_path (tmp);
930 if (!g_file_delete (file, NULL, &error_local)) {
931 g_set_error (error,
932 FWUPD_ERROR,
933 FWUPD_ERROR_INVALID_FILE,
934 "Failed to delete %s: %s",
935 tmp, error_local->message);
936 return FALSE;
937 }
938 }
939 }
Richard Hughesd0905142016-03-13 09:46:49 +0000940 return TRUE;
941}
Richard Hughescff38bc2016-12-12 12:03:37 +0000942
943gboolean
944fu_plugin_runner_clear_results (FuPlugin *plugin, FuDevice *device, GError **error)
945{
946 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000947 FuPluginDeviceFunc func = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +0000948 g_autoptr(GError) error_local = NULL;
949 g_autoptr(FwupdResult) res_pending = NULL;
950 g_autoptr(FuPending) pending = NULL;
951
952 /* not enabled */
953 if (!priv->enabled)
954 return TRUE;
955
956 /* use the plugin if the vfunc is provided */
957 g_module_symbol (priv->module, "fu_plugin_clear_result", (gpointer *) &func);
958 if (func != NULL) {
959 g_debug ("performing clear_result() on %s", priv->name);
960 if (!func (plugin, device, error)) {
961 g_prefix_error (error, "failed to clear_result %s: ", priv->name);
962 return FALSE;
963 }
964 return TRUE;
965 }
966
967 /* handled using the database */
968 pending = fu_pending_new ();
969 res_pending = fu_pending_get_device (pending,
970 fu_device_get_id (device),
971 &error_local);
972 if (res_pending == NULL) {
973 g_set_error (error,
974 FWUPD_ERROR,
975 FWUPD_ERROR_INVALID_FILE,
976 "Failed to find %s in pending database: %s",
977 fu_device_get_id (device),
978 error_local->message);
979 return FALSE;
980 }
981
982 /* remove from pending database */
983 return fu_pending_remove_device (pending, FWUPD_RESULT (device), error);
984}
985
986gboolean
987fu_plugin_runner_get_results (FuPlugin *plugin, FuDevice *device, GError **error)
988{
989 FuPluginPrivate *priv = GET_PRIVATE (plugin);
Richard Hughes7b8b2022016-12-12 16:15:03 +0000990 FuPluginDeviceFunc func = NULL;
Richard Hughescff38bc2016-12-12 12:03:37 +0000991 FwupdUpdateState update_state;
Richard Hughes1642b3b2017-06-05 17:40:08 +0100992 FwupdRelease *rel;
993 FwupdDevice *dev;
Richard Hughescff38bc2016-12-12 12:03:37 +0000994 const gchar *tmp;
995 g_autoptr(GError) error_local = NULL;
996 g_autoptr(FwupdResult) res_pending = NULL;
997 g_autoptr(FuPending) pending = NULL;
998
999 /* not enabled */
1000 if (!priv->enabled)
1001 return TRUE;
1002
1003 /* use the plugin if the vfunc is provided */
1004 g_module_symbol (priv->module, "fu_plugin_get_results", (gpointer *) &func);
1005 if (func != NULL) {
1006 g_debug ("performing get_results() on %s", priv->name);
1007 if (!func (plugin, device, error)) {
1008 g_prefix_error (error, "failed to get_results %s: ", priv->name);
1009 return FALSE;
1010 }
1011 return TRUE;
1012 }
1013
1014 /* handled using the database */
1015 pending = fu_pending_new ();
1016 res_pending = fu_pending_get_device (pending,
1017 fu_device_get_id (device),
1018 &error_local);
1019 if (res_pending == NULL) {
1020 g_set_error (error,
1021 FWUPD_ERROR,
1022 FWUPD_ERROR_NOTHING_TO_DO,
1023 "Failed to find %s in pending database: %s",
1024 fu_device_get_id (device),
1025 error_local->message);
1026 return FALSE;
1027 }
1028
1029 /* copy the important parts from the pending device to the real one */
1030 update_state = fwupd_result_get_update_state (res_pending);
1031 if (update_state == FWUPD_UPDATE_STATE_UNKNOWN ||
1032 update_state == FWUPD_UPDATE_STATE_PENDING) {
1033 g_set_error (error,
1034 FWUPD_ERROR,
1035 FWUPD_ERROR_NOTHING_TO_DO,
1036 "Device %s has not been updated offline yet",
1037 fu_device_get_id (device));
1038 return FALSE;
1039 }
1040
1041 /* copy */
1042 fu_device_set_update_state (device, update_state);
1043 tmp = fwupd_result_get_update_error (res_pending);
1044 if (tmp != NULL)
1045 fu_device_set_update_error (device, tmp);
Richard Hughes1642b3b2017-06-05 17:40:08 +01001046 dev = fwupd_result_get_device (res_pending);
1047 tmp = fwupd_device_get_version (dev);
Richard Hughescff38bc2016-12-12 12:03:37 +00001048 if (tmp != NULL)
1049 fu_device_set_version (device, tmp);
Richard Hughes1642b3b2017-06-05 17:40:08 +01001050 rel = fwupd_result_get_release (res_pending);
1051 tmp = fwupd_release_get_version (rel);
Richard Hughescff38bc2016-12-12 12:03:37 +00001052 if (tmp != NULL)
1053 fu_device_set_update_version (device, tmp);
1054 return TRUE;
1055}
1056
1057static void
1058fu_plugin_class_init (FuPluginClass *klass)
1059{
1060 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1061 object_class->finalize = fu_plugin_finalize;
1062 signals[SIGNAL_DEVICE_ADDED] =
1063 g_signal_new ("device-added",
1064 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1065 G_STRUCT_OFFSET (FuPluginClass, device_added),
1066 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1067 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
1068 signals[SIGNAL_DEVICE_REMOVED] =
1069 g_signal_new ("device-removed",
1070 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1071 G_STRUCT_OFFSET (FuPluginClass, device_removed),
1072 NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1073 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
1074 signals[SIGNAL_STATUS_CHANGED] =
1075 g_signal_new ("status-changed",
1076 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1077 G_STRUCT_OFFSET (FuPluginClass, status_changed),
1078 NULL, NULL, g_cclosure_marshal_VOID__UINT,
1079 G_TYPE_NONE, 1, G_TYPE_UINT);
1080 signals[SIGNAL_PERCENTAGE_CHANGED] =
1081 g_signal_new ("percentage-changed",
1082 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1083 G_STRUCT_OFFSET (FuPluginClass, percentage_changed),
1084 NULL, NULL, g_cclosure_marshal_VOID__UINT,
1085 G_TYPE_NONE, 1, G_TYPE_UINT);
Richard Hughes362d6d72017-01-07 21:42:14 +00001086 signals[SIGNAL_RECOLDPLUG] =
1087 g_signal_new ("recoldplug",
1088 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1089 G_STRUCT_OFFSET (FuPluginClass, recoldplug),
1090 NULL, NULL, g_cclosure_marshal_VOID__VOID,
1091 G_TYPE_NONE, 0);
Richard Hughesb0829032017-01-10 09:27:08 +00001092 signals[SIGNAL_SET_COLDPLUG_DELAY] =
1093 g_signal_new ("set-coldplug-delay",
1094 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
1095 G_STRUCT_OFFSET (FuPluginClass, set_coldplug_delay),
1096 NULL, NULL, g_cclosure_marshal_VOID__UINT,
1097 G_TYPE_NONE, 1, G_TYPE_UINT);
Richard Hughescff38bc2016-12-12 12:03:37 +00001098}
1099
1100static void
1101fu_plugin_init (FuPlugin *plugin)
1102{
1103 FuPluginPrivate *priv = GET_PRIVATE (plugin);
1104 priv->enabled = TRUE;
1105 priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal,
1106 g_free, (GDestroyNotify) g_object_unref);
Richard Hughesae3d65f2016-12-16 09:38:01 +00001107 priv->devices_delay = g_hash_table_new (g_str_hash, g_str_equal);
Richard Hughescff38bc2016-12-12 12:03:37 +00001108}
1109
1110static void
1111fu_plugin_finalize (GObject *object)
1112{
1113 FuPlugin *plugin = FU_PLUGIN (object);
1114 FuPluginPrivate *priv = GET_PRIVATE (plugin);
1115 FuPluginInitFunc func = NULL;
1116
1117 /* optional */
1118 if (priv->module != NULL) {
1119 g_module_symbol (priv->module, "fu_plugin_destroy", (gpointer *) &func);
1120 if (func != NULL) {
1121 g_debug ("performing destroy() on %s", priv->name);
1122 func (plugin);
1123 }
1124 }
1125
1126 if (priv->usb_ctx != NULL)
1127 g_object_unref (priv->usb_ctx);
Richard Hughesb8f8db22017-04-25 15:56:00 +01001128 if (priv->hwids != NULL)
1129 g_hash_table_unref (priv->hwids);
Richard Hughes576c0122017-02-24 09:47:00 +00001130#ifndef RUNNING_ON_VALGRIND
Richard Hughescff38bc2016-12-12 12:03:37 +00001131 if (priv->module != NULL)
1132 g_module_close (priv->module);
Richard Hughes576c0122017-02-24 09:47:00 +00001133#endif
Richard Hughescff38bc2016-12-12 12:03:37 +00001134 g_hash_table_unref (priv->devices);
Richard Hughesae3d65f2016-12-16 09:38:01 +00001135 g_hash_table_unref (priv->devices_delay);
Richard Hughescff38bc2016-12-12 12:03:37 +00001136 g_free (priv->name);
1137 g_free (priv->data);
1138
1139 G_OBJECT_CLASS (fu_plugin_parent_class)->finalize (object);
1140}
1141
1142FuPlugin *
1143fu_plugin_new (void)
1144{
1145 FuPlugin *plugin;
1146 plugin = g_object_new (FU_TYPE_PLUGIN, NULL);
1147 return plugin;
1148}